Skip to main content

partiql_logical/
lib.rs

1#![deny(rust_2018_idioms)]
2#![deny(clippy::all)]
3
4//! A `PartiQL` logical plan.
5//!
6//! This module contains the structures for a `PartiQL` logical plan. Three main entities in the
7//! module are [`LogicalPlan`], [`BindingsOp`], and [`ValueExpr`].
8//! `LogicalPlan` represents a graph based logical plan. `BindingsOp` represent operations that
9//! operate on binding tuples and `ValueExpr` represents `PartiQL` expressions that produce `PartiQL`
10//! values; all as specified in [PartiQL Specification 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
11//!
12//! Plan graph nodes are called _operators_ and edges are called _flows_ re-instating the fact that
13//! the plan captures data flows for a given `PartiQL` statement.
14//!
15
16mod util;
17
18pub mod graph;
19
20use ordered_float::OrderedFloat;
21use partiql_common::catalog::ObjectId;
22use partiql_value::BindingsName;
23/// # Examples
24/// ```
25/// use partiql_logical::{BinaryOp, BindingsOp, LogicalPlan, PathComponent, ProjectValue, Scan, ValueExpr, VarRefType};
26/// use partiql_value::{BindingsName, Value};
27///
28/// // Plan for `SELECT VALUE 2*v.a FROM [{'a': 1}, {'a': 2}, {'a': 3}] AS v`
29///
30/// let mut p: LogicalPlan<BindingsOp> = LogicalPlan::new();
31///
32/// let from = p.add_operator(BindingsOp::Scan(Scan {
33///     expr: ValueExpr::VarRef(BindingsName::CaseInsensitive("data".into()), VarRefType::Global),
34///     as_key: "v".to_string(),
35///     at_key: None,
36/// }));
37///
38/// let va = ValueExpr::Path(
39///     Box::new(ValueExpr::VarRef(BindingsName::CaseInsensitive(
40///         "v".into(),
41///     ), VarRefType::Local)),
42///     vec![PathComponent::Key(BindingsName::CaseInsensitive("a".to_string().into()))],
43/// );
44///
45/// let select_value = p.add_operator(BindingsOp::ProjectValue(ProjectValue {
46///     expr: ValueExpr::BinaryExpr(
47///         BinaryOp::Mul,
48///         Box::new(va),
49///         Box::new(ValueExpr::Lit(Box::new(Value::Integer(2).into()))),
50///     ),
51/// }));
52///
53/// let sink = p.add_operator(BindingsOp::Sink);
54///
55/// // Define the data flow as SCAN -> PROJECT_VALUE -> SINK
56/// p.add_flow(from, select_value);
57/// p.add_flow(select_value, sink);
58///
59/// assert_eq!(3, p.operators().len());
60/// assert_eq!(2, p.flows().len());
61/// ```
62use rust_decimal::Decimal as RustDecimal;
63use rustc_hash::FxHashMap;
64#[cfg(feature = "serde")]
65use serde::{Deserialize, Serialize};
66use std::fmt::{Debug, Display, Formatter};
67
68/// Represents a `PartiQL` logical plan.
69#[derive(Debug, Clone, Eq, PartialEq, Default)]
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71pub struct LogicalPlan<T>
72where
73    T: Default + Debug,
74{
75    nodes: Vec<T>,
76    /// Third argument indicates the branch number into the outgoing node.
77    edges: Vec<(OpId, OpId, u8)>,
78}
79
80impl<T> LogicalPlan<T>
81where
82    T: Default + Debug,
83{
84    /// Creates a new default logical plan.
85    #[must_use]
86    pub fn new() -> Self {
87        Self::default()
88    }
89
90    /// Adds a new operator to the plan.
91    pub fn add_operator(&mut self, op: T) -> OpId {
92        self.nodes.push(op);
93        OpId(self.operator_count())
94    }
95
96    /// Adds a data flow to the plan.
97    #[inline]
98    pub fn add_flow(&mut self, src: OpId, dst: OpId) {
99        assert!(src.index() <= self.operator_count());
100        assert!(dst.index() <= self.operator_count());
101
102        self.edges.push((src, dst, 0));
103    }
104
105    /// Adds a data flow with a branch number.
106    #[inline]
107    pub fn add_flow_with_branch_num(&mut self, src: OpId, dst: OpId, branch_num: u8) {
108        assert!(src.index() <= self.operator_count());
109        assert!(dst.index() <= self.operator_count());
110
111        self.edges.push((src, dst, branch_num));
112    }
113
114    /// Extends the logical plan with the given data flows.
115    /// #Examples:
116    /// ```
117    /// use partiql_logical::{BindingsOp, GroupBy, GroupingStrategy, LimitOffset, LogicalPlan, OrderBy};
118    /// let mut p: LogicalPlan<BindingsOp> = LogicalPlan::new();
119    ///
120    /// let a = p.add_operator(BindingsOp::OrderBy(OrderBy{specs: vec![]}));
121    /// let b = p.add_operator(BindingsOp::Sink);
122    /// let c = p.add_operator(BindingsOp::LimitOffset(LimitOffset{limit:None, offset:None}));
123    /// let d = p.add_operator(BindingsOp::GroupBy(GroupBy {
124    ///     strategy: GroupingStrategy::GroupFull,
125    ///     exprs: Default::default(),
126    ///     aggregate_exprs: vec![],
127    ///     group_as_alias: None,
128    /// }));
129    ///
130    /// p.add_flow(a, b);
131    ///
132    /// p.extend_with_flows(&[(a,c), (c, d)]);
133    /// assert_eq!(3, p.flows().len());
134    /// ```
135    #[inline]
136    pub fn extend_with_flows(&mut self, flows: &[(OpId, OpId)]) {
137        flows.iter().for_each(|&(s, d)| self.add_flow(s, d));
138    }
139
140    #[inline]
141    pub fn merge_plan(&mut self, other: Self) {
142        let LogicalPlan { nodes, edges } = other;
143        let mut mapping = FxHashMap::default();
144        for (old_id, op) in nodes.into_iter().enumerate().map(|(i, n)| (OpId(i + 1), n)) {
145            let new_id = self.add_operator(op);
146            mapping.insert(old_id, new_id);
147        }
148
149        for (old1, old2, branch) in edges {
150            let new1 = mapping[&old1];
151            let new2 = mapping[&old2];
152            self.edges.push((new1, new2, branch));
153        }
154    }
155
156    /// Returns the number of operators in the plan.
157    #[inline]
158    #[must_use]
159    pub fn operator_count(&self) -> usize {
160        self.nodes.len()
161    }
162
163    /// Returns the operators of the plan.
164    #[must_use]
165    pub fn operators(&self) -> &Vec<T> {
166        &self.nodes
167    }
168
169    /// Returns the operators of the plan with their `OpId`.
170    pub fn operators_by_id(&self) -> impl Iterator<Item = (OpId, &T)> {
171        self.nodes.iter().enumerate().map(|(i, n)| (OpId(i + 1), n))
172    }
173
174    /// Returns the data flows of the plan.
175    #[must_use]
176    pub fn flows(&self) -> &Vec<(OpId, OpId, u8)> {
177        &self.edges
178    }
179
180    #[must_use]
181    pub fn operator(&self, id: OpId) -> Option<&T> {
182        self.nodes.get(id.0 - 1)
183    }
184
185    pub fn operator_as_mut(&mut self, id: OpId) -> Option<&mut T> {
186        self.nodes.get_mut(id.0 - 1)
187    }
188
189    // TODO add DAG validation method.
190}
191
192/// Represents an operator identifier in a [`LogicalPlan`]
193#[derive(Debug, Clone, Eq, PartialEq, Copy, Hash)]
194#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
195pub struct OpId(usize);
196
197impl OpId {
198    /// Returns operator's index
199    #[must_use]
200    pub fn index(&self) -> usize {
201        self.0
202    }
203}
204
205impl<T> Display for LogicalPlan<T>
206where
207    T: Default + Debug,
208{
209    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
210        let flows = self.flows();
211        writeln!(f, "LogicalPlan")?;
212        writeln!(f, "---")?;
213        for (s, d, _w) in flows {
214            let src_node = self.operator(*s).expect("Unable to get the src operator");
215            let dst_node = self.operator(*d).expect("Unable to get the dst operator");
216            writeln!(f, ">>> [{src_node:?}] -> [{dst_node:?}]")?;
217        }
218        writeln!(f)
219    }
220}
221
222/// Represents `PartiQL` binding operators; A `BindingOp` is an operator that operates on
223/// binding tuples as specified by [PartiQL Specification 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
224#[derive(Debug, Clone, Default, Eq, PartialEq)]
225#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
226#[non_exhaustive]
227pub enum BindingsOp {
228    Scan(Scan),
229    Pivot(Pivot),
230    Unpivot(Unpivot),
231    Filter(Filter),
232    OrderBy(OrderBy),
233    LimitOffset(LimitOffset),
234    Join(Join),
235    BagOp(BagOp),
236    Project(Project),
237    ProjectAll(ProjectAllMode),
238    ProjectValue(ProjectValue),
239    ExprQuery(ExprQuery),
240    Distinct,
241    GroupBy(GroupBy),
242    Having(Having),
243    #[default]
244    Sink,
245}
246
247#[derive(Debug, Clone, Default, Eq, PartialEq)]
248#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
249#[non_exhaustive]
250pub enum ProjectAllMode {
251    /// Unwrap the outer alias when project *
252    ///     (e.g., for `<< {'_1': {'a': 1, 'b': 2}} >>`, 'unwrap' the `'_1'` to project `a` and `b`)
253    #[default]
254    Unwrap,
255    /// Do not unwrap an outer alias when project *
256    ///     (e.g., for `<< {'a': 1, 'b': 2} >>`, project `a` and `b`)
257    PassThrough,
258}
259
260/// [`Scan`] bridges from [`ValueExpr`]s to [`BindingsOp`]s.
261#[derive(Debug, Clone, Eq, PartialEq)]
262#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
263pub struct Scan {
264    pub expr: ValueExpr,
265    pub as_key: String,
266    pub at_key: Option<String>,
267}
268
269/// [`Pivot`] represents a PIVOT operator, e.g. `PIVOT sp.price AT sp."symbol` in
270/// `PIVOT sp.price AT sp."symbol" FROM todaysStockPrices sp`. For `Pivot` operational semantics,
271/// see section `6.2` of
272/// [PartiQL Specification — August 1, 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
273#[derive(Debug, Clone, Eq, PartialEq)]
274#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
275pub struct Pivot {
276    pub key: ValueExpr,
277    pub value: ValueExpr,
278}
279
280/// [`Unpivot`] bridges from [`ValueExpr`]s to [`BindingsOp`]s.
281#[derive(Debug, Clone, Eq, PartialEq)]
282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
283pub struct Unpivot {
284    pub expr: ValueExpr,
285    pub as_key: String,
286    pub at_key: Option<String>,
287}
288
289/// [`Filter`] represents a filter operator, e.g. `WHERE a = 10` in `SELECT a FROM t WHERE a = 10`.
290#[derive(Debug, Clone, Eq, PartialEq)]
291#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
292pub struct Filter {
293    pub expr: ValueExpr,
294}
295
296/// [`Having`] represents the having operator, e.g. `HAVING a = 10` in `SELECT b FROM t GROUP BY a, b HAVING a = 10`.
297#[derive(Debug, Clone, Eq, PartialEq)]
298#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
299pub struct Having {
300    pub expr: ValueExpr,
301}
302
303/// [`OrderBy`] represents a sort operatyion, e.g. `ORDER BY a DESC NULLS LAST` in `SELECT a FROM t ORDER BY a DESC NULLS LAST`.
304#[derive(Debug, Clone, Eq, PartialEq)]
305#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
306pub struct OrderBy {
307    pub specs: Vec<SortSpec>,
308}
309
310#[derive(Clone, Debug, PartialEq, Eq)]
311#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
312#[non_exhaustive]
313pub enum SortSpecOrder {
314    Asc,
315    Desc,
316}
317
318#[derive(Clone, Debug, PartialEq, Eq)]
319#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
320#[non_exhaustive]
321pub enum SortSpecNullOrder {
322    First,
323    Last,
324}
325
326/// Represents a `PartiQL` sort specification.
327#[derive(Debug, Clone, Eq, PartialEq)]
328#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
329pub struct SortSpec {
330    pub expr: ValueExpr,
331    pub order: SortSpecOrder,
332    pub null_order: SortSpecNullOrder,
333}
334
335/// [`LimitOffset`] represents a possible limit and/or offset operator, e.g. `LIMIT 10 OFFSET 5` in `SELECT a FROM t LIMIT 10 OFFSET 5`.
336#[derive(Debug, Clone, Eq, PartialEq)]
337#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
338pub struct LimitOffset {
339    pub limit: Option<ValueExpr>,
340    pub offset: Option<ValueExpr>,
341}
342
343/// [`BagOp`] represents a bag operator, e.g. `UNION ALL` in `SELECT a, b FROM foo UNION ALL SELECT c, d FROM bar`.
344#[derive(Debug, Clone, Eq, PartialEq)]
345#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
346pub struct BagOp {
347    pub bag_op: BagOperator,
348    pub setq: SetQuantifier,
349}
350
351/// Represents the supported bag operator types.
352#[derive(Debug, Clone, Eq, PartialEq)]
353#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
354#[non_exhaustive]
355pub enum BagOperator {
356    Union,
357    Except,
358    Intersect,
359    OuterUnion,
360    OuterExcept,
361    OuterIntersect,
362}
363
364/// ['Join`] represents a join operator, e.g. implicit `CROSS JOIN` specified by comma in `FROM`
365/// clause in `SELECT t1.a, t2.b FROM tbl1 AS t1, tbl2 AS t2`.
366#[derive(Debug, Clone, Eq, PartialEq)]
367#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
368pub struct Join {
369    pub kind: JoinKind,
370    pub left: Box<BindingsOp>,
371    pub right: Box<BindingsOp>,
372    pub on: Option<ValueExpr>,
373}
374
375/// Represents join types.
376#[derive(Debug, Clone, Eq, PartialEq)]
377#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
378#[non_exhaustive]
379pub enum JoinKind {
380    Inner,
381    Left,
382    Right,
383    Full,
384    Cross,
385}
386
387/// An SQL aggregation function call with its arguments
388#[derive(Debug, Clone, Eq, PartialEq)]
389#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
390pub struct AggregateExpression {
391    pub name: String,
392    pub expr: ValueExpr,
393    pub func: AggFunc,
394    pub setq: SetQuantifier,
395}
396
397/// SQL aggregate function
398#[derive(Debug, Clone, Eq, PartialEq)]
399#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
400#[non_exhaustive]
401pub enum AggFunc {
402    // TODO: modeling of COUNT(*)
403    /// Represents SQL's `AVG` aggregation function
404    AggAvg,
405    /// Represents SQL's `COUNT` aggregation function
406    AggCount,
407    /// Represents SQL's `MAX` aggregation function
408    AggMax,
409    /// Represents SQL's `MIN` aggregation function
410    AggMin,
411    /// Represents SQL's `SUM` aggregation function
412    AggSum,
413    /// Represents SQL's `ANY`/`SOME` aggregation function
414    AggAny,
415    /// Represents SQL's `EVERY` aggregation function
416    AggEvery,
417}
418
419/// Represents `GROUP BY` <strategy> <`group_key`>[, <`group_key`>] ... \[AS <`as_alias`>\]
420#[derive(Debug, Clone, Eq, PartialEq)]
421#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
422pub struct GroupBy {
423    pub strategy: GroupingStrategy,
424    pub exprs: FxHashMap<String, ValueExpr>,
425    pub aggregate_exprs: Vec<AggregateExpression>,
426    pub group_as_alias: Option<String>,
427}
428
429/// Grouping qualifier: ALL or PARTIAL
430#[derive(Debug, Clone, Eq, PartialEq)]
431#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
432#[non_exhaustive]
433pub enum GroupingStrategy {
434    GroupFull,
435    GroupPartial,
436}
437
438/// Represents a projection, e.g. `SELECT a` in `SELECT a FROM t`.
439#[derive(Debug, Clone, Eq, PartialEq)]
440#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
441pub struct Project {
442    pub exprs: Vec<(String, ValueExpr)>,
443}
444
445/// Represents a value projection (SELECT VALUE) e.g. `SELECT VALUE t.a * 2` in
446///`SELECT VALUE t.a * 2 IN tbl AS t`.
447#[derive(Debug, Clone, Eq, PartialEq)]
448#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
449pub struct ProjectValue {
450    pub expr: ValueExpr,
451}
452
453/// Represents an expression query e.g. `a * 2` in `a * 2` or an expression like `2+2`.
454#[derive(Debug, Clone, Eq, PartialEq)]
455#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
456pub struct ExprQuery {
457    pub expr: ValueExpr,
458}
459
460/// Represents a `PartiQL` value expression. Evaluation of a [`ValueExpr`] leads to a `PartiQL` value as
461/// specified by [PartiQL Specification 2019](https://partiql.org/assets/PartiQL-Specification.pdf).
462#[derive(Debug, Clone, Eq, PartialEq)]
463#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
464#[non_exhaustive]
465pub enum ValueExpr {
466    UnExpr(UnaryOp, Box<ValueExpr>),
467    BinaryExpr(BinaryOp, Box<ValueExpr>, Box<ValueExpr>),
468    Lit(Box<Lit>),
469    DynamicLookup(Box<Vec<ValueExpr>>),
470    Path(Box<ValueExpr>, Vec<PathComponent>),
471    VarRef(BindingsName<'static>, VarRefType),
472    TupleExpr(TupleExpr),
473    ListExpr(ListExpr),
474    BagExpr(BagExpr),
475    BetweenExpr(BetweenExpr),
476    PatternMatchExpr(PatternMatchExpr),
477    SubQueryExpr(SubQueryExpr),
478    SimpleCase(SimpleCase),
479    SearchedCase(SearchedCase),
480    IsTypeExpr(IsTypeExpr),
481    NullIfExpr(NullIfExpr),
482    CoalesceExpr(CoalesceExpr),
483    Call(CallExpr),
484    GraphMatch(Box<GraphMatchExpr>),
485}
486
487/// Represents a `PartiQL` literal value.
488#[derive(Debug, Clone, Eq, PartialEq)]
489#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
490#[non_exhaustive]
491pub enum Lit {
492    Null,
493    Missing,
494    Int8(i8),
495    Int16(i16),
496    Int32(i32),
497    Int64(i64),
498    Decimal(RustDecimal),
499    Double(OrderedFloat<f64>),
500    Bool(bool),
501    String(String),
502    Variant(Vec<u8>, String), // (bytes, type-name as string) TODO replace with strongly typed box name.
503    Struct(Vec<(String, Lit)>),
504    Bag(Vec<Lit>),
505    List(Vec<Lit>),
506}
507// TODO we should replace this enum with some identifier that can be looked up in a symtab/funcregistry?
508/// Represents logical plan's unary operators.
509#[derive(Debug, Clone, Eq, PartialEq)]
510#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
511#[non_exhaustive]
512pub enum UnaryOp {
513    Pos,
514    Neg,
515    Not,
516}
517
518// TODO we should replace this enum with some identifier that can be looked up in a symtab/funcregistry?
519/// Represents logical plan's binary operators.
520#[derive(Debug, Clone, Eq, PartialEq)]
521#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
522#[non_exhaustive]
523pub enum BinaryOp {
524    And,
525    Or,
526    Concat,
527    Eq,
528    Neq,
529    Gt,
530    Gteq,
531    Lt,
532    Lteq,
533
534    // Arithmetic ops
535    Add,
536    Sub,
537    Mul,
538    Div,
539    Mod,
540    Exp,
541
542    In,
543}
544
545#[derive(Debug, Clone, Eq, PartialEq)]
546#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
547/// Represents a path component in a plan.
548#[non_exhaustive]
549pub enum PathComponent {
550    /// E.g. `b` in `a.b`
551    Key(BindingsName<'static>),
552    /// E.g. 4 in `a[4]`
553    Index(i64),
554    KeyExpr(Box<ValueExpr>),
555    IndexExpr(Box<ValueExpr>),
556}
557
558/// Represents a `PartiQL` tuple expression, e.g: `{ a.b: a.c * 2, 'count': a.c + 10}`.
559#[derive(Clone, Debug, Default, Eq, PartialEq)]
560#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
561pub struct TupleExpr {
562    pub attrs: Vec<ValueExpr>,
563    pub values: Vec<ValueExpr>,
564}
565
566impl TupleExpr {
567    /// Creates a new default [`TupleExpr`].
568    #[must_use]
569    pub fn new() -> Self {
570        Self::default()
571    }
572}
573
574/// Represents a `PartiQL` list expression, e.g. `[a.c * 2, 5]`.
575#[derive(Clone, Debug, Default, Eq, PartialEq)]
576#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
577pub struct ListExpr {
578    pub elements: Vec<ValueExpr>,
579}
580
581impl ListExpr {
582    /// Creates a new default [`ListExpr`].
583    #[must_use]
584    pub fn new() -> Self {
585        Self::default()
586    }
587}
588
589/// Represents a `PartiQL` bag expression, e.g. `<<a.c * 2, 5>>`.
590#[derive(Clone, Debug, Default, Eq, PartialEq)]
591#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
592pub struct BagExpr {
593    pub elements: Vec<ValueExpr>,
594}
595
596impl BagExpr {
597    /// Creates a new default [`BagExpr`].
598    #[must_use]
599    pub fn new() -> Self {
600        Self::default()
601    }
602}
603
604/// Represents a `PartiQL` `BETWEEN` expression, e.g. `BETWEEN 500 AND 600`.
605#[derive(Debug, Clone, Eq, PartialEq)]
606#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
607pub struct BetweenExpr {
608    pub value: Box<ValueExpr>,
609    pub from: Box<ValueExpr>,
610    pub to: Box<ValueExpr>,
611}
612
613/// Represents a `PartiQL` Pattern Match expression, e.g. `'foo' LIKE 'foo'`.
614#[derive(Debug, Clone, Eq, PartialEq)]
615#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
616pub struct PatternMatchExpr {
617    pub value: Box<ValueExpr>,
618    pub pattern: Pattern,
619}
620
621#[derive(Debug, Clone, Eq, PartialEq)]
622#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
623#[non_exhaustive]
624pub enum Pattern {
625    Like(LikeMatch), // TODO other e.g., SIMILAR_TO, or regex match
626    LikeNonStringNonLiteral(LikeNonStringNonLiteralMatch),
627}
628
629/// Represents a LIKE expression where both the `pattern` and `escape` are string literals,
630/// e.g. `'foo%' ESCAPE '/'`
631#[derive(Debug, Clone, Eq, PartialEq)]
632#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
633pub struct LikeMatch {
634    pub pattern: String,
635    pub escape: String,
636}
637
638/// Represents a LIKE expression where one of `pattern` and `escape` is not a string literal,
639/// e.g. `some_pattern ESCAPE '/'`
640#[derive(Debug, Clone, Eq, PartialEq)]
641#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
642pub struct LikeNonStringNonLiteralMatch {
643    pub pattern: Box<ValueExpr>,
644    pub escape: Box<ValueExpr>,
645}
646
647/// Represents a `PartiQL` GPML expression, e.g. `graph MATCH (node1:NodeLabel) -> (node2:OtherLabel)`.
648#[derive(Debug, Clone, Eq, PartialEq)]
649#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
650pub struct GraphMatchExpr {
651    pub value: Box<ValueExpr>,
652    pub pattern: graph::PathPatternMatch,
653}
654#[derive(Debug, Clone, Eq, PartialEq)]
655#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
656pub struct GraphPathPattern {}
657
658/// Represents a sub-query expression, e.g. `SELECT v.a*2 AS u FROM t AS v` in
659/// `SELECT t.a, s FROM data AS t, (SELECT v.a*2 AS u FROM t AS v) AS s`
660#[derive(Debug, Clone, Eq, PartialEq)]
661#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
662pub struct SubQueryExpr {
663    pub plan: LogicalPlan<BindingsOp>,
664}
665
666/// Represents a `PartiQL`'s simple case expressions,
667/// e.g.`CASE <expr> [ WHEN <expr> THEN <expr> ]... [ ELSE <expr> ] END`.
668#[derive(Debug, Clone, Eq, PartialEq)]
669#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
670pub struct SimpleCase {
671    pub expr: Box<ValueExpr>,
672    pub cases: Vec<(Box<ValueExpr>, Box<ValueExpr>)>,
673    pub default: Option<Box<ValueExpr>>,
674}
675
676/// Represents a `PartiQL`'s searched case expressions,
677/// e.g.`CASE [ WHEN <expr> THEN <expr> ]... [ ELSE <expr> ] END`.
678#[derive(Debug, Clone, Eq, PartialEq)]
679#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
680pub struct SearchedCase {
681    pub cases: Vec<(Box<ValueExpr>, Box<ValueExpr>)>,
682    pub default: Option<Box<ValueExpr>>,
683}
684
685/// Represents an `IS` expression, e.g. `IS TRUE`.
686#[derive(Debug, Clone, Eq, PartialEq)]
687#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
688pub struct IsTypeExpr {
689    pub not: bool,
690    pub expr: Box<ValueExpr>,
691    pub is_type: Type,
692}
693
694/// Represents a `PartiQL` Type.
695#[derive(Clone, Debug, PartialEq, Eq)]
696#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
697#[non_exhaustive]
698pub enum Type {
699    NullType,
700    BooleanType,
701    Integer2Type,
702    Integer4Type,
703    Integer8Type,
704    DecimalType,
705    NumericType,
706    RealType,
707    DoublePrecisionType,
708    TimestampType,
709    CharacterType,
710    CharacterVaryingType,
711    MissingType,
712    StringType,
713    SymbolType,
714    BlobType,
715    ClobType,
716    DateType,
717    TimeType,
718    ZonedTimestampType,
719    StructType,
720    TupleType,
721    ListType,
722    SexpType,
723    BagType,
724    AnyType,
725    // TODO CustomType
726}
727
728/// Represents a `NULLIF` expression, e.g. `NULLIF(v1, v2)` in `SELECT NULLIF(v1, v2) FROM data`.
729#[derive(Debug, Clone, Eq, PartialEq)]
730#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
731pub struct NullIfExpr {
732    pub lhs: Box<ValueExpr>,
733    pub rhs: Box<ValueExpr>,
734}
735
736/// Represents a `COALESCE` expression, e.g.
737/// `COALESCE(NULL, 10)` in `SELECT COALESCE(NULL, 10) FROM data`.
738#[derive(Debug, Clone, Eq, PartialEq)]
739#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
740pub struct CoalesceExpr {
741    pub elements: Vec<ValueExpr>,
742}
743
744/// Represents a `CALL` expression (i.e., a function call), e.g. `LOWER("ALL CAPS")`.
745#[derive(Debug, Clone, Eq, PartialEq)]
746#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
747pub struct CallExpr {
748    pub name: CallName,
749    pub arguments: Vec<ValueExpr>,
750}
751
752/// Represents a known function.
753#[derive(Debug, Clone, Eq, PartialEq)]
754#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
755#[non_exhaustive]
756pub enum CallName {
757    Lower,
758    Upper,
759    CharLength,
760    OctetLength,
761    BitLength,
762    LTrim,
763    BTrim,
764    RTrim,
765    Substring,
766    Position,
767    Overlay,
768    Exists,
769    Abs,
770    Mod,
771    Cardinality,
772    CollToScalar,
773    ExtractYear,
774    ExtractMonth,
775    ExtractDay,
776    ExtractHour,
777    ExtractMinute,
778    ExtractSecond,
779    ExtractTimezoneHour,
780    ExtractTimezoneMinute,
781    CollAvg(SetQuantifier),
782    CollCount(SetQuantifier),
783    CollMax(SetQuantifier),
784    CollMin(SetQuantifier),
785    CollSum(SetQuantifier),
786    CollAny(SetQuantifier),
787    CollEvery(SetQuantifier),
788    ByName(String),
789    ById(String, ObjectId, usize),
790}
791
792/// Indicates if a set should be reduced to its distinct elements or not.
793///
794/// Consumers must account for future variants:
795///
796/// ```
797/// use partiql_logical::SetQuantifier;
798///
799/// fn is_distinct(quantifier: &SetQuantifier) -> Option<bool> {
800///     match quantifier {
801///         SetQuantifier::All => Some(false),
802///         SetQuantifier::Distinct => Some(true),
803///         _ => None, // An unsupported quantifier.
804///     }
805/// }
806///
807/// assert_eq!(is_distinct(&SetQuantifier::All), Some(false));
808/// assert_eq!(is_distinct(&SetQuantifier::Distinct), Some(true));
809/// ```
810///
811/// Listing only today's variants is not exhaustive outside this crate:
812///
813/// ```compile_fail
814/// use partiql_logical::SetQuantifier;
815///
816/// fn is_distinct(quantifier: &SetQuantifier) -> bool {
817///     match quantifier {
818///         SetQuantifier::All => false,
819///         SetQuantifier::Distinct => true,
820///     }
821/// }
822/// ```
823#[derive(Debug, Clone, Eq, PartialEq)]
824#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
825#[non_exhaustive]
826pub enum SetQuantifier {
827    All,
828    Distinct,
829}
830
831/// Indicates whether to look in the local/lexical or global environment when resolving a variable.
832#[derive(Clone, Debug, PartialEq, Eq)]
833#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
834#[non_exhaustive]
835pub enum VarRefType {
836    /// Resolve the variable by looking in the global environment.
837    Global,
838    /// Resolve the variable by looking in the local/lexical scope environment.
839    Local,
840}
841
842#[cfg(test)]
843mod tests {
844    use super::*;
845
846    #[test]
847    fn test_plan() {
848        let mut p: LogicalPlan<BindingsOp> = LogicalPlan::new();
849        let a = p.add_operator(BindingsOp::OrderBy(OrderBy { specs: vec![] }));
850        let b = p.add_operator(BindingsOp::Sink);
851        let c = p.add_operator(BindingsOp::LimitOffset(LimitOffset {
852            limit: None,
853            offset: None,
854        }));
855        let d = p.add_operator(BindingsOp::GroupBy(GroupBy {
856            strategy: GroupingStrategy::GroupFull,
857            exprs: Default::default(),
858            aggregate_exprs: vec![],
859            group_as_alias: None,
860        }));
861        p.add_flow(a, b);
862        p.add_flow(a, c);
863        p.extend_with_flows(&[(c, d), (b, c)]);
864        assert_eq!(4, p.operators().len());
865        assert_eq!(4, p.flows().len());
866    }
867}