1use serde::{Deserialize, Serialize};
8use std::sync::atomic::{AtomicU64, Ordering};
9use uqa_core::Value;
10
11use super::{
12 FromClause, FunctionBinding, FunctionBody, MergeWhen, OnConflictAction, SelectStmt, Statement,
13 CTE,
14};
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
20#[doc(hidden)]
21pub struct InternalRelationId(u64);
22
23impl InternalRelationId {
24 #[must_use]
26 pub fn allocate() -> Self {
27 static NEXT_ID: AtomicU64 = AtomicU64::new(1);
28 let id = NEXT_ID
29 .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| {
30 current.checked_add(1)
31 })
32 .expect("internal relation identity space exhausted");
33 Self(id)
34 }
35
36 #[must_use]
38 pub fn column(self, attribute: usize) -> InternalColumnRef {
39 InternalColumnRef {
40 relation: self,
41 attribute: u32::try_from(attribute).expect("internal relation attribute exceeds u32"),
42 }
43 }
44
45 #[must_use]
46 pub const fn raw(self) -> u64 {
47 self.0
48 }
49
50 #[must_use]
51 pub const fn from_raw(raw: u64) -> Self {
52 Self(raw)
53 }
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
60#[doc(hidden)]
61pub struct InternalColumnRef {
62 relation: InternalRelationId,
63 attribute: u32,
64}
65
66impl InternalColumnRef {
67 #[must_use]
68 pub const fn relation(self) -> InternalRelationId {
69 self.relation
70 }
71
72 #[must_use]
73 pub const fn attribute(self) -> usize {
74 self.attribute as usize
75 }
76
77 #[must_use]
78 pub const fn from_raw(relation: u64, attribute: u32) -> Self {
79 Self {
80 relation: InternalRelationId::from_raw(relation),
81 attribute,
82 }
83 }
84}
85
86#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
87pub struct Projection {
88 pub expr: Expr,
89 pub alias: Option<String>,
90}
91
92#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93pub struct OrderBy {
94 pub expr: Expr,
95 pub descending: bool,
96 pub nulls: Option<NullsOrder>,
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
103pub enum NullsOrder {
104 First,
105 Last,
106}
107
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct WindowSpec {
110 #[serde(default, skip_serializing_if = "Option::is_none")]
112 pub reference: Option<WindowReference>,
113 pub partition_by: Vec<Expr>,
114 pub order_by: Vec<OrderBy>,
115 pub frame: Option<WindowFrame>,
118}
119
120#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121pub struct WindowReference {
122 pub name: String,
123 pub kind: WindowReferenceKind,
124}
125
126#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
127pub enum WindowReferenceKind {
128 Direct,
130 Copy,
132}
133
134#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct WindowFrame {
136 pub mode: FrameMode,
137 pub start: FrameBound,
138 pub end: FrameBound,
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
142pub enum FrameMode {
143 Rows,
144 Range,
145 Groups,
146}
147
148#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
149pub enum FrameBound {
150 UnboundedPreceding,
151 UnboundedFollowing,
152 CurrentRow,
153 Preceding(Box<Expr>),
154 Following(Box<Expr>),
155}
156
157#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159pub enum Expr {
160 Star,
161 QualifiedStar(String),
163 Default,
167 Column(String),
169 QualifiedColumn {
171 qualifier: String,
172 column: String,
173 },
174 #[doc(hidden)]
177 InternalColumn(InternalColumnRef),
178 Literal(Value),
179 Param(usize),
181 Func {
184 name: String,
185 #[serde(default, skip_serializing_if = "Option::is_none")]
186 binding: Option<FunctionBinding>,
187 args: Vec<Expr>,
188 distinct: bool,
191 order_by: Vec<OrderBy>,
194 filter: Option<Box<Expr>>,
196 },
197 Array(Vec<Expr>),
200 Row(Vec<Expr>),
202 Binary {
204 op: BinaryOp,
205 lhs: Box<Expr>,
206 rhs: Box<Expr>,
207 },
208 UnaryMinus(Box<Expr>),
211 Not(Box<Expr>),
213 And(Vec<Expr>),
215 Or(Vec<Expr>),
217 IsNull {
219 expr: Box<Expr>,
220 negated: bool,
221 },
222 Between {
224 expr: Box<Expr>,
225 low: Box<Expr>,
226 high: Box<Expr>,
227 },
228 InList {
230 expr: Box<Expr>,
231 list: Vec<Expr>,
232 negated: bool,
233 },
234 WindowCall {
236 name: String,
237 args: Vec<Expr>,
238 spec: WindowSpec,
239 },
240 Case {
245 base: Option<Box<Expr>>,
246 when: Vec<(Expr, Expr)>,
247 else_branch: Option<Box<Expr>>,
248 },
249 Cast {
252 expr: Box<Expr>,
253 ty: String,
254 },
255 ScalarSubquery(Box<SelectStmt>),
258 Exists {
261 body: Box<SelectStmt>,
262 negated: bool,
263 },
264 InSubquery {
268 expr: Box<Expr>,
269 body: Box<SelectStmt>,
270 negated: bool,
271 },
272}
273
274impl Expr {
275 pub fn qualified_column(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
276 Self::QualifiedColumn {
277 qualifier: qualifier.into(),
278 column: column.into(),
279 }
280 }
281
282 #[doc(hidden)]
285 #[expect(
286 clippy::too_many_lines,
287 reason = "exhaustive AST migration preserves every serialized variant"
288 )]
289 pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
290 let mut changed = false;
291 match self {
292 Self::Func {
293 name,
294 binding,
295 args,
296 order_by,
297 filter,
298 ..
299 } => {
300 for argument in args {
301 changed |= argument.upgrade_legacy_serialized_dispatches();
302 }
303 for order in order_by {
304 changed |= order.expr.upgrade_legacy_serialized_dispatches();
305 }
306 if let Some(filter) = filter {
307 changed |= filter.upgrade_legacy_serialized_dispatches();
308 }
309 changed |=
310 super::FunctionBinding::upgrade_legacy_serialized_dispatch(name, binding);
311 }
312 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
313 for item in items {
314 changed |= item.upgrade_legacy_serialized_dispatches();
315 }
316 }
317 Self::Binary { lhs, rhs, .. } => {
318 changed |= lhs.upgrade_legacy_serialized_dispatches();
319 changed |= rhs.upgrade_legacy_serialized_dispatches();
320 }
321 Self::UnaryMinus(inner)
322 | Self::Not(inner)
323 | Self::IsNull { expr: inner, .. }
324 | Self::Cast { expr: inner, .. } => {
325 changed |= inner.upgrade_legacy_serialized_dispatches();
326 }
327 Self::Between { expr, low, high } => {
328 changed |= expr.upgrade_legacy_serialized_dispatches();
329 changed |= low.upgrade_legacy_serialized_dispatches();
330 changed |= high.upgrade_legacy_serialized_dispatches();
331 }
332 Self::InList { expr, list, .. } => {
333 changed |= expr.upgrade_legacy_serialized_dispatches();
334 for item in list {
335 changed |= item.upgrade_legacy_serialized_dispatches();
336 }
337 }
338 Self::WindowCall { args, spec, .. } => {
339 for argument in args {
340 changed |= argument.upgrade_legacy_serialized_dispatches();
341 }
342 for partition in &mut spec.partition_by {
343 changed |= partition.upgrade_legacy_serialized_dispatches();
344 }
345 for order in &mut spec.order_by {
346 changed |= order.expr.upgrade_legacy_serialized_dispatches();
347 }
348 if let Some(frame) = &mut spec.frame {
349 for bound in [&mut frame.start, &mut frame.end] {
350 match bound {
351 FrameBound::Preceding(expression)
352 | FrameBound::Following(expression) => {
353 changed |= expression.upgrade_legacy_serialized_dispatches();
354 }
355 FrameBound::UnboundedPreceding
356 | FrameBound::UnboundedFollowing
357 | FrameBound::CurrentRow => {}
358 }
359 }
360 }
361 }
362 Self::Case {
363 base,
364 when,
365 else_branch,
366 } => {
367 if let Some(base) = base {
368 changed |= base.upgrade_legacy_serialized_dispatches();
369 }
370 for (condition, result) in when {
371 changed |= condition.upgrade_legacy_serialized_dispatches();
372 changed |= result.upgrade_legacy_serialized_dispatches();
373 }
374 if let Some(branch) = else_branch {
375 changed |= branch.upgrade_legacy_serialized_dispatches();
376 }
377 }
378 Self::InSubquery { expr, body, .. } => {
379 changed |= expr.upgrade_legacy_serialized_dispatches();
380 changed |= body.upgrade_legacy_serialized_dispatches();
381 }
382 Self::ScalarSubquery(body) | Self::Exists { body, .. } => {
383 changed |= body.upgrade_legacy_serialized_dispatches();
384 }
385 Self::Default
386 | Self::Star
387 | Self::QualifiedStar(_)
388 | Self::Column(_)
389 | Self::QualifiedColumn { .. }
390 | Self::InternalColumn(_)
391 | Self::Literal(_)
392 | Self::Param(_) => {}
393 }
394 changed
395 }
396
397 #[must_use]
399 pub fn contains_window(&self) -> bool {
400 self.any_node(&|node| matches!(node, Self::WindowCall { .. }))
401 }
402
403 #[must_use]
405 pub fn contains_aggregate(&self) -> bool {
406 self.any_node(
407 &|node| matches!(node, Self::Func { name, .. } if is_builtin_aggregate_function(name)),
408 )
409 }
410
411 #[must_use]
413 pub fn contains_unqualified_column(&self) -> bool {
414 self.any_node(&|node| matches!(node, Self::Column(_)))
415 }
416
417 #[must_use]
419 pub fn contains_function_with_unknown_strictness(&self) -> bool {
420 self.any_node(&|node| {
421 matches!(
422 node,
423 Self::Func {
424 name,
425 args,
426 binding,
427 ..
428 } if crate::expr::bound_scalar_function_strictness(
429 name,
430 binding.as_ref(),
431 args.len(),
432 )
433 .is_none()
434 )
435 })
436 }
437
438 #[must_use]
440 pub fn any_node(&self, hit: &dyn Fn(&Self) -> bool) -> bool {
441 if hit(self) {
442 return true;
443 }
444 match self {
445 Self::Func {
446 args,
447 order_by,
448 filter,
449 ..
450 } => {
451 args.iter().any(|arg| arg.any_node(hit))
452 || order_by.iter().any(|order| order.expr.any_node(hit))
453 || filter.as_deref().is_some_and(|filter| filter.any_node(hit))
454 }
455 Self::Array(items) | Self::Row(items) | Self::And(items) | Self::Or(items) => {
456 items.iter().any(|item| item.any_node(hit))
457 }
458 Self::UnaryMinus(expr) | Self::Not(expr) | Self::Cast { expr, .. } => {
459 expr.any_node(hit)
460 }
461 Self::Binary { lhs, rhs, .. } => lhs.any_node(hit) || rhs.any_node(hit),
462 Self::IsNull { expr, .. } | Self::InSubquery { expr, .. } => expr.any_node(hit),
463 Self::Between { expr, low, high } => {
464 expr.any_node(hit) || low.any_node(hit) || high.any_node(hit)
465 }
466 Self::InList { expr, list, .. } => {
467 expr.any_node(hit) || list.iter().any(|item| item.any_node(hit))
468 }
469 Self::Case {
470 base,
471 when,
472 else_branch,
473 } => {
474 base.as_deref().is_some_and(|base| base.any_node(hit))
475 || when
476 .iter()
477 .any(|(condition, result)| condition.any_node(hit) || result.any_node(hit))
478 || else_branch
479 .as_deref()
480 .is_some_and(|branch| branch.any_node(hit))
481 }
482 Self::WindowCall { .. }
483 | Self::Star
484 | Self::QualifiedStar(_)
485 | Self::Default
486 | Self::Column(_)
487 | Self::QualifiedColumn { .. }
488 | Self::InternalColumn(_)
489 | Self::Literal(_)
490 | Self::Param(_)
491 | Self::ScalarSubquery(_)
492 | Self::Exists { .. } => false,
493 }
494 }
495}
496
497fn upgrade_exprs(expressions: &mut [Expr]) -> bool {
498 expressions.iter_mut().fold(false, |changed, expression| {
499 expression.upgrade_legacy_serialized_dispatches() | changed
500 })
501}
502
503fn upgrade_rows(rows: &mut [Vec<Expr>]) -> bool {
504 rows.iter_mut()
505 .fold(false, |changed, row| upgrade_exprs(row) | changed)
506}
507
508fn upgrade_optional(expression: &mut Option<Expr>) -> bool {
509 expression
510 .as_mut()
511 .is_some_and(Expr::upgrade_legacy_serialized_dispatches)
512}
513
514fn upgrade_projections(projections: &mut [Projection]) -> bool {
515 projections.iter_mut().fold(false, |changed, projection| {
516 projection.expr.upgrade_legacy_serialized_dispatches() | changed
517 })
518}
519
520fn upgrade_assignments(assignments: &mut [(String, Expr)]) -> bool {
521 assignments
522 .iter_mut()
523 .fold(false, |changed, (_, expression)| {
524 expression.upgrade_legacy_serialized_dispatches() | changed
525 })
526}
527
528fn upgrade_ctes(ctes: &mut [CTE]) -> bool {
529 ctes.iter_mut().fold(false, |mut changed, cte| {
530 if let Some(cycle) = &mut cte.cycle {
531 changed |= cycle.mark_value.upgrade_legacy_serialized_dispatches();
532 changed |= cycle.mark_default.upgrade_legacy_serialized_dispatches();
533 }
534 changed | cte.query.upgrade_legacy_serialized_dispatches()
535 })
536}
537
538impl FromClause {
539 fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
540 match self {
541 Self::Table { .. } => false,
542 Self::Join {
543 left, right, on, ..
544 } => {
545 left.upgrade_legacy_serialized_dispatches()
546 | right.upgrade_legacy_serialized_dispatches()
547 | upgrade_optional(on)
548 }
549 Self::Values { rows, .. } => upgrade_rows(rows),
550 Self::Function { args, .. } => upgrade_exprs(args),
551 Self::FunctionGroup { functions, .. } => {
552 functions.iter_mut().fold(false, |changed, function| {
553 upgrade_exprs(&mut function.args) | changed
554 })
555 }
556 Self::Subquery { body, .. } => body.upgrade_legacy_serialized_dispatches(),
557 }
558 }
559}
560
561impl SelectStmt {
562 #[doc(hidden)]
564 pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
565 let mut changed = upgrade_projections(&mut self.projections);
566 changed |= upgrade_rows(&mut self.values);
567 if let Some(from) = &mut self.from {
568 changed |= from.upgrade_legacy_serialized_dispatches();
569 }
570 changed |= upgrade_optional(&mut self.r#where);
571 changed |= upgrade_exprs(&mut self.group_by);
572 for grouping_set in &mut self.grouping_sets {
573 changed |= upgrade_exprs(grouping_set);
574 }
575 changed |= upgrade_optional(&mut self.having);
576 for order in &mut self.order_by {
577 changed |= order.expr.upgrade_legacy_serialized_dispatches();
578 }
579 changed |= upgrade_optional(&mut self.limit);
580 changed |= upgrade_optional(&mut self.offset);
581 changed |= upgrade_ctes(&mut self.with);
582 if let Some(set_operation) = &mut self.set_op {
583 if let Some(left) = &mut set_operation.left {
584 changed |= left.upgrade_legacy_serialized_dispatches();
585 }
586 changed |= set_operation.right.upgrade_legacy_serialized_dispatches();
587 for order in &mut set_operation.combined_order_by {
588 changed |= order.expr.upgrade_legacy_serialized_dispatches();
589 }
590 changed |= upgrade_optional(&mut set_operation.combined_limit);
591 changed |= upgrade_optional(&mut set_operation.combined_offset);
592 }
593 changed | upgrade_exprs(&mut self.distinct_on)
594 }
595}
596
597impl MergeWhen {
598 fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
599 match self {
600 Self::UpdateMatched {
601 condition,
602 assignments,
603 }
604 | Self::UpdateNotMatchedBySource {
605 condition,
606 assignments,
607 } => upgrade_optional(condition) | upgrade_assignments(assignments),
608 Self::InsertNotMatched {
609 condition, values, ..
610 } => upgrade_optional(condition) | upgrade_exprs(values),
611 Self::DeleteMatched { condition }
612 | Self::DeleteNotMatchedBySource { condition }
613 | Self::NothingMatched { condition }
614 | Self::NothingNotMatched { condition }
615 | Self::NothingNotMatchedBySource { condition } => upgrade_optional(condition),
616 }
617 }
618}
619
620impl Statement {
621 #[doc(hidden)]
623 #[expect(
624 clippy::too_many_lines,
625 reason = "exhaustive AST migration preserves every serialized variant"
626 )]
627 pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
628 match self {
629 Self::Select(select) => select.upgrade_legacy_serialized_dispatches(),
630 Self::Insert(insert) => {
631 let mut changed = upgrade_ctes(&mut insert.with);
632 changed |= upgrade_rows(&mut insert.rows);
633 if let Some(source) = &mut insert.select_source {
634 changed |= source.upgrade_legacy_serialized_dispatches();
635 }
636 if let Some(conflict) = &mut insert.on_conflict {
637 for expression in &mut conflict.expressions {
638 changed |= expression.upgrade_legacy_serialized_dispatches();
639 }
640 changed |= conflict
641 .predicate
642 .as_deref_mut()
643 .is_some_and(Expr::upgrade_legacy_serialized_dispatches);
644 if let OnConflictAction::Update {
645 assignments,
646 r#where,
647 } = &mut conflict.action
648 {
649 changed |= upgrade_assignments(assignments);
650 changed |= r#where
651 .as_deref_mut()
652 .is_some_and(Expr::upgrade_legacy_serialized_dispatches);
653 }
654 }
655 changed | upgrade_projections(&mut insert.returning)
656 }
657 Self::Update(update) => {
658 let mut changed = upgrade_assignments(&mut update.assignments);
659 changed |= upgrade_optional(&mut update.r#where);
660 changed |= upgrade_ctes(&mut update.with);
661 if let Some(from) = &mut update.from {
662 changed |= from.upgrade_legacy_serialized_dispatches();
663 }
664 changed | upgrade_projections(&mut update.returning)
665 }
666 Self::Delete(delete) => {
667 let mut changed = upgrade_optional(&mut delete.r#where);
668 changed |= upgrade_ctes(&mut delete.with);
669 if let Some(using) = &mut delete.using {
670 changed |= using.upgrade_legacy_serialized_dispatches();
671 }
672 changed | upgrade_projections(&mut delete.returning)
673 }
674 Self::CreateView { body, .. }
675 | Self::CreateMaterializedView { body, .. }
676 | Self::CreateTableAs { body, .. } => body.upgrade_legacy_serialized_dispatches(),
677 Self::DeclareCursor(cursor) => cursor.query.upgrade_legacy_serialized_dispatches(),
678 Self::Explain { body, .. } | Self::Prepare { body, .. } => {
679 body.upgrade_legacy_serialized_dispatches()
680 }
681 Self::Execute { params, .. } | Self::Call { args: params, .. } => upgrade_exprs(params),
682 Self::Values { rows } => upgrade_rows(rows),
683 Self::Merge(merge) => {
684 let mut changed = merge.source.upgrade_legacy_serialized_dispatches();
685 changed |= merge.join_condition.upgrade_legacy_serialized_dispatches();
686 for clause in &mut merge.when_clauses {
687 changed |= clause.upgrade_legacy_serialized_dispatches();
688 }
689 changed | upgrade_projections(&mut merge.returning)
690 }
691 Self::CreateFunction(definition) => {
692 let mut changed = definition
693 .params
694 .iter_mut()
695 .fold(false, |changed, parameter| {
696 parameter
697 .default
698 .as_mut()
699 .is_some_and(Expr::upgrade_legacy_serialized_dispatches)
700 | changed
701 });
702 if let FunctionBody::Statements(statements) = &mut definition.body {
703 for statement in statements {
704 changed |= statement.upgrade_legacy_serialized_dispatches();
705 }
706 }
707 changed
708 }
709 Self::CreateTrigger(trigger) => upgrade_optional(&mut trigger.when),
710 Self::CreateRule(rule) => {
711 let mut changed = upgrade_optional(&mut rule.condition);
712 for action in &mut rule.actions {
713 changed |= action.upgrade_legacy_serialized_dispatches();
714 }
715 changed
716 }
717 Self::CreateTable(_)
718 | Self::CreateTableIfNotExists(_)
719 | Self::CreateIndex(_)
720 | Self::Drop(_)
721 | Self::AlterTable(_)
722 | Self::AlterForeignTable(_)
723 | Self::AlterView(_)
724 | Self::RefreshMaterializedView { .. }
725 | Self::CreateSchema { .. }
726 | Self::Notify { .. }
727 | Self::Listen { .. }
728 | Self::Unlisten { .. }
729 | Self::SetVariable { .. }
730 | Self::ResetVariable { .. }
731 | Self::ResetAllVariables
732 | Self::SetConstraints { .. }
733 | Self::ShowVariable { .. }
734 | Self::Discard { .. }
735 | Self::Load { .. }
736 | Self::Analyze { .. }
737 | Self::Vacuum(_)
738 | Self::Truncate { .. }
739 | Self::Transaction(_)
740 | Self::FetchCursor(_)
741 | Self::CloseCursor { .. }
742 | Self::CreateSequence(_)
743 | Self::AlterSequence(_)
744 | Self::Deallocate { .. }
745 | Self::CreateForeignServer(_)
746 | Self::CreateForeignTable(_)
747 | Self::CreateForeignTableIfNotExists(_)
748 | Self::DropFunction(_)
749 | Self::AlterRoutine(_)
750 | Self::AlterRoutineOwner(_)
751 | Self::RenameRoutine(_)
752 | Self::GrantRoutine(_)
753 | Self::GrantTable(_)
754 | Self::GrantSequence(_)
755 | Self::GrantDatabase(_)
756 | Self::GrantSchema(_)
757 | Self::GrantRole(_)
758 | Self::CreateRole(_)
759 | Self::AlterRole(_)
760 | Self::DropRole(_)
761 | Self::DropTrigger(_)
762 | Self::DropRule(_)
763 | Self::DoBlock { .. } => false,
764 }
765 }
766}
767
768#[must_use]
770pub fn is_builtin_aggregate_function(name: &str) -> bool {
771 matches!(
772 name.to_ascii_lowercase().as_str(),
773 "count"
774 | "sum"
775 | "avg"
776 | "min"
777 | "max"
778 | "string_agg"
779 | "array_agg"
780 | "bool_and"
781 | "bool_or"
782 | "stddev"
783 | "stddev_samp"
784 | "stddev_pop"
785 | "variance"
786 | "var_samp"
787 | "var_pop"
788 | "percentile_cont"
789 | "percentile_disc"
790 | "mode"
791 | "json_agg"
792 | "jsonb_agg"
793 | "json_object_agg"
794 | "jsonb_object_agg"
795 )
796}
797
798#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
799pub enum BinaryOp {
800 Equal,
801 NotEqual,
802 Less,
803 LessEqual,
804 Greater,
805 GreaterEqual,
806 Add,
807 Subtract,
808 Multiply,
809 Divide,
810}
811
812pub type ValueExpr = Expr;