Skip to main content

reifydb_rql/expression/
json.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4use std::sync::Arc;
5
6use reifydb_core::{
7	interface::identifier::{ColumnIdentifier, ColumnShape},
8	internal,
9};
10use reifydb_type::{
11	Result,
12	error::{Error, TypeError},
13	fragment::Fragment,
14	value::r#type::Type,
15};
16use serde::{Deserialize, Serialize};
17use serde_json::{from_str, to_string, to_string_pretty};
18
19use super::{
20	AccessShapeExpression, AddExpression, AliasExpression, AndExpression, BetweenExpression, CallExpression,
21	CastExpression, ColumnExpression, ConstantExpression, ContainsExpression, DivExpression, ElseIfExpression,
22	EqExpression, Expression, ExtendExpression, FieldAccessExpression, GreaterThanEqExpression,
23	GreaterThanExpression, IdentExpression, IfExpression, InExpression, LessThanEqExpression, LessThanExpression,
24	ListExpression, MapExpression, MulExpression, NotEqExpression, OrExpression, ParameterExpression,
25	PrefixExpression, PrefixOperator, RemExpression, SubExpression, TupleExpression, TypeExpression,
26	VariableExpression, XorExpression,
27};
28
29/// JSON-serializable expression for query builders.
30///
31/// This enum mirrors the `Expression` type but uses simple owned types
32/// instead of lifetimed `Fragment`s, making it suitable for JSON serialization.
33#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
34#[serde(tag = "type", rename_all = "snake_case")]
35pub enum JsonExpression {
36	// Constants
37	None,
38	Bool {
39		value: String,
40	},
41	Number {
42		value: String,
43	},
44	Text {
45		value: String,
46	},
47	Temporal {
48		value: String,
49	},
50
51	// Identifiers
52	Column {
53		namespace: String,
54		shape: String,
55		name: String,
56	},
57	AccessSource {
58		namespace: String,
59		shape: String,
60		name: String,
61	},
62	Variable {
63		name: String,
64	},
65	#[serde(rename = "parameter_positional")]
66	ParameterPositional {
67		position: String,
68	},
69	#[serde(rename = "parameter_named")]
70	ParameterNamed {
71		name: String,
72	},
73
74	// Comparison
75	GreaterThan {
76		left: Box<JsonExpression>,
77		right: Box<JsonExpression>,
78	},
79	GreaterThanEqual {
80		left: Box<JsonExpression>,
81		right: Box<JsonExpression>,
82	},
83	LessThan {
84		left: Box<JsonExpression>,
85		right: Box<JsonExpression>,
86	},
87	LessThanEqual {
88		left: Box<JsonExpression>,
89		right: Box<JsonExpression>,
90	},
91	Equal {
92		left: Box<JsonExpression>,
93		right: Box<JsonExpression>,
94	},
95	NotEqual {
96		left: Box<JsonExpression>,
97		right: Box<JsonExpression>,
98	},
99
100	// Logical
101	And {
102		left: Box<JsonExpression>,
103		right: Box<JsonExpression>,
104	},
105	Or {
106		left: Box<JsonExpression>,
107		right: Box<JsonExpression>,
108	},
109	Xor {
110		left: Box<JsonExpression>,
111		right: Box<JsonExpression>,
112	},
113
114	// Arithmetic
115	Add {
116		left: Box<JsonExpression>,
117		right: Box<JsonExpression>,
118	},
119	Sub {
120		left: Box<JsonExpression>,
121		right: Box<JsonExpression>,
122	},
123	Mul {
124		left: Box<JsonExpression>,
125		right: Box<JsonExpression>,
126	},
127	Div {
128		left: Box<JsonExpression>,
129		right: Box<JsonExpression>,
130	},
131	Rem {
132		left: Box<JsonExpression>,
133		right: Box<JsonExpression>,
134	},
135
136	// Complex
137	Alias {
138		alias: String,
139		expression: Box<JsonExpression>,
140	},
141	Cast {
142		expression: Box<JsonExpression>,
143		to: String,
144	},
145	Call {
146		function: String,
147		args: Vec<JsonExpression>,
148	},
149	Tuple {
150		expressions: Vec<JsonExpression>,
151	},
152	List {
153		expressions: Vec<JsonExpression>,
154	},
155	Prefix {
156		operator: String,
157		expression: Box<JsonExpression>,
158	},
159	Between {
160		value: Box<JsonExpression>,
161		lower: Box<JsonExpression>,
162		upper: Box<JsonExpression>,
163	},
164	In {
165		value: Box<JsonExpression>,
166		list: Box<JsonExpression>,
167		negated: bool,
168	},
169	Contains {
170		value: Box<JsonExpression>,
171		list: Box<JsonExpression>,
172	},
173	If {
174		condition: Box<JsonExpression>,
175		then: Box<JsonExpression>,
176		else_ifs: Vec<JsonElseIf>,
177		#[serde(skip_serializing_if = "Option::is_none")]
178		else_expr: Option<Box<JsonExpression>>,
179	},
180	Map {
181		expressions: Vec<JsonExpression>,
182	},
183	Extend {
184		expressions: Vec<JsonExpression>,
185	},
186	Type {
187		ty: String,
188	},
189	FieldAccess {
190		object: Box<JsonExpression>,
191		field: String,
192	},
193}
194
195/// JSON representation of an else-if branch.
196#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
197pub struct JsonElseIf {
198	pub condition: Box<JsonExpression>,
199	pub then: Box<JsonExpression>,
200}
201
202// Helper to extract namespace and primitive from ColumnShape
203fn extract_shape(cp: &ColumnShape) -> (String, String) {
204	match cp {
205		ColumnShape::Qualified {
206			namespace,
207			name,
208		} => (namespace.text().to_string(), name.text().to_string()),
209		ColumnShape::Alias(alias) => ("_alias".to_string(), alias.text().to_string()),
210	}
211}
212
213// Helper to create an internal fragment
214fn internal_fragment(text: &str) -> Fragment {
215	Fragment::Internal {
216		text: Arc::from(text),
217	}
218}
219
220impl From<&Expression> for JsonExpression {
221	fn from(expr: &Expression) -> Self {
222		match expr {
223			// Constants
224			Expression::Constant(constant) => match constant {
225				ConstantExpression::None {
226					..
227				} => JsonExpression::None,
228				ConstantExpression::Bool {
229					fragment,
230				} => JsonExpression::Bool {
231					value: fragment.text().to_string(),
232				},
233				ConstantExpression::Number {
234					fragment,
235				} => JsonExpression::Number {
236					value: fragment.text().to_string(),
237				},
238				ConstantExpression::Text {
239					fragment,
240				} => JsonExpression::Text {
241					value: fragment.text().to_string(),
242				},
243				ConstantExpression::Temporal {
244					fragment,
245				} => JsonExpression::Temporal {
246					value: fragment.text().to_string(),
247				},
248			},
249
250			// Identifiers
251			Expression::Column(ColumnExpression(col)) => {
252				let (namespace, shape) = extract_shape(&col.shape);
253				JsonExpression::Column {
254					namespace,
255					shape,
256					name: col.name.text().to_string(),
257				}
258			}
259			Expression::AccessSource(AccessShapeExpression {
260				column,
261			}) => {
262				let (namespace, shape) = extract_shape(&column.shape);
263				JsonExpression::AccessSource {
264					namespace,
265					shape,
266					name: column.name.text().to_string(),
267				}
268			}
269			Expression::Variable(var) => JsonExpression::Variable {
270				name: var.name().to_string(),
271			},
272			Expression::Parameter(param) => match param {
273				ParameterExpression::Positional {
274					fragment,
275				} => JsonExpression::ParameterPositional {
276					position: fragment.text()[1..].to_string(), // Skip '$'
277				},
278				ParameterExpression::Named {
279					fragment,
280				} => JsonExpression::ParameterNamed {
281					name: fragment.text()[1..].to_string(), // Skip '$'
282				},
283			},
284
285			// Comparison
286			Expression::GreaterThan(e) => JsonExpression::GreaterThan {
287				left: Box::new((&*e.left).into()),
288				right: Box::new((&*e.right).into()),
289			},
290			Expression::GreaterThanEqual(e) => JsonExpression::GreaterThanEqual {
291				left: Box::new((&*e.left).into()),
292				right: Box::new((&*e.right).into()),
293			},
294			Expression::LessThan(e) => JsonExpression::LessThan {
295				left: Box::new((&*e.left).into()),
296				right: Box::new((&*e.right).into()),
297			},
298			Expression::LessThanEqual(e) => JsonExpression::LessThanEqual {
299				left: Box::new((&*e.left).into()),
300				right: Box::new((&*e.right).into()),
301			},
302			Expression::Equal(e) => JsonExpression::Equal {
303				left: Box::new((&*e.left).into()),
304				right: Box::new((&*e.right).into()),
305			},
306			Expression::NotEqual(e) => JsonExpression::NotEqual {
307				left: Box::new((&*e.left).into()),
308				right: Box::new((&*e.right).into()),
309			},
310
311			// Logical
312			Expression::And(e) => JsonExpression::And {
313				left: Box::new((&*e.left).into()),
314				right: Box::new((&*e.right).into()),
315			},
316			Expression::Or(e) => JsonExpression::Or {
317				left: Box::new((&*e.left).into()),
318				right: Box::new((&*e.right).into()),
319			},
320			Expression::Xor(e) => JsonExpression::Xor {
321				left: Box::new((&*e.left).into()),
322				right: Box::new((&*e.right).into()),
323			},
324
325			// Arithmetic
326			Expression::Add(e) => JsonExpression::Add {
327				left: Box::new((&*e.left).into()),
328				right: Box::new((&*e.right).into()),
329			},
330			Expression::Sub(e) => JsonExpression::Sub {
331				left: Box::new((&*e.left).into()),
332				right: Box::new((&*e.right).into()),
333			},
334			Expression::Mul(e) => JsonExpression::Mul {
335				left: Box::new((&*e.left).into()),
336				right: Box::new((&*e.right).into()),
337			},
338			Expression::Div(e) => JsonExpression::Div {
339				left: Box::new((&*e.left).into()),
340				right: Box::new((&*e.right).into()),
341			},
342			Expression::Rem(e) => JsonExpression::Rem {
343				left: Box::new((&*e.left).into()),
344				right: Box::new((&*e.right).into()),
345			},
346
347			// Complex
348			Expression::Alias(e) => JsonExpression::Alias {
349				alias: e.alias.name().to_string(),
350				expression: Box::new((&*e.expression).into()),
351			},
352			Expression::Cast(e) => JsonExpression::Cast {
353				expression: Box::new((&*e.expression).into()),
354				to: format!("{:?}", e.to.ty),
355			},
356			Expression::Call(e) => JsonExpression::Call {
357				function: e.func.name().to_string(),
358				args: e.args.iter().map(|a| a.into()).collect(),
359			},
360			Expression::Tuple(e) => JsonExpression::Tuple {
361				expressions: e.expressions.iter().map(|a| a.into()).collect(),
362			},
363			Expression::List(e) => JsonExpression::List {
364				expressions: e.expressions.iter().map(|a| a.into()).collect(),
365			},
366			Expression::Prefix(e) => {
367				let operator = match &e.operator {
368					PrefixOperator::Minus(_) => "-",
369					PrefixOperator::Plus(_) => "+",
370					PrefixOperator::Not(_) => "not",
371				};
372				JsonExpression::Prefix {
373					operator: operator.to_string(),
374					expression: Box::new((&*e.expression).into()),
375				}
376			}
377			Expression::Between(e) => JsonExpression::Between {
378				value: Box::new((&*e.value).into()),
379				lower: Box::new((&*e.lower).into()),
380				upper: Box::new((&*e.upper).into()),
381			},
382			Expression::In(e) => JsonExpression::In {
383				value: Box::new((&*e.value).into()),
384				list: Box::new((&*e.list).into()),
385				negated: e.negated,
386			},
387			Expression::Contains(e) => JsonExpression::Contains {
388				value: Box::new((&*e.value).into()),
389				list: Box::new((&*e.list).into()),
390			},
391			Expression::If(e) => JsonExpression::If {
392				condition: Box::new((&*e.condition).into()),
393				then: Box::new((&*e.then_expr).into()),
394				else_ifs: e
395					.else_ifs
396					.iter()
397					.map(|ei| JsonElseIf {
398						condition: Box::new((&*ei.condition).into()),
399						then: Box::new((&*ei.then_expr).into()),
400					})
401					.collect(),
402				else_expr: e.else_expr.as_ref().map(|ee| Box::new((&**ee).into())),
403			},
404			Expression::Map(e) => JsonExpression::Map {
405				expressions: e.expressions.iter().map(|a| a.into()).collect(),
406			},
407			Expression::Extend(e) => JsonExpression::Extend {
408				expressions: e.expressions.iter().map(|a| a.into()).collect(),
409			},
410			Expression::Type(e) => JsonExpression::Type {
411				ty: format!("{:?}", e.ty),
412			},
413			Expression::SumTypeConstructor(_) => JsonExpression::Type {
414				ty: "SumTypeConstructor".to_string(),
415			},
416			Expression::IsVariant(_) => JsonExpression::Type {
417				ty: "IsVariant".to_string(),
418			},
419			Expression::FieldAccess(e) => JsonExpression::FieldAccess {
420				object: Box::new((&*e.object).into()),
421				field: e.field.text().to_string(),
422			},
423		}
424	}
425}
426
427impl TryFrom<JsonExpression> for Expression {
428	type Error = Error;
429
430	fn try_from(json: JsonExpression) -> Result<Self> {
431		Ok(match json {
432			// Constants
433			JsonExpression::None => Expression::Constant(ConstantExpression::None {
434				fragment: Fragment::None,
435			}),
436			JsonExpression::Bool {
437				value,
438			} => Expression::Constant(ConstantExpression::Bool {
439				fragment: internal_fragment(&value),
440			}),
441			JsonExpression::Number {
442				value,
443			} => Expression::Constant(ConstantExpression::Number {
444				fragment: internal_fragment(&value),
445			}),
446			JsonExpression::Text {
447				value,
448			} => Expression::Constant(ConstantExpression::Text {
449				fragment: internal_fragment(&value),
450			}),
451			JsonExpression::Temporal {
452				value,
453			} => Expression::Constant(ConstantExpression::Temporal {
454				fragment: internal_fragment(&value),
455			}),
456
457			// Identifiers
458			JsonExpression::Column {
459				namespace,
460				shape,
461				name,
462			} => Expression::Column(ColumnExpression(ColumnIdentifier {
463				shape: ColumnShape::Qualified {
464					namespace: internal_fragment(&namespace),
465					name: internal_fragment(&shape),
466				},
467				name: internal_fragment(&name),
468			})),
469			JsonExpression::AccessSource {
470				namespace,
471				shape,
472				name,
473			} => Expression::AccessSource(AccessShapeExpression {
474				column: ColumnIdentifier {
475					shape: ColumnShape::Qualified {
476						namespace: internal_fragment(&namespace),
477						name: internal_fragment(&shape),
478					},
479					name: internal_fragment(&name),
480				},
481			}),
482			JsonExpression::Variable {
483				name,
484			} => Expression::Variable(VariableExpression {
485				fragment: internal_fragment(&format!("${}", name)),
486			}),
487			JsonExpression::ParameterPositional {
488				position,
489			} => Expression::Parameter(ParameterExpression::Positional {
490				fragment: internal_fragment(&format!("${}", position)),
491			}),
492			JsonExpression::ParameterNamed {
493				name,
494			} => Expression::Parameter(ParameterExpression::Named {
495				fragment: internal_fragment(&format!("${}", name)),
496			}),
497
498			// Comparison
499			JsonExpression::GreaterThan {
500				left,
501				right,
502			} => Expression::GreaterThan(GreaterThanExpression {
503				left: Box::new((*left).try_into()?),
504				right: Box::new((*right).try_into()?),
505				fragment: Fragment::None,
506			}),
507			JsonExpression::GreaterThanEqual {
508				left,
509				right,
510			} => Expression::GreaterThanEqual(GreaterThanEqExpression {
511				left: Box::new((*left).try_into()?),
512				right: Box::new((*right).try_into()?),
513				fragment: Fragment::None,
514			}),
515			JsonExpression::LessThan {
516				left,
517				right,
518			} => Expression::LessThan(LessThanExpression {
519				left: Box::new((*left).try_into()?),
520				right: Box::new((*right).try_into()?),
521				fragment: Fragment::None,
522			}),
523			JsonExpression::LessThanEqual {
524				left,
525				right,
526			} => Expression::LessThanEqual(LessThanEqExpression {
527				left: Box::new((*left).try_into()?),
528				right: Box::new((*right).try_into()?),
529				fragment: Fragment::None,
530			}),
531			JsonExpression::Equal {
532				left,
533				right,
534			} => Expression::Equal(EqExpression {
535				left: Box::new((*left).try_into()?),
536				right: Box::new((*right).try_into()?),
537				fragment: Fragment::None,
538			}),
539			JsonExpression::NotEqual {
540				left,
541				right,
542			} => Expression::NotEqual(NotEqExpression {
543				left: Box::new((*left).try_into()?),
544				right: Box::new((*right).try_into()?),
545				fragment: Fragment::None,
546			}),
547
548			// Logical
549			JsonExpression::And {
550				left,
551				right,
552			} => Expression::And(AndExpression {
553				left: Box::new((*left).try_into()?),
554				right: Box::new((*right).try_into()?),
555				fragment: Fragment::None,
556			}),
557			JsonExpression::Or {
558				left,
559				right,
560			} => Expression::Or(OrExpression {
561				left: Box::new((*left).try_into()?),
562				right: Box::new((*right).try_into()?),
563				fragment: Fragment::None,
564			}),
565			JsonExpression::Xor {
566				left,
567				right,
568			} => Expression::Xor(XorExpression {
569				left: Box::new((*left).try_into()?),
570				right: Box::new((*right).try_into()?),
571				fragment: Fragment::None,
572			}),
573
574			// Arithmetic
575			JsonExpression::Add {
576				left,
577				right,
578			} => Expression::Add(AddExpression {
579				left: Box::new((*left).try_into()?),
580				right: Box::new((*right).try_into()?),
581				fragment: Fragment::None,
582			}),
583			JsonExpression::Sub {
584				left,
585				right,
586			} => Expression::Sub(SubExpression {
587				left: Box::new((*left).try_into()?),
588				right: Box::new((*right).try_into()?),
589				fragment: Fragment::None,
590			}),
591			JsonExpression::Mul {
592				left,
593				right,
594			} => Expression::Mul(MulExpression {
595				left: Box::new((*left).try_into()?),
596				right: Box::new((*right).try_into()?),
597				fragment: Fragment::None,
598			}),
599			JsonExpression::Div {
600				left,
601				right,
602			} => Expression::Div(DivExpression {
603				left: Box::new((*left).try_into()?),
604				right: Box::new((*right).try_into()?),
605				fragment: Fragment::None,
606			}),
607			JsonExpression::Rem {
608				left,
609				right,
610			} => Expression::Rem(RemExpression {
611				left: Box::new((*left).try_into()?),
612				right: Box::new((*right).try_into()?),
613				fragment: Fragment::None,
614			}),
615
616			// Complex
617			JsonExpression::Alias {
618				alias,
619				expression,
620			} => Expression::Alias(AliasExpression {
621				alias: IdentExpression(internal_fragment(&alias)),
622				expression: Box::new((*expression).try_into()?),
623				fragment: Fragment::None,
624			}),
625			JsonExpression::Cast {
626				expression,
627				to,
628			} => {
629				let ty = parse_type(&to)?;
630				Expression::Cast(CastExpression {
631					expression: Box::new((*expression).try_into()?),
632					to: TypeExpression {
633						ty,
634						fragment: internal_fragment(&to),
635					},
636					fragment: Fragment::None,
637				})
638			}
639			JsonExpression::Call {
640				function,
641				args,
642			} => Expression::Call(CallExpression {
643				func: IdentExpression(internal_fragment(&function)),
644				args: args.into_iter().map(|a| a.try_into()).collect::<Result<Vec<_>>>()?,
645				fragment: Fragment::None,
646			}),
647			JsonExpression::Tuple {
648				expressions,
649			} => Expression::Tuple(TupleExpression {
650				expressions: expressions
651					.into_iter()
652					.map(|a| a.try_into())
653					.collect::<Result<Vec<_>>>()?,
654				fragment: Fragment::None,
655			}),
656			JsonExpression::List {
657				expressions,
658			} => Expression::List(ListExpression {
659				expressions: expressions
660					.into_iter()
661					.map(|a| a.try_into())
662					.collect::<Result<Vec<_>>>()?,
663				fragment: Fragment::None,
664			}),
665			JsonExpression::Prefix {
666				operator,
667				expression,
668			} => {
669				let op = match operator.as_str() {
670					"-" => PrefixOperator::Minus(Fragment::None),
671					"+" => PrefixOperator::Plus(Fragment::None),
672					"not" => PrefixOperator::Not(Fragment::None),
673					_ => {
674						return Err(Error(Box::new(internal!(
675							"Unknown prefix operator: {}",
676							operator
677						))));
678					}
679				};
680				Expression::Prefix(PrefixExpression {
681					operator: op,
682					expression: Box::new((*expression).try_into()?),
683					fragment: Fragment::None,
684				})
685			}
686			JsonExpression::Between {
687				value,
688				lower,
689				upper,
690			} => Expression::Between(BetweenExpression {
691				value: Box::new((*value).try_into()?),
692				lower: Box::new((*lower).try_into()?),
693				upper: Box::new((*upper).try_into()?),
694				fragment: Fragment::None,
695			}),
696			JsonExpression::In {
697				value,
698				list,
699				negated,
700			} => Expression::In(InExpression {
701				value: Box::new((*value).try_into()?),
702				list: Box::new((*list).try_into()?),
703				negated,
704				fragment: Fragment::None,
705			}),
706			JsonExpression::Contains {
707				value,
708				list,
709			} => Expression::Contains(ContainsExpression {
710				value: Box::new((*value).try_into()?),
711				list: Box::new((*list).try_into()?),
712				fragment: Fragment::None,
713			}),
714			JsonExpression::If {
715				condition,
716				then,
717				else_ifs,
718				else_expr,
719			} => {
720				let converted_else: Option<Box<Expression>> = match else_expr {
721					Some(ee) => Some(Box::new((*ee).try_into()?)),
722					None => None,
723				};
724				Expression::If(IfExpression {
725					condition: Box::new((*condition).try_into()?),
726					then_expr: Box::new((*then).try_into()?),
727					else_ifs: else_ifs
728						.into_iter()
729						.map(|ei| {
730							Ok(ElseIfExpression {
731								condition: Box::new((*ei.condition).try_into()?),
732								then_expr: Box::new((*ei.then).try_into()?),
733								fragment: Fragment::None,
734							})
735						})
736						.collect::<Result<Vec<_>>>()?,
737					else_expr: converted_else,
738					fragment: Fragment::None,
739				})
740			}
741			JsonExpression::Map {
742				expressions,
743			} => Expression::Map(MapExpression {
744				expressions: expressions
745					.into_iter()
746					.map(|a| a.try_into())
747					.collect::<Result<Vec<_>>>()?,
748				fragment: Fragment::None,
749			}),
750			JsonExpression::Extend {
751				expressions,
752			} => Expression::Extend(ExtendExpression {
753				expressions: expressions
754					.into_iter()
755					.map(|a| a.try_into())
756					.collect::<Result<Vec<_>>>()?,
757				fragment: Fragment::None,
758			}),
759			JsonExpression::Type {
760				ty,
761			} => {
762				let parsed_ty = parse_type(&ty)?;
763				Expression::Type(TypeExpression {
764					ty: parsed_ty,
765					fragment: internal_fragment(&ty),
766				})
767			}
768			JsonExpression::FieldAccess {
769				object,
770				field,
771			} => Expression::FieldAccess(FieldAccessExpression {
772				object: Box::new((*object).try_into()?),
773				field: internal_fragment(&field),
774				fragment: Fragment::None,
775			}),
776		})
777	}
778}
779
780// Helper to parse type strings back to Type enum
781fn parse_type(s: &str) -> Result<Type> {
782	// Handle type debug representations
783	let ty = match s.to_lowercase().as_str() {
784		"boolean" => Type::Boolean,
785		"bool" => Type::Boolean,
786		"int1" => Type::Int1,
787		"int2" => Type::Int2,
788		"int4" => Type::Int4,
789		"int8" => Type::Int8,
790		"int16" => Type::Int16,
791		"int32" => Type::Int4,
792		"int64" => Type::Int8,
793		"uint1" => Type::Uint1,
794		"uint2" => Type::Uint2,
795		"uint4" => Type::Uint4,
796		"uint8" => Type::Uint8,
797		"uint16" => Type::Uint16,
798		"float4" => Type::Float4,
799		"float8" => Type::Float8,
800		"float32" => Type::Float4,
801		"float64" => Type::Float8,
802		"utf8" => Type::Utf8,
803		"string" => Type::Utf8,
804		"text" => Type::Utf8,
805		"blob" => Type::Blob,
806		"uuid4" => Type::Uuid4,
807		"uuid7" => Type::Uuid7,
808		"date" => Type::Date,
809		"time" => Type::Time,
810		"datetime" => Type::DateTime,
811		"duration" => Type::Duration,
812		"identityid" => Type::IdentityId,
813		"int" => Type::Int,
814		"uint" => Type::Uint,
815		"decimal" => Type::Decimal,
816		_ => {
817			return Err(Error(Box::new(internal!("Unknown type: {}", s))));
818		}
819	};
820
821	Ok(ty)
822}
823
824/// Serialize an Expression to a JSON string.
825///
826/// The output skips Fragment source location metadata and preserves only
827/// semantic data suitable for frontend query builders.
828pub fn to_json(expr: &Expression) -> String {
829	let json_expr: JsonExpression = expr.into();
830	to_string(&json_expr).expect("JsonExpression should always serialize")
831}
832
833/// Serialize an Expression to a pretty-printed JSON string.
834pub fn to_json_pretty(expr: &Expression) -> String {
835	let json_expr: JsonExpression = expr.into();
836	to_string_pretty(&json_expr).expect("JsonExpression should always serialize")
837}
838
839/// Deserialize an Expression from a JSON string.
840pub fn from_json(json: &str) -> Result<Expression> {
841	let json_expr: JsonExpression = from_str(json).map_err(|e| {
842		Error::from(TypeError::SerdeDeserialize {
843			message: e.to_string(),
844		})
845	})?;
846	json_expr.try_into()
847}
848
849#[cfg(test)]
850pub mod tests {
851	use super::*;
852
853	// Helper functions to create test expressions
854	fn column_expr(name: &str) -> Expression {
855		Expression::Column(ColumnExpression(ColumnIdentifier {
856			shape: ColumnShape::Qualified {
857				namespace: internal_fragment("_context"),
858				name: internal_fragment("_context"),
859			},
860			name: internal_fragment(name),
861		}))
862	}
863
864	fn constant_number(val: &str) -> Expression {
865		Expression::Constant(ConstantExpression::Number {
866			fragment: internal_fragment(val),
867		})
868	}
869
870	fn constant_text(val: &str) -> Expression {
871		Expression::Constant(ConstantExpression::Text {
872			fragment: internal_fragment(val),
873		})
874	}
875
876	fn constant_bool(val: &str) -> Expression {
877		Expression::Constant(ConstantExpression::Bool {
878			fragment: internal_fragment(val),
879		})
880	}
881
882	#[test]
883	fn test_undefined() {
884		let expr = Expression::Constant(ConstantExpression::None {
885			fragment: Fragment::None,
886		});
887
888		let json = to_json(&expr);
889		assert_eq!(json, r#"{"type":"none"}"#);
890
891		let recovered = from_json(&json).unwrap();
892		assert_eq!(to_json(&recovered), json);
893	}
894
895	#[test]
896	fn test_bool() {
897		let expr = constant_bool("true");
898
899		let json = to_json(&expr);
900		assert_eq!(json, r#"{"type":"bool","value":"true"}"#);
901
902		let recovered = from_json(&json).unwrap();
903		assert_eq!(to_json(&recovered), json);
904	}
905
906	#[test]
907	fn test_number() {
908		let expr = constant_number("42");
909
910		let json = to_json(&expr);
911		assert_eq!(json, r#"{"type":"number","value":"42"}"#);
912
913		let recovered = from_json(&json).unwrap();
914		assert_eq!(to_json(&recovered), json);
915	}
916
917	#[test]
918	fn test_text() {
919		let expr = constant_text("hello world");
920
921		let json = to_json(&expr);
922		assert_eq!(json, r#"{"type":"text","value":"hello world"}"#);
923
924		let recovered = from_json(&json).unwrap();
925		assert_eq!(to_json(&recovered), json);
926	}
927
928	#[test]
929	fn test_temporal() {
930		let expr = Expression::Constant(ConstantExpression::Temporal {
931			fragment: internal_fragment("2024-01-15T10:30:00"),
932		});
933
934		let json = to_json(&expr);
935		assert_eq!(json, r#"{"type":"temporal","value":"2024-01-15T10:30:00"}"#);
936
937		let recovered = from_json(&json).unwrap();
938		assert_eq!(to_json(&recovered), json);
939	}
940
941	#[test]
942	fn test_column() {
943		let expr = column_expr("age");
944
945		let json = to_json(&expr);
946		assert_eq!(json, r#"{"type":"column","namespace":"_context","shape":"_context","name":"age"}"#);
947
948		let recovered = from_json(&json).unwrap();
949		assert_eq!(to_json(&recovered), json);
950	}
951
952	#[test]
953	fn test_variable() {
954		let expr = Expression::Variable(VariableExpression {
955			fragment: internal_fragment("$my_var"),
956		});
957
958		let json = to_json(&expr);
959		assert_eq!(json, r#"{"type":"variable","name":"my_var"}"#);
960
961		let recovered = from_json(&json).unwrap();
962		assert_eq!(to_json(&recovered), json);
963	}
964
965	#[test]
966	fn test_parameter_positional() {
967		let expr = Expression::Parameter(ParameterExpression::Positional {
968			fragment: internal_fragment("$1"),
969		});
970
971		let json = to_json(&expr);
972		assert_eq!(json, r#"{"type":"parameter_positional","position":"1"}"#);
973
974		let recovered = from_json(&json).unwrap();
975		assert_eq!(to_json(&recovered), json);
976	}
977
978	#[test]
979	fn test_parameter_named() {
980		let expr = Expression::Parameter(ParameterExpression::Named {
981			fragment: internal_fragment("$name"),
982		});
983
984		let json = to_json(&expr);
985		assert_eq!(json, r#"{"type":"parameter_named","name":"name"}"#);
986
987		let recovered = from_json(&json).unwrap();
988		assert_eq!(to_json(&recovered), json);
989	}
990
991	#[test]
992	fn test_greater_than() {
993		let expr = Expression::GreaterThan(GreaterThanExpression {
994			left: Box::new(column_expr("age")),
995			right: Box::new(constant_number("18")),
996			fragment: Fragment::None,
997		});
998
999		let json = to_json(&expr);
1000		assert_eq!(
1001			json,
1002			r#"{"type":"greater_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"age"},"right":{"type":"number","value":"18"}}"#
1003		);
1004
1005		let recovered = from_json(&json).unwrap();
1006		assert_eq!(to_json(&recovered), json);
1007	}
1008
1009	#[test]
1010	fn test_greater_than_equal() {
1011		let expr = Expression::GreaterThanEqual(GreaterThanEqExpression {
1012			left: Box::new(column_expr("price")),
1013			right: Box::new(constant_number("100")),
1014			fragment: Fragment::None,
1015		});
1016
1017		let json = to_json(&expr);
1018		assert_eq!(
1019			json,
1020			r#"{"type":"greater_than_equal","left":{"type":"column","namespace":"_context","shape":"_context","name":"price"},"right":{"type":"number","value":"100"}}"#
1021		);
1022
1023		let recovered = from_json(&json).unwrap();
1024		assert_eq!(to_json(&recovered), json);
1025	}
1026
1027	#[test]
1028	fn test_less_than() {
1029		let expr = Expression::LessThan(LessThanExpression {
1030			left: Box::new(column_expr("count")),
1031			right: Box::new(constant_number("10")),
1032			fragment: Fragment::None,
1033		});
1034
1035		let json = to_json(&expr);
1036		assert_eq!(
1037			json,
1038			r#"{"type":"less_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"count"},"right":{"type":"number","value":"10"}}"#
1039		);
1040
1041		let recovered = from_json(&json).unwrap();
1042		assert_eq!(to_json(&recovered), json);
1043	}
1044
1045	#[test]
1046	fn test_less_than_equal() {
1047		let expr = Expression::LessThanEqual(LessThanEqExpression {
1048			left: Box::new(column_expr("quantity")),
1049			right: Box::new(constant_number("5")),
1050			fragment: Fragment::None,
1051		});
1052
1053		let json = to_json(&expr);
1054		assert_eq!(
1055			json,
1056			r#"{"type":"less_than_equal","left":{"type":"column","namespace":"_context","shape":"_context","name":"quantity"},"right":{"type":"number","value":"5"}}"#
1057		);
1058
1059		let recovered = from_json(&json).unwrap();
1060		assert_eq!(to_json(&recovered), json);
1061	}
1062
1063	#[test]
1064	fn test_equal() {
1065		let expr = Expression::Equal(EqExpression {
1066			left: Box::new(column_expr("status")),
1067			right: Box::new(constant_text("active")),
1068			fragment: Fragment::None,
1069		});
1070
1071		let json = to_json(&expr);
1072		assert_eq!(
1073			json,
1074			r#"{"type":"equal","left":{"type":"column","namespace":"_context","shape":"_context","name":"status"},"right":{"type":"text","value":"active"}}"#
1075		);
1076
1077		let recovered = from_json(&json).unwrap();
1078		assert_eq!(to_json(&recovered), json);
1079	}
1080
1081	#[test]
1082	fn test_not_equal() {
1083		let expr = Expression::NotEqual(NotEqExpression {
1084			left: Box::new(column_expr("type")),
1085			right: Box::new(constant_text("deleted")),
1086			fragment: Fragment::None,
1087		});
1088
1089		let json = to_json(&expr);
1090		assert_eq!(
1091			json,
1092			r#"{"type":"not_equal","left":{"type":"column","namespace":"_context","shape":"_context","name":"type"},"right":{"type":"text","value":"deleted"}}"#
1093		);
1094
1095		let recovered = from_json(&json).unwrap();
1096		assert_eq!(to_json(&recovered), json);
1097	}
1098
1099	#[test]
1100	fn test_and() {
1101		let expr = Expression::And(AndExpression {
1102			left: Box::new(Expression::GreaterThan(GreaterThanExpression {
1103				left: Box::new(column_expr("age")),
1104				right: Box::new(constant_number("18")),
1105				fragment: Fragment::None,
1106			})),
1107			right: Box::new(Expression::Equal(EqExpression {
1108				left: Box::new(column_expr("active")),
1109				right: Box::new(constant_bool("true")),
1110				fragment: Fragment::None,
1111			})),
1112			fragment: Fragment::None,
1113		});
1114
1115		let json = to_json(&expr);
1116		assert_eq!(
1117			json,
1118			r#"{"type":"and","left":{"type":"greater_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"age"},"right":{"type":"number","value":"18"}},"right":{"type":"equal","left":{"type":"column","namespace":"_context","shape":"_context","name":"active"},"right":{"type":"bool","value":"true"}}}"#
1119		);
1120
1121		let recovered = from_json(&json).unwrap();
1122		assert_eq!(to_json(&recovered), json);
1123	}
1124
1125	#[test]
1126	fn test_or() {
1127		let expr = Expression::Or(OrExpression {
1128			left: Box::new(column_expr("a")),
1129			right: Box::new(column_expr("b")),
1130			fragment: Fragment::None,
1131		});
1132
1133		let json = to_json(&expr);
1134		assert_eq!(
1135			json,
1136			r#"{"type":"or","left":{"type":"column","namespace":"_context","shape":"_context","name":"a"},"right":{"type":"column","namespace":"_context","shape":"_context","name":"b"}}"#
1137		);
1138
1139		let recovered = from_json(&json).unwrap();
1140		assert_eq!(to_json(&recovered), json);
1141	}
1142
1143	#[test]
1144	fn test_xor() {
1145		let expr = Expression::Xor(XorExpression {
1146			left: Box::new(column_expr("x")),
1147			right: Box::new(column_expr("y")),
1148			fragment: Fragment::None,
1149		});
1150
1151		let json = to_json(&expr);
1152		assert_eq!(
1153			json,
1154			r#"{"type":"xor","left":{"type":"column","namespace":"_context","shape":"_context","name":"x"},"right":{"type":"column","namespace":"_context","shape":"_context","name":"y"}}"#
1155		);
1156
1157		let recovered = from_json(&json).unwrap();
1158		assert_eq!(to_json(&recovered), json);
1159	}
1160
1161	#[test]
1162	fn test_add() {
1163		let expr = Expression::Add(AddExpression {
1164			left: Box::new(column_expr("price")),
1165			right: Box::new(constant_number("10")),
1166			fragment: Fragment::None,
1167		});
1168
1169		let json = to_json(&expr);
1170		assert_eq!(
1171			json,
1172			r#"{"type":"add","left":{"type":"column","namespace":"_context","shape":"_context","name":"price"},"right":{"type":"number","value":"10"}}"#
1173		);
1174
1175		let recovered = from_json(&json).unwrap();
1176		assert_eq!(to_json(&recovered), json);
1177	}
1178
1179	#[test]
1180	fn test_sub() {
1181		let expr = Expression::Sub(SubExpression {
1182			left: Box::new(column_expr("total")),
1183			right: Box::new(constant_number("5")),
1184			fragment: Fragment::None,
1185		});
1186
1187		let json = to_json(&expr);
1188		assert_eq!(
1189			json,
1190			r#"{"type":"sub","left":{"type":"column","namespace":"_context","shape":"_context","name":"total"},"right":{"type":"number","value":"5"}}"#
1191		);
1192
1193		let recovered = from_json(&json).unwrap();
1194		assert_eq!(to_json(&recovered), json);
1195	}
1196
1197	#[test]
1198	fn test_mul() {
1199		let expr = Expression::Mul(MulExpression {
1200			left: Box::new(column_expr("qty")),
1201			right: Box::new(constant_number("2")),
1202			fragment: Fragment::None,
1203		});
1204
1205		let json = to_json(&expr);
1206		assert_eq!(
1207			json,
1208			r#"{"type":"mul","left":{"type":"column","namespace":"_context","shape":"_context","name":"qty"},"right":{"type":"number","value":"2"}}"#
1209		);
1210
1211		let recovered = from_json(&json).unwrap();
1212		assert_eq!(to_json(&recovered), json);
1213	}
1214
1215	#[test]
1216	fn test_div() {
1217		let expr = Expression::Div(DivExpression {
1218			left: Box::new(column_expr("amount")),
1219			right: Box::new(constant_number("4")),
1220			fragment: Fragment::None,
1221		});
1222
1223		let json = to_json(&expr);
1224		assert_eq!(
1225			json,
1226			r#"{"type":"div","left":{"type":"column","namespace":"_context","shape":"_context","name":"amount"},"right":{"type":"number","value":"4"}}"#
1227		);
1228
1229		let recovered = from_json(&json).unwrap();
1230		assert_eq!(to_json(&recovered), json);
1231	}
1232
1233	#[test]
1234	fn test_rem() {
1235		let expr = Expression::Rem(RemExpression {
1236			left: Box::new(column_expr("num")),
1237			right: Box::new(constant_number("3")),
1238			fragment: Fragment::None,
1239		});
1240
1241		let json = to_json(&expr);
1242		assert_eq!(
1243			json,
1244			r#"{"type":"rem","left":{"type":"column","namespace":"_context","shape":"_context","name":"num"},"right":{"type":"number","value":"3"}}"#
1245		);
1246
1247		let recovered = from_json(&json).unwrap();
1248		assert_eq!(to_json(&recovered), json);
1249	}
1250
1251	#[test]
1252	fn test_alias() {
1253		let expr = Expression::Alias(AliasExpression {
1254			alias: IdentExpression(internal_fragment("user_name")),
1255			expression: Box::new(column_expr("name")),
1256			fragment: Fragment::None,
1257		});
1258
1259		let json = to_json(&expr);
1260		assert_eq!(
1261			json,
1262			r#"{"type":"alias","alias":"user_name","expression":{"type":"column","namespace":"_context","shape":"_context","name":"name"}}"#
1263		);
1264
1265		let recovered = from_json(&json).unwrap();
1266		assert_eq!(to_json(&recovered), json);
1267	}
1268
1269	#[test]
1270	fn test_cast() {
1271		let expr = Expression::Cast(CastExpression {
1272			expression: Box::new(column_expr("value")),
1273			to: TypeExpression {
1274				ty: Type::Int4,
1275				fragment: internal_fragment("Int4"),
1276			},
1277			fragment: Fragment::None,
1278		});
1279
1280		let json = to_json(&expr);
1281		assert_eq!(
1282			json,
1283			r#"{"type":"cast","expression":{"type":"column","namespace":"_context","shape":"_context","name":"value"},"to":"Int4"}"#
1284		);
1285
1286		let recovered = from_json(&json).unwrap();
1287		assert_eq!(to_json(&recovered), json);
1288	}
1289
1290	#[test]
1291	fn test_call() {
1292		let expr = Expression::Call(CallExpression {
1293			func: IdentExpression(internal_fragment("math::avg")),
1294			args: vec![column_expr("price")],
1295			fragment: Fragment::None,
1296		});
1297
1298		let json = to_json(&expr);
1299		assert_eq!(
1300			json,
1301			r#"{"type":"call","function":"math::avg","args":[{"type":"column","namespace":"_context","shape":"_context","name":"price"}]}"#
1302		);
1303
1304		let recovered = from_json(&json).unwrap();
1305		assert_eq!(to_json(&recovered), json);
1306	}
1307
1308	#[test]
1309	fn test_tuple() {
1310		let expr = Expression::Tuple(TupleExpression {
1311			expressions: vec![constant_number("1"), constant_number("2"), constant_number("3")],
1312			fragment: Fragment::None,
1313		});
1314
1315		let json = to_json(&expr);
1316		assert_eq!(
1317			json,
1318			r#"{"type":"tuple","expressions":[{"type":"number","value":"1"},{"type":"number","value":"2"},{"type":"number","value":"3"}]}"#
1319		);
1320
1321		let recovered = from_json(&json).unwrap();
1322		assert_eq!(to_json(&recovered), json);
1323	}
1324
1325	#[test]
1326	fn test_prefix_minus() {
1327		let expr = Expression::Prefix(PrefixExpression {
1328			operator: PrefixOperator::Minus(Fragment::None),
1329			expression: Box::new(column_expr("value")),
1330			fragment: Fragment::None,
1331		});
1332
1333		let json = to_json(&expr);
1334		assert_eq!(
1335			json,
1336			r#"{"type":"prefix","operator":"-","expression":{"type":"column","namespace":"_context","shape":"_context","name":"value"}}"#
1337		);
1338
1339		let recovered = from_json(&json).unwrap();
1340		assert_eq!(to_json(&recovered), json);
1341	}
1342
1343	#[test]
1344	fn test_prefix_not() {
1345		let expr = Expression::Prefix(PrefixExpression {
1346			operator: PrefixOperator::Not(Fragment::None),
1347			expression: Box::new(column_expr("flag")),
1348			fragment: Fragment::None,
1349		});
1350
1351		let json = to_json(&expr);
1352		assert_eq!(
1353			json,
1354			r#"{"type":"prefix","operator":"not","expression":{"type":"column","namespace":"_context","shape":"_context","name":"flag"}}"#
1355		);
1356
1357		let recovered = from_json(&json).unwrap();
1358		assert_eq!(to_json(&recovered), json);
1359	}
1360
1361	#[test]
1362	fn test_between() {
1363		let expr = Expression::Between(BetweenExpression {
1364			value: Box::new(column_expr("age")),
1365			lower: Box::new(constant_number("18")),
1366			upper: Box::new(constant_number("65")),
1367			fragment: Fragment::None,
1368		});
1369
1370		let json = to_json(&expr);
1371		assert_eq!(
1372			json,
1373			r#"{"type":"between","value":{"type":"column","namespace":"_context","shape":"_context","name":"age"},"lower":{"type":"number","value":"18"},"upper":{"type":"number","value":"65"}}"#
1374		);
1375
1376		let recovered = from_json(&json).unwrap();
1377		assert_eq!(to_json(&recovered), json);
1378	}
1379
1380	#[test]
1381	fn test_in() {
1382		let expr = Expression::In(InExpression {
1383			value: Box::new(column_expr("status")),
1384			list: Box::new(Expression::Tuple(TupleExpression {
1385				expressions: vec![constant_text("active"), constant_text("pending")],
1386				fragment: Fragment::None,
1387			})),
1388			negated: false,
1389			fragment: Fragment::None,
1390		});
1391
1392		let json = to_json(&expr);
1393		assert_eq!(
1394			json,
1395			r#"{"type":"in","value":{"type":"column","namespace":"_context","shape":"_context","name":"status"},"list":{"type":"tuple","expressions":[{"type":"text","value":"active"},{"type":"text","value":"pending"}]},"negated":false}"#
1396		);
1397
1398		let recovered = from_json(&json).unwrap();
1399		assert_eq!(to_json(&recovered), json);
1400	}
1401
1402	#[test]
1403	fn test_not_in() {
1404		let expr = Expression::In(InExpression {
1405			value: Box::new(column_expr("type")),
1406			list: Box::new(Expression::Tuple(TupleExpression {
1407				expressions: vec![constant_text("deleted"), constant_text("archived")],
1408				fragment: Fragment::None,
1409			})),
1410			negated: true,
1411			fragment: Fragment::None,
1412		});
1413
1414		let json = to_json(&expr);
1415		assert_eq!(
1416			json,
1417			r#"{"type":"in","value":{"type":"column","namespace":"_context","shape":"_context","name":"type"},"list":{"type":"tuple","expressions":[{"type":"text","value":"deleted"},{"type":"text","value":"archived"}]},"negated":true}"#
1418		);
1419
1420		let recovered = from_json(&json).unwrap();
1421		assert_eq!(to_json(&recovered), json);
1422	}
1423
1424	#[test]
1425	fn test_if_simple() {
1426		let expr = Expression::If(IfExpression {
1427			condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1428				left: Box::new(column_expr("age")),
1429				right: Box::new(constant_number("18")),
1430				fragment: Fragment::None,
1431			})),
1432			then_expr: Box::new(constant_text("adult")),
1433			else_ifs: vec![],
1434			else_expr: Some(Box::new(constant_text("minor"))),
1435			fragment: Fragment::None,
1436		});
1437
1438		let json = to_json(&expr);
1439		assert_eq!(
1440			json,
1441			r#"{"type":"if","condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"age"},"right":{"type":"number","value":"18"}},"then":{"type":"text","value":"adult"},"else_ifs":[],"else_expr":{"type":"text","value":"minor"}}"#
1442		);
1443
1444		let recovered = from_json(&json).unwrap();
1445		assert_eq!(to_json(&recovered), json);
1446	}
1447
1448	#[test]
1449	fn test_if_with_else_if() {
1450		let expr = Expression::If(IfExpression {
1451			condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1452				left: Box::new(column_expr("score")),
1453				right: Box::new(constant_number("90")),
1454				fragment: Fragment::None,
1455			})),
1456			then_expr: Box::new(constant_text("A")),
1457			else_ifs: vec![ElseIfExpression {
1458				condition: Box::new(Expression::GreaterThan(GreaterThanExpression {
1459					left: Box::new(column_expr("score")),
1460					right: Box::new(constant_number("80")),
1461					fragment: Fragment::None,
1462				})),
1463				then_expr: Box::new(constant_text("B")),
1464				fragment: Fragment::None,
1465			}],
1466			else_expr: Some(Box::new(constant_text("C"))),
1467			fragment: Fragment::None,
1468		});
1469
1470		let json = to_json(&expr);
1471		assert_eq!(
1472			json,
1473			r#"{"type":"if","condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"score"},"right":{"type":"number","value":"90"}},"then":{"type":"text","value":"A"},"else_ifs":[{"condition":{"type":"greater_than","left":{"type":"column","namespace":"_context","shape":"_context","name":"score"},"right":{"type":"number","value":"80"}},"then":{"type":"text","value":"B"}}],"else_expr":{"type":"text","value":"C"}}"#
1474		);
1475
1476		let recovered = from_json(&json).unwrap();
1477		assert_eq!(to_json(&recovered), json);
1478	}
1479
1480	#[test]
1481	fn test_map() {
1482		let expr = Expression::Map(MapExpression {
1483			expressions: vec![
1484				Expression::Alias(AliasExpression {
1485					alias: IdentExpression(internal_fragment("user_name")),
1486					expression: Box::new(column_expr("name")),
1487					fragment: Fragment::None,
1488				}),
1489				column_expr("id"),
1490			],
1491			fragment: Fragment::None,
1492		});
1493
1494		let json = to_json(&expr);
1495		assert_eq!(
1496			json,
1497			r#"{"type":"map","expressions":[{"type":"alias","alias":"user_name","expression":{"type":"column","namespace":"_context","shape":"_context","name":"name"}},{"type":"column","namespace":"_context","shape":"_context","name":"id"}]}"#
1498		);
1499
1500		let recovered = from_json(&json).unwrap();
1501		assert_eq!(to_json(&recovered), json);
1502	}
1503
1504	#[test]
1505	fn test_extend() {
1506		let expr = Expression::Extend(ExtendExpression {
1507			expressions: vec![Expression::Alias(AliasExpression {
1508				alias: IdentExpression(internal_fragment("full_name")),
1509				expression: Box::new(Expression::Add(AddExpression {
1510					left: Box::new(column_expr("first")),
1511					right: Box::new(column_expr("last")),
1512					fragment: Fragment::None,
1513				})),
1514				fragment: Fragment::None,
1515			})],
1516			fragment: Fragment::None,
1517		});
1518
1519		let json = to_json(&expr);
1520		assert_eq!(
1521			json,
1522			r#"{"type":"extend","expressions":[{"type":"alias","alias":"full_name","expression":{"type":"add","left":{"type":"column","namespace":"_context","shape":"_context","name":"first"},"right":{"type":"column","namespace":"_context","shape":"_context","name":"last"}}}]}"#
1523		);
1524
1525		let recovered = from_json(&json).unwrap();
1526		assert_eq!(to_json(&recovered), json);
1527	}
1528
1529	#[test]
1530	fn test_type_expression() {
1531		let expr = Expression::Type(TypeExpression {
1532			ty: Type::Utf8,
1533			fragment: internal_fragment("Utf8"),
1534		});
1535
1536		let json = to_json(&expr);
1537		assert_eq!(json, r#"{"type":"type","ty":"Utf8"}"#);
1538
1539		let recovered = from_json(&json).unwrap();
1540		assert_eq!(to_json(&recovered), json);
1541	}
1542
1543	#[test]
1544	fn test_complex_nested_expression() {
1545		// Build: (age > 18 AND status = 'active') OR (role = 'admin')
1546		let expr = Expression::Or(OrExpression {
1547			left: Box::new(Expression::And(AndExpression {
1548				left: Box::new(Expression::GreaterThan(GreaterThanExpression {
1549					left: Box::new(column_expr("age")),
1550					right: Box::new(constant_number("18")),
1551					fragment: Fragment::None,
1552				})),
1553				right: Box::new(Expression::Equal(EqExpression {
1554					left: Box::new(column_expr("status")),
1555					right: Box::new(constant_text("active")),
1556					fragment: Fragment::None,
1557				})),
1558				fragment: Fragment::None,
1559			})),
1560			right: Box::new(Expression::Equal(EqExpression {
1561				left: Box::new(column_expr("role")),
1562				right: Box::new(constant_text("admin")),
1563				fragment: Fragment::None,
1564			})),
1565			fragment: Fragment::None,
1566		});
1567
1568		let json = to_json(&expr);
1569		let recovered = from_json(&json).unwrap();
1570		assert_eq!(to_json(&recovered), json);
1571
1572		// Verify pretty print works
1573		let pretty = to_json_pretty(&expr);
1574		assert!(pretty.contains('\n'));
1575		assert!(pretty.contains("greater_than"));
1576	}
1577
1578	#[test]
1579	fn test_access_source() {
1580		let expr = Expression::AccessSource(AccessShapeExpression {
1581			column: ColumnIdentifier {
1582				shape: ColumnShape::Qualified {
1583					namespace: internal_fragment("public"),
1584					name: internal_fragment("users"),
1585				},
1586				name: internal_fragment("email"),
1587			},
1588		});
1589
1590		let json = to_json(&expr);
1591		assert_eq!(json, r#"{"type":"access_source","namespace":"public","shape":"users","name":"email"}"#);
1592
1593		let recovered = from_json(&json).unwrap();
1594		assert_eq!(to_json(&recovered), json);
1595	}
1596}