Skip to main content

uqa_sql/ast/
expressions.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7use serde::{Deserialize, Serialize};
8use std::sync::atomic::{AtomicU64, Ordering};
9use uqa_core::Value;
10
11use super::{FunctionBinding, SelectStmt};
12
13/// Query-local identity for an executor-only row source. Parser-produced SQL never contains this identity, so internal row carriers cannot collide with user relation aliases.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[doc(hidden)]
16pub struct InternalRelationId(u64);
17
18impl InternalRelationId {
19    /// Allocate an opaque relation identity for an engine-injected row source.
20    #[must_use]
21    pub fn allocate() -> Self {
22        static NEXT_ID: AtomicU64 = AtomicU64::new(1);
23        let id = uqa_core::atomic::try_update_u64(
24            &NEXT_ID,
25            Ordering::Relaxed,
26            Ordering::Relaxed,
27            |current| current.checked_add(1),
28        )
29        .expect("internal relation identity space exhausted");
30        Self(id)
31    }
32
33    /// Address one zero-based attribute of this internal relation.
34    #[must_use]
35    pub fn column(self, attribute: usize) -> InternalColumnRef {
36        InternalColumnRef {
37            relation: self,
38            attribute: u32::try_from(attribute).expect("internal relation attribute exceeds u32"),
39        }
40    }
41
42    #[must_use]
43    pub const fn raw(self) -> u64 {
44        self.0
45    }
46
47    #[must_use]
48    pub const fn from_raw(raw: u64) -> Self {
49        Self(raw)
50    }
51}
52
53/// Structural reference to an executor-only relation attribute. This is the UQA analogue of PostgreSQL's `Var(varno, varattno)` identity: it is never resolved through SQL text names.
54#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
55#[doc(hidden)]
56pub struct InternalColumnRef {
57    relation: InternalRelationId,
58    attribute: u32,
59}
60
61impl InternalColumnRef {
62    #[must_use]
63    pub const fn relation(self) -> InternalRelationId {
64        self.relation
65    }
66
67    #[must_use]
68    pub const fn attribute(self) -> usize {
69        self.attribute as usize
70    }
71
72    #[must_use]
73    pub const fn from_raw(relation: u64, attribute: u32) -> Self {
74        Self {
75            relation: InternalRelationId::from_raw(relation),
76            attribute,
77        }
78    }
79}
80
81#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
82pub struct Projection {
83    pub expr: Expr,
84    pub alias: Option<String>,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub struct OrderBy {
89    pub expr: Expr,
90    pub descending: bool,
91    /// `NULLS FIRST` / `NULLS LAST` placement. `None` means the
92    /// SQL-standard default - `NULLS LAST` for ASC and `NULLS FIRST`
93    /// for DESC. Mirrors `PostgreSQL` semantics.
94    pub nulls: Option<NullsOrder>,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
98pub enum NullsOrder {
99    First,
100    Last,
101}
102
103/// One query-local window definition. The specification owns only its written clauses; inheritance is resolved through an earlier named definition.
104#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
105pub struct WindowDefinition<S = WindowSpec> {
106    pub name: Option<String>,
107    pub inherited: Option<usize>,
108    pub spec: S,
109}
110
111/// Compiler-only equality key for a raw window declaration; not part of stored SQL.
112#[derive(Debug, Clone, PartialEq)]
113pub struct WindowDefinitionSyntax(pub(crate) serde_json::Value);
114
115#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116pub struct WindowSpec {
117    /// Raw syntax survives only until the compiler selects its query-local definition.
118    #[serde(skip)]
119    pub raw_definition: Option<WindowDefinitionSyntax>,
120    /// Canonical definition in the enclosing query block. Older stored inline specifications have no slot.
121    #[serde(default, skip_serializing_if = "Option::is_none")]
122    pub definition: Option<usize>,
123    /// Named window referenced by this specification while the SQL compiler resolves a `WINDOW` clause. Compiler-produced plans clear this field before lowering into the unified scalar IR.
124    #[serde(default, skip_serializing_if = "Option::is_none")]
125    pub reference: Option<WindowReference>,
126    pub partition_by: Vec<Expr>,
127    pub order_by: Vec<OrderBy>,
128    /// `ROWS` / `RANGE` frame, or `None` when not specified (defaults
129    /// to `RANGE BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`).
130    pub frame: Option<WindowFrame>,
131}
132
133impl WindowSpec {
134    /// Scalar roots owned by this specification, excluding separately stored inherited clauses.
135    pub fn expressions_mut(&mut self) -> impl Iterator<Item = &mut Expr> {
136        self.partition_by
137            .iter_mut()
138            .chain(self.order_by.iter_mut().map(|order| &mut order.expr))
139            .chain(
140                self.frame
141                    .iter_mut()
142                    .flat_map(|frame| [&mut frame.start, &mut frame.end])
143                    .filter_map(|bound| match bound {
144                        FrameBound::Preceding(value) | FrameBound::Following(value) => {
145                            Some(value.as_mut())
146                        }
147                        _ => None,
148                    }),
149            )
150    }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct WindowReference {
155    pub name: String,
156    pub kind: WindowReferenceKind,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
160pub enum WindowReferenceKind {
161    /// `OVER window_name` uses the named definition directly, including its frame.
162    Direct,
163    /// `OVER (window_name ...)` or `WINDOW child AS (parent ...)` copies and may extend a frameless definition.
164    Copy,
165}
166
167#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
168pub struct WindowFrame {
169    pub mode: FrameMode,
170    pub start: FrameBound,
171    pub end: FrameBound,
172    /// Whether the frame was written `BETWEEN start AND end`. A frame that names only its start, such as `ROWS UNBOUNDED PRECEDING`, ends at the current row, and the definition is deparsed as it was written.
173    #[serde(default = "super::default_true")]
174    pub between: bool,
175    #[serde(default, skip_serializing_if = "FrameExclusion::is_no_others")]
176    pub exclusion: FrameExclusion,
177}
178
179#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
180pub enum FrameMode {
181    Rows,
182    Range,
183    Groups,
184}
185
186/// The written function-call form, retained independently of overload binding. The durable `order_syntax` field keeps its original name for stored-expression compatibility.
187#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
188pub enum FunctionCallSyntax {
189    #[default]
190    Legacy,
191    Ordinary,
192    WithinGroup,
193    Extract,
194}
195
196/// The original public name, preserved for callers and stored ordering metadata.
197pub type FunctionOrderSyntax = FunctionCallSyntax;
198
199impl FunctionCallSyntax {
200    #[must_use]
201    pub const fn is_legacy(&self) -> bool {
202        matches!(self, Self::Legacy)
203    }
204}
205
206/// The aggregate modifiers written on a window call, which `ParseFuncOrColumn` rejects after it has resolved the function. The arguments of a call written with `WITHIN GROUP` include its ordering expressions.
207#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
208pub struct WindowCallModifiers {
209    pub distinct: bool,
210    /// An aggregate `ORDER BY` inside the argument list.
211    pub ordered: bool,
212    pub within_group: bool,
213}
214
215impl WindowCallModifiers {
216    #[must_use]
217    pub const fn is_empty(&self) -> bool {
218        !self.distinct && !self.ordered && !self.within_group
219    }
220}
221
222/// The frame exclusion clause: the rows of the frame that a window function or aggregate does not see.
223#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
224pub enum FrameExclusion {
225    #[default]
226    NoOthers,
227    CurrentRow,
228    /// The current row and its peers.
229    Group,
230    /// The peers of the current row, but not the current row itself.
231    Ties,
232}
233
234impl FrameExclusion {
235    #[must_use]
236    pub const fn is_no_others(&self) -> bool {
237        matches!(self, Self::NoOthers)
238    }
239
240    /// The clause as `pg_get_viewdef` spells it, or `None` for `EXCLUDE NO OTHERS`, which it omits.
241    #[must_use]
242    pub const fn sql(self) -> Option<&'static str> {
243        match self {
244            Self::NoOthers => None,
245            Self::CurrentRow => Some("EXCLUDE CURRENT ROW"),
246            Self::Group => Some("EXCLUDE GROUP"),
247            Self::Ties => Some("EXCLUDE TIES"),
248        }
249    }
250}
251
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub enum FrameBound {
254    UnboundedPreceding,
255    UnboundedFollowing,
256    CurrentRow,
257    Preceding(Box<Expr>),
258    Following(Box<Expr>),
259}
260
261/// Scalar expression nodes the compiler handles.
262#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
263pub enum Expr {
264    Star,
265    /// Relation-qualified wildcard projection (`table.*` or `alias.*`).
266    QualifiedStar(String),
267    /// `DEFAULT` in an INSERT/UPDATE assignment is a mutation marker resolved against the target column before expression evaluation.
268    Default,
269    /// Unqualified column reference (`col`).
270    Column(String),
271    /// Qualified column reference (`table.col` or `alias.col`).
272    QualifiedColumn {
273        qualifier: String,
274        column: String,
275    },
276    /// Structural column reference emitted internally rather than by SQL parsing; SQL name binding must not rewrite it.
277    #[doc(hidden)]
278    InternalColumn(InternalColumnRef),
279    Literal(Value),
280    /// An already-coerced runtime datum with its declared SQL type. Variable binding emits this leaf so reading a domain value does not repeat its constraints.
281    #[doc(hidden)]
282    TypedLiteral {
283        value: Value,
284        ty: String,
285        /// Selected implicit result collation; absent metadata retains the declared type default.
286        #[serde(default, skip_serializing_if = "Option::is_none")]
287        collation_oid: Option<u32>,
288        /// Original composite input retained across non-invertible descriptor changes.
289        #[serde(default, skip_serializing_if = "Option::is_none")]
290        composite_source: Option<Box<crate::expr::composites::CompositeConstantSource>>,
291    },
292    /// A positional bind parameter (`$1`, `$2`, ...).
293    Param(usize),
294    /// Function call dispatched through the function registry.
295    Func {
296        name: String,
297        #[serde(default, skip_serializing_if = "Option::is_none")]
298        binding: Option<FunctionBinding>,
299        args: Vec<Expr>,
300        /// `func(DISTINCT expr)` - only meaningful for aggregate
301        /// functions. Mirrors `PostgreSQL`'s `agg_distinct`.
302        distinct: bool,
303        /// Ordering expressions from either `func(expr ORDER BY ...)` or `func(expr) WITHIN GROUP (ORDER BY ...)`; `order_syntax` preserves their written location.
304        order_by: Vec<OrderBy>,
305        #[serde(default, skip_serializing_if = "FunctionOrderSyntax::is_legacy")]
306        order_syntax: FunctionOrderSyntax,
307        /// `func(...) FILTER (WHERE expr)` - aggregate-level row filter.
308        filter: Option<Box<Expr>>,
309    },
310    /// SQL array constructor.
311    Array(Vec<Expr>),
312    /// Anonymous SQL row constructor (`ROW(...)` or `(a, b)`).
313    Row(Vec<Expr>),
314    /// A stored typed row with creation-time attribute positions and already selected field conversions.
315    CompositeRow {
316        items: Vec<Expr>,
317        binding: super::CompositeRowBinding,
318    },
319    /// `lhs op rhs` - comparison or arithmetic.
320    Binary {
321        op: BinaryOp,
322        lhs: Box<Expr>,
323        rhs: Box<Expr>,
324    },
325    /// `PostgreSQL` prefix `-`, kept distinct from binary subtraction so the
326    /// operand's declared numeric width and overflow behavior survive lowering.
327    UnaryMinus(Box<Expr>),
328    /// `NOT expr`.
329    Not(Box<Expr>),
330    /// `cond_1 AND cond_2 AND ...` (n-ary).
331    And(Vec<Expr>),
332    /// `cond_1 OR cond_2 OR ...` (n-ary).
333    Or(Vec<Expr>),
334    /// `expr IS NULL` / `expr IS NOT NULL`.
335    IsNull {
336        expr: Box<Expr>,
337        negated: bool,
338    },
339    /// `expr BETWEEN low AND high`.
340    Between {
341        expr: Box<Expr>,
342        low: Box<Expr>,
343        high: Box<Expr>,
344    },
345    /// `expr IN (a, b, c)` literal list.
346    InList {
347        expr: Box<Expr>,
348        list: Vec<Expr>,
349        negated: bool,
350    },
351    /// `func(args) [FILTER (WHERE condition)] OVER (PARTITION BY ... ORDER BY ...)`. Only aggregates accept `FILTER`.
352    WindowCall {
353        name: String,
354        args: Vec<Expr>,
355        spec: Box<WindowSpec>,
356        #[serde(default, skip_serializing_if = "Option::is_none")]
357        filter: Option<Box<Expr>>,
358        #[serde(default, skip_serializing_if = "WindowCallModifiers::is_empty")]
359        modifiers: WindowCallModifiers,
360    },
361    /// `CASE [base] WHEN cond THEN result ... [ELSE default] END`.
362    /// `base` lifts simple-form `CASE expr WHEN val THEN ...` into an
363    /// optional comparison anchor; searched-form `CASE WHEN cond ...`
364    /// leaves it `None`.
365    Case {
366        base: Option<Box<Expr>>,
367        when: Vec<(Expr, Expr)>,
368        else_branch: Option<Box<Expr>>,
369    },
370    /// `CAST(expr AS type)`. The type name is preserved verbatim so
371    /// the evaluator can apply the correct coercion.
372    Cast {
373        /// Set by analysis for an implicit coercion retained in stored syntax.
374        #[serde(default, skip_serializing_if = "std::ops::Not::not")]
375        implicit: bool,
376        expr: Box<Expr>,
377        ty: String,
378    },
379    /// `(SELECT ...)` query expression: ordinary scalar consumers select one
380    /// column; a multiple-column SET target consumes the positional result.
381    ScalarSubquery(Box<SelectStmt>),
382    /// `EXISTS (SELECT ...)` -- truthy when the body produces at
383    /// least one row.
384    Exists {
385        body: Box<SelectStmt>,
386        negated: bool,
387    },
388    /// `expr [NOT] IN (SELECT ...)` set membership against a
389    /// subquery. Evaluator runs the body once per top-level
390    /// expression and tests membership.
391    InSubquery {
392        expr: Box<Expr>,
393        body: Box<SelectStmt>,
394        negated: bool,
395    },
396}
397
398impl Expr {
399    pub fn qualified_column(qualifier: impl Into<String>, column: impl Into<String>) -> Self {
400        Self::QualifiedColumn {
401            qualifier: qualifier.into(),
402            column: column.into(),
403        }
404    }
405
406    /// Upgrade compiler-owned function markers and range predicates in retained catalog syntax, preserving bound identities.
407    #[doc(hidden)]
408    #[expect(
409        clippy::too_many_lines,
410        reason = "exhaustive AST migration preserves every serialized variant"
411    )]
412    pub fn upgrade_legacy_serialized_dispatches(&mut self) -> bool {
413        let mut changed = false;
414        match self {
415            Self::Func {
416                name,
417                binding,
418                args,
419                order_by,
420                filter,
421                ..
422            } => {
423                for argument in args.iter_mut() {
424                    changed |= argument.upgrade_legacy_serialized_dispatches();
425                }
426                for order in order_by {
427                    changed |= order.expr.upgrade_legacy_serialized_dispatches();
428                }
429                if let Some(filter) = filter {
430                    changed |= filter.upgrade_legacy_serialized_dispatches();
431                }
432                changed |=
433                    super::FunctionBinding::upgrade_legacy_serialized_dispatch(name, binding);
434                changed |= super::FunctionBinding::upgrade_legacy_array_slices(
435                    binding,
436                    args.len(),
437                    |index| matches!(args[index], Expr::Literal(Value::Null)),
438                );
439            }
440            Self::Array(items)
441            | Self::Row(items)
442            | Self::CompositeRow { items, .. }
443            | Self::And(items)
444            | Self::Or(items) => {
445                for item in items {
446                    changed |= item.upgrade_legacy_serialized_dispatches();
447                }
448            }
449            Self::Binary { lhs, rhs, .. } => {
450                changed |= lhs.upgrade_legacy_serialized_dispatches();
451                changed |= rhs.upgrade_legacy_serialized_dispatches();
452            }
453            Self::UnaryMinus(inner)
454            | Self::Not(inner)
455            | Self::IsNull { expr: inner, .. }
456            | Self::Cast { expr: inner, .. } => {
457                changed |= inner.upgrade_legacy_serialized_dispatches();
458            }
459            Self::Between { expr, low, high } => {
460                changed |= expr.upgrade_legacy_serialized_dispatches();
461                changed |= low.upgrade_legacy_serialized_dispatches();
462                changed |= high.upgrade_legacy_serialized_dispatches();
463            }
464            Self::InList { expr, list, .. } => {
465                changed |= expr.upgrade_legacy_serialized_dispatches();
466                for item in list {
467                    changed |= item.upgrade_legacy_serialized_dispatches();
468                }
469            }
470            Self::WindowCall {
471                args, spec, filter, ..
472            } => {
473                for argument in args {
474                    changed |= argument.upgrade_legacy_serialized_dispatches();
475                }
476                if let Some(filter) = filter {
477                    changed |= filter.upgrade_legacy_serialized_dispatches();
478                }
479                for partition in &mut spec.partition_by {
480                    changed |= partition.upgrade_legacy_serialized_dispatches();
481                }
482                for order in &mut spec.order_by {
483                    changed |= order.expr.upgrade_legacy_serialized_dispatches();
484                }
485                if let Some(frame) = &mut spec.frame {
486                    for bound in [&mut frame.start, &mut frame.end] {
487                        match bound {
488                            FrameBound::Preceding(expression)
489                            | FrameBound::Following(expression) => {
490                                changed |= expression.upgrade_legacy_serialized_dispatches();
491                            }
492                            FrameBound::UnboundedPreceding
493                            | FrameBound::UnboundedFollowing
494                            | FrameBound::CurrentRow => {}
495                        }
496                    }
497                }
498            }
499            Self::Case {
500                base,
501                when,
502                else_branch,
503            } => {
504                if let Some(base) = base {
505                    changed |= base.upgrade_legacy_serialized_dispatches();
506                }
507                for (condition, result) in when {
508                    changed |= condition.upgrade_legacy_serialized_dispatches();
509                    changed |= result.upgrade_legacy_serialized_dispatches();
510                }
511                if let Some(branch) = else_branch {
512                    changed |= branch.upgrade_legacy_serialized_dispatches();
513                }
514            }
515            Self::InSubquery { expr, body, .. } => {
516                changed |= expr.upgrade_legacy_serialized_dispatches();
517                changed |= body.upgrade_legacy_serialized_dispatches();
518            }
519            Self::ScalarSubquery(body) | Self::Exists { body, .. } => {
520                changed |= body.upgrade_legacy_serialized_dispatches();
521            }
522            Self::Default
523            | Self::Star
524            | Self::QualifiedStar(_)
525            | Self::Column(_)
526            | Self::QualifiedColumn { .. }
527            | Self::InternalColumn(_)
528            | Self::Literal(_)
529            | Self::TypedLiteral { .. }
530            | Self::Param(_) => {}
531        }
532        crate::type_resolution::between::restore_ast_node(self) | changed
533    }
534
535    /// True when this expression tree contains a window function call.
536    #[must_use]
537    pub fn contains_window(&self) -> bool {
538        self.any_node(&|node| matches!(node, Self::WindowCall { .. }))
539    }
540
541    /// True when this expression tree contains a built-in aggregate call.
542    #[must_use]
543    pub fn contains_aggregate(&self) -> bool {
544        self.any_node(
545            &|node| matches!(node, Self::Func { name, .. } if is_builtin_aggregate_function(name)),
546        )
547    }
548
549    /// True when this expression contains a column whose owning relation can only be determined after catalog schemas have been bound.
550    #[must_use]
551    pub fn contains_unqualified_column(&self) -> bool {
552        self.any_node(&|node| matches!(node, Self::Column(_)))
553    }
554
555    /// True when this expression contains a function whose strictness cannot be decided without an engine catalog.
556    #[must_use]
557    pub fn contains_function_with_unknown_strictness(&self) -> bool {
558        self.any_node(&|node| {
559            matches!(
560                node,
561                Self::Func {
562                    name,
563                    args,
564                    binding,
565                    ..
566                } if crate::expr::bound_scalar_function_strictness(
567                    name,
568                    binding.as_ref(),
569                    args.len(),
570                )
571                .is_none()
572            )
573        })
574    }
575
576    /// Whether `hit` matches this node or any scalar node below it. Subquery bodies are opaque because they own independent query trees.
577    #[must_use]
578    pub fn any_node(&self, hit: &dyn Fn(&Self) -> bool) -> bool {
579        if hit(self) {
580            return true;
581        }
582        match self {
583            Self::Func {
584                args,
585                order_by,
586                filter,
587                ..
588            } => {
589                args.iter().any(|arg| arg.any_node(hit))
590                    || order_by.iter().any(|order| order.expr.any_node(hit))
591                    || filter.as_deref().is_some_and(|filter| filter.any_node(hit))
592            }
593            Self::Array(items)
594            | Self::Row(items)
595            | Self::CompositeRow { items, .. }
596            | Self::And(items)
597            | Self::Or(items) => items.iter().any(|item| item.any_node(hit)),
598            Self::UnaryMinus(expr) | Self::Not(expr) | Self::Cast { expr, .. } => {
599                expr.any_node(hit)
600            }
601            Self::Binary { lhs, rhs, .. } => lhs.any_node(hit) || rhs.any_node(hit),
602            Self::IsNull { expr, .. } | Self::InSubquery { expr, .. } => expr.any_node(hit),
603            Self::Between { expr, low, high } => {
604                expr.any_node(hit) || low.any_node(hit) || high.any_node(hit)
605            }
606            Self::InList { expr, list, .. } => {
607                expr.any_node(hit) || list.iter().any(|item| item.any_node(hit))
608            }
609            Self::Case {
610                base,
611                when,
612                else_branch,
613            } => {
614                base.as_deref().is_some_and(|base| base.any_node(hit))
615                    || when
616                        .iter()
617                        .any(|(condition, result)| condition.any_node(hit) || result.any_node(hit))
618                    || else_branch
619                        .as_deref()
620                        .is_some_and(|branch| branch.any_node(hit))
621            }
622            Self::WindowCall { .. }
623            | Self::Star
624            | Self::QualifiedStar(_)
625            | Self::Default
626            | Self::Column(_)
627            | Self::QualifiedColumn { .. }
628            | Self::InternalColumn(_)
629            | Self::Literal(_)
630            | Self::TypedLiteral { .. }
631            | Self::Param(_)
632            | Self::ScalarSubquery(_)
633            | Self::Exists { .. } => false,
634        }
635    }
636}
637
638/// Return whether `name` is a built-in aggregate understood by the planner.
639#[must_use]
640pub fn is_builtin_aggregate_function(name: &str) -> bool {
641    matches!(
642        name.to_ascii_lowercase().as_str(),
643        "count"
644            | "sum"
645            | "avg"
646            | "min"
647            | "max"
648            | "string_agg"
649            | "array_agg"
650            | "bool_and"
651            | "bool_or"
652            | "stddev"
653            | "stddev_samp"
654            | "stddev_pop"
655            | "variance"
656            | "var_samp"
657            | "var_pop"
658            | "percentile_cont"
659            | "percentile_disc"
660            | "mode"
661            | "json_agg"
662            | "jsonb_agg"
663            | "json_object_agg"
664            | "jsonb_object_agg"
665    )
666}
667
668#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
669pub enum BinaryOp {
670    Equal,
671    NotEqual,
672    Less,
673    LessEqual,
674    Greater,
675    GreaterEqual,
676    Add,
677    Subtract,
678    Multiply,
679    Divide,
680}
681
682/// `Expr` restricted to value-producing forms used by `INSERT` rows.
683pub type ValueExpr = Expr;