Skip to main content

partiql_logical_planner/
lower.rs

1use fnv::FnvBuildHasher;
2use indexmap::IndexMap;
3use num::Integer;
4use ordered_float::OrderedFloat;
5use partiql_ast::ast;
6use partiql_ast::ast::{
7    Assignment, Bag, BagOpExpr, BagOperator, Between, BinOp, BinOpKind, Call, CallAgg, CallArg,
8    CallArgNamed, CaseSensitivity, CreateIndex, CreateTable, Ddl, DdlOp, Delete, Dml, DmlOp,
9    DropIndex, DropTable, Exclusion, Expr, FromClause, FromLet, FromLetKind, GroupByExpr, GroupKey,
10    GroupingStrategy, Insert, InsertValue, Item, Join, JoinKind, JoinSpec, Like, List, Lit,
11    NullOrderingSpec, OnConflict, OrderByExpr, OrderingSpec, Path, PathStep, ProjectExpr,
12    Projection, ProjectionKind, Query, QuerySet, Remove, SearchedCase, Select, Set, SetQuantifier,
13    SimpleCase, SortSpec, Struct, SymbolPrimitive, UniOp, UniOpKind, VarRef,
14};
15use partiql_ast::visit::{Traverse, Visit, Visitor};
16use partiql_logical as logical;
17use partiql_logical::{
18    AggregateExpression, BagExpr, BagOp, BetweenExpr, BindingsOp, GraphMatchExpr, IsTypeExpr,
19    LikeMatch, LikeNonStringNonLiteralMatch, ListExpr, LogicalPlan, OpId, PathComponent, Pattern,
20    PatternMatchExpr, ProjectAllMode, SortSpecOrder, TupleExpr, ValueExpr, VarRefType,
21};
22use std::borrow::Cow;
23
24use partiql_value::BindingsName;
25
26use crate::builtins::{FnSymTab, FN_SYM_TAB};
27use itertools::Itertools;
28use partiql_ast_passes::name_resolver;
29use partiql_catalog::call_defs::{CallArgument, CallDef};
30
31use partiql_ast_passes::error::{AstTransformError, AstTransformationError};
32
33use crate::functions::Function;
34use partiql_ast_passes::name_resolver::NameRef;
35use partiql_catalog::catalog::SharedCatalog;
36use partiql_common::node::{IdAnnotated, NodeId};
37
38use partiql_logical::AggFunc::{AggAny, AggAvg, AggCount, AggEvery, AggMax, AggMin, AggSum};
39use partiql_logical::ValueExpr::DynamicLookup;
40use rustc_hash::{FxBuildHasher, FxHashMap};
41use std::sync::atomic::{AtomicU32, Ordering};
42
43type FnvIndexMap<K, V> = IndexMap<K, V, FnvBuildHasher>;
44
45#[macro_export]
46macro_rules! eq_or_fault {
47    ($self:ident, $lhs:expr, $rhs:expr, $msg:expr) => {
48        if $lhs != $rhs {
49            $self
50                .errors
51                .push(AstTransformError::IllegalState($msg.to_string()));
52            return partiql_ast::visit::Traverse::Stop;
53        }
54    };
55}
56
57#[macro_export]
58macro_rules! true_or_fault {
59    ($self:ident, $expr:expr, $msg:expr) => {
60        if !$expr {
61            true_or_fault_err!($self, $expr, $msg);
62            return partiql_ast::visit::Traverse::Stop;
63        }
64    };
65}
66
67#[macro_export]
68macro_rules! true_or_fault_err {
69    ($self:ident, $expr:expr, $msg:expr) => {
70        if !$expr {
71            $self
72                .errors
73                .push(AstTransformError::IllegalState($msg.to_string()));
74        }
75    };
76}
77
78#[macro_export]
79macro_rules! not_yet_implemented_fault {
80    ($self:ident, $msg:expr) => {
81        not_yet_implemented_err!($self, $msg);
82        return partiql_ast::visit::Traverse::Stop;
83    };
84}
85
86#[macro_export]
87macro_rules! not_yet_implemented_err {
88    ($self:ident, $msg:expr) => {
89        $self
90            .errors
91            .push(AstTransformError::NotYetImplemented($msg.to_string()));
92    };
93}
94
95pub(crate) fn extract_vexpr_by_id(
96    v: &mut Vec<(NodeId, ValueExpr)>,
97    target: NodeId,
98) -> Option<ValueExpr> {
99    let position: usize = v.iter().position(|(id, _v)| *id == target)?;
100    Some(v.remove(position).1)
101}
102
103#[derive(Copy, Clone, Debug)]
104enum QueryContext {
105    FromLet,
106    Path,
107    Order,
108    Query,
109}
110
111#[derive(Clone, Debug, Default)]
112struct QueryClauses {
113    from_clause: Option<logical::OpId>,
114    let_clause: Option<logical::OpId>,
115    where_clause: Option<logical::OpId>,
116    group_by_clause: Option<logical::OpId>,
117    having_clause: Option<logical::OpId>,
118    order_by_clause: Option<logical::OpId>,
119    limit_offset_clause: Option<logical::OpId>,
120    select_clause: Option<logical::OpId>,
121    distinct: Option<logical::OpId>,
122}
123
124impl QueryClauses {
125    pub fn evaluation_order(&self) -> Vec<OpId> {
126        [
127            self.from_clause,
128            self.let_clause,
129            self.where_clause,
130            self.group_by_clause,
131            self.having_clause,
132            self.order_by_clause,
133            self.limit_offset_clause,
134            self.select_clause,
135            self.distinct,
136        ]
137        .iter()
138        .copied()
139        .flatten()
140        .collect()
141    }
142}
143
144#[derive(Debug)]
145struct IdGenerator {
146    next_id: AtomicU32,
147}
148
149impl Default for IdGenerator {
150    fn default() -> Self {
151        Self {
152            next_id: AtomicU32::new(1),
153        }
154    }
155}
156
157impl IdGenerator {
158    fn id(&self) -> String {
159        format!("_{}", self.next_id())
160    }
161
162    fn next_id(&self) -> u32 {
163        self.next_id.fetch_add(1, Ordering::SeqCst)
164    }
165}
166
167#[derive(Debug)]
168pub struct AstToLogical<'a> {
169    // current stack of node ids
170    id_stack: Vec<NodeId>,
171
172    q_stack: Vec<QueryClauses>,
173    ctx_stack: Vec<QueryContext>,
174    bexpr_stack: Vec<Vec<logical::OpId>>,
175    vexpr_stack: Vec<Vec<(NodeId, ValueExpr)>>,
176    arg_stack: Vec<Vec<CallArgument>>,
177    path_stack: Vec<Vec<PathComponent>>,
178    sort_stack: Vec<Vec<logical::SortSpec>>,
179    aggregate_exprs: Vec<Vec<AggregateExpression>>,
180    projection_renames: Vec<FnvIndexMap<String, BindingsName<'a>>>,
181
182    aliases: FnvIndexMap<NodeId, SymbolPrimitive>,
183
184    // generator of 'fresh' ids
185    id: IdGenerator,
186    agg_id: IdGenerator,
187
188    // output
189    plan_stack: Vec<LogicalPlan<BindingsOp>>,
190
191    // catalog & data flow data
192    key_registry: name_resolver::KeyRegistry,
193    fnsym_tab: &'static FnSymTab,
194    catalog: &'a dyn SharedCatalog,
195
196    // list of errors encountered during AST lowering
197    errors: Vec<AstTransformError>,
198}
199
200/// Attempt to infer an alias for a simple variable reference expression.
201/// For example infer such that  `SELECT a, b.c.d.e ...` <=> `SELECT a as a, b.c.d.e as e`
202fn infer_id(expr: &ValueExpr) -> Option<SymbolPrimitive> {
203    let sensitive = |value: &str| {
204        Some(SymbolPrimitive {
205            value: value.to_string(),
206            case: CaseSensitivity::CaseSensitive,
207        })
208    };
209    let insensitive = |value: &str| {
210        Some(SymbolPrimitive {
211            value: value.to_string(),
212            case: CaseSensitivity::CaseInsensitive,
213        })
214    };
215
216    match expr {
217        ValueExpr::VarRef(BindingsName::CaseInsensitive(s), _) => insensitive(s.as_ref()),
218        ValueExpr::VarRef(BindingsName::CaseSensitive(s), _) => sensitive(s.as_ref()),
219        ValueExpr::Path(_root, steps) => match steps.last() {
220            Some(PathComponent::Key(BindingsName::CaseInsensitive(s))) => insensitive(s.as_ref()),
221            Some(PathComponent::Key(BindingsName::CaseSensitive(s))) => sensitive(s.as_ref()),
222            Some(PathComponent::KeyExpr(ke)) => match &**ke {
223                ValueExpr::VarRef(BindingsName::CaseInsensitive(s), _) => insensitive(s.as_ref()),
224                ValueExpr::VarRef(BindingsName::CaseSensitive(s), _) => sensitive(s.as_ref()),
225                _ => None,
226            },
227            _ => None,
228        },
229        ValueExpr::DynamicLookup(d) => infer_id(d.first().unwrap()),
230        _ => None,
231    }
232}
233
234impl<'a> AstToLogical<'a> {
235    pub fn new(catalog: &'a dyn SharedCatalog, registry: name_resolver::KeyRegistry) -> Self {
236        let fnsym_tab: &FnSymTab = &FN_SYM_TAB;
237        AstToLogical {
238            id_stack: Default::default(),
239
240            q_stack: Default::default(),
241            ctx_stack: Default::default(),
242            bexpr_stack: Default::default(),
243            vexpr_stack: Default::default(),
244            arg_stack: Default::default(),
245            path_stack: Default::default(),
246            sort_stack: Default::default(),
247            aggregate_exprs: Default::default(),
248
249            projection_renames: Default::default(),
250
251            aliases: Default::default(),
252
253            // generator of 'fresh' ids
254            id: Default::default(),
255            agg_id: Default::default(),
256
257            // output
258            plan_stack: Default::default(),
259
260            key_registry: registry,
261            fnsym_tab,
262            catalog,
263
264            errors: vec![],
265        }
266    }
267
268    pub fn lower_query(
269        mut self,
270        query: &ast::AstNode<ast::TopLevelQuery>,
271    ) -> Result<logical::LogicalPlan<logical::BindingsOp>, AstTransformationError> {
272        self.enter_plan();
273        query.visit(&mut self);
274        true_or_fault_err!(
275            self,
276            self.plan_stack.len() == 1,
277            "self.plan_stack.len() != 1"
278        );
279        if !self.errors.is_empty() {
280            return Err(AstTransformationError {
281                errors: self.errors,
282            });
283        }
284        Ok(self.plan_stack.pop().unwrap())
285    }
286
287    #[inline]
288    fn current_node(&self) -> &NodeId {
289        self.id_stack.last().unwrap()
290    }
291
292    #[inline]
293    fn gen_id(&self) -> SymbolPrimitive {
294        // TODO assure non-collision with provided identifiers. e.g., we shouldn't generate `_1` if the query contains `AS _1`
295        SymbolPrimitive {
296            value: self.id.id(),
297            case: CaseSensitivity::CaseInsensitive,
298        }
299    }
300
301    #[inline]
302    fn infer_id(&self, expr: &ValueExpr, as_alias: &Option<SymbolPrimitive>) -> SymbolPrimitive {
303        as_alias
304            .to_owned()
305            .or_else(|| infer_id(expr))
306            .unwrap_or_else(|| self.gen_id())
307    }
308
309    fn resolve_varref(&self, varref: &ast::VarRef) -> logical::ValueExpr {
310        fn binding_to_static<'a>(binding: &'a BindingsName<'a>) -> BindingsName<'static> {
311            match binding {
312                BindingsName::CaseSensitive(n) => {
313                    BindingsName::CaseSensitive(Cow::Owned(n.as_ref().to_string()))
314                }
315                BindingsName::CaseInsensitive(n) => {
316                    BindingsName::CaseInsensitive(Cow::Owned(n.as_ref().to_string()))
317                }
318            }
319        }
320
321        // Convert a `SymbolPrimitive` into a `BindingsName`
322        fn symprim_to_binding(sym: &SymbolPrimitive) -> BindingsName<'static> {
323            match sym.case {
324                CaseSensitivity::CaseSensitive => {
325                    BindingsName::CaseSensitive(Cow::Owned(sym.value.clone()))
326                }
327                CaseSensitivity::CaseInsensitive => {
328                    BindingsName::CaseInsensitive(Cow::Owned(sym.value.clone()))
329                }
330            }
331        }
332        // Convert a `name_resolver::Symbol` into a `BindingsName`
333        fn sym_to_binding(sym: &name_resolver::Symbol) -> Option<BindingsName<'static>> {
334            match sym {
335                name_resolver::Symbol::Known(sym) => Some(symprim_to_binding(sym)),
336                name_resolver::Symbol::Unknown(_) => None,
337            }
338        }
339
340        for id in self.id_stack.iter().rev() {
341            if let Some(key_schema) = self.key_registry.schema.get(id) {
342                let key_schema: &name_resolver::KeySchema = key_schema;
343
344                let name_ref: &NameRef = key_schema
345                    .consume
346                    .iter()
347                    .find(|name_ref| name_ref.sym == varref.name)
348                    .expect("NameRef");
349
350                let var_binding = symprim_to_binding(&name_ref.sym);
351                let mut lookups = vec![];
352
353                if matches!(self.current_ctx(), Some(QueryContext::Order)) {
354                    if let Some(renames) = self.projection_renames.last() {
355                        let binding = renames
356                            .iter()
357                            .find(|(k, _)| {
358                                let SymbolPrimitive { value, case } = &name_ref.sym;
359                                match case {
360                                    CaseSensitivity::CaseSensitive => value == *k,
361                                    CaseSensitivity::CaseInsensitive => unicase::eq(value, *k),
362                                }
363                            })
364                            .map_or_else(
365                                || symprim_to_binding(&name_ref.sym),
366                                |(_k, v)| binding_to_static(v),
367                            );
368
369                        lookups.push(DynamicLookup(Box::new(vec![ValueExpr::VarRef(
370                            binding,
371                            VarRefType::Local,
372                        )])));
373                    }
374                }
375
376                for lookup in &name_ref.lookup {
377                    match lookup {
378                        name_resolver::NameLookup::Global => {
379                            let var_ref_expr =
380                                ValueExpr::VarRef(var_binding.clone(), VarRefType::Global);
381                            if !lookups.contains(&var_ref_expr) {
382                                lookups.push(var_ref_expr.clone());
383                            }
384                        }
385                        name_resolver::NameLookup::Local => {
386                            if let Some(scope_ids) = self.key_registry.in_scope.get(id) {
387                                let scopes: Vec<&name_resolver::KeySchema> = scope_ids
388                                    .iter()
389                                    .filter_map(|scope_id| self.key_registry.schema.get(scope_id))
390                                    .collect();
391
392                                let mut exact = scopes.iter().filter(|key_schema| {
393                                    key_schema.produce.contains(&name_resolver::Symbol::Known(
394                                        name_ref.sym.clone(),
395                                    ))
396                                });
397
398                                if let Some(_matching) = exact.next() {
399                                    let var_ref_expr =
400                                        ValueExpr::VarRef(var_binding.clone(), VarRefType::Local);
401                                    lookups.push(var_ref_expr);
402                                    continue;
403                                }
404
405                                for schema in scopes {
406                                    for produce in &schema.produce {
407                                        if let name_resolver::Symbol::Known(sym) = produce {
408                                            if (sym == &varref.name)
409                                                || (sym.value.to_lowercase()
410                                                    == varref.name.value.to_lowercase()
411                                                    && varref.name.case
412                                                        == ast::CaseSensitivity::CaseInsensitive)
413                                            {
414                                                let expr = ValueExpr::VarRef(
415                                                    sym_to_binding(produce).unwrap_or_else(|| {
416                                                        symprim_to_binding(&self.gen_id())
417                                                    }),
418                                                    VarRefType::Local,
419                                                );
420                                                if !lookups.contains(&expr) {
421                                                    lookups.push(expr);
422                                                }
423
424                                                continue;
425                                            } else if let Some(_type_entry) = self
426                                                .catalog
427                                                .resolve_type(name_ref.sym.value.as_ref())
428                                            {
429                                                let expr = ValueExpr::VarRef(
430                                                    var_binding.clone(),
431                                                    VarRefType::Global,
432                                                );
433                                                if !lookups.contains(&expr) {
434                                                    lookups.push(expr);
435                                                }
436                                                continue;
437                                            } else {
438                                                let path = logical::ValueExpr::Path(
439                                                    Box::new(ValueExpr::VarRef(
440                                                        sym_to_binding(produce).unwrap_or_else(
441                                                            || symprim_to_binding(&self.gen_id()),
442                                                        ),
443                                                        VarRefType::Local,
444                                                    )),
445                                                    vec![PathComponent::Key(var_binding.clone())],
446                                                );
447
448                                                if !lookups.contains(&path) {
449                                                    lookups.push(path);
450                                                }
451                                            }
452                                        } else if let name_resolver::Symbol::Unknown(num) = produce
453                                        {
454                                            let formatted_num = format!("_{num}");
455                                            if formatted_num == varref.name.value {
456                                                let expr = ValueExpr::VarRef(
457                                                    BindingsName::CaseInsensitive(Cow::Owned(
458                                                        formatted_num,
459                                                    )),
460                                                    VarRefType::Local,
461                                                );
462                                                if !lookups.contains(&expr) {
463                                                    lookups.push(expr);
464                                                    continue;
465                                                }
466                                            } else {
467                                                let path = logical::ValueExpr::Path(
468                                                    Box::new(ValueExpr::VarRef(
469                                                        sym_to_binding(produce).unwrap_or({
470                                                            BindingsName::CaseInsensitive(
471                                                                Cow::Owned(formatted_num),
472                                                            )
473                                                        }),
474                                                        VarRefType::Local,
475                                                    )),
476                                                    vec![PathComponent::Key(var_binding.clone())],
477                                                );
478
479                                                if !lookups.contains(&path) {
480                                                    lookups.push(path);
481                                                }
482                                            }
483                                        }
484                                    }
485                                }
486                            }
487                        }
488                    }
489                }
490                return ValueExpr::DynamicLookup(Box::new(lookups));
491            }
492        }
493
494        // TODO in the presence of schema, error if the variable reference doesn't correspond to a data table
495
496        // assume global
497        ValueExpr::VarRef(symprim_to_binding(&varref.name), VarRefType::Global)
498    }
499
500    #[inline]
501    fn enter_q(&mut self) {
502        self.q_stack.push(Default::default());
503        self.aggregate_exprs.push(Default::default());
504        self.ctx_stack.push(QueryContext::Query);
505        self.projection_renames.push(Default::default());
506    }
507
508    #[inline]
509    fn exit_q(&mut self) -> QueryClauses {
510        self.projection_renames.pop().expect("q level");
511        self.ctx_stack.pop().expect("q level");
512        self.aggregate_exprs.pop().expect("q level");
513        self.q_stack.pop().expect("q level")
514    }
515
516    #[inline]
517    fn current_ctx(&self) -> Option<&QueryContext> {
518        self.ctx_stack.last()
519    }
520
521    #[inline]
522    fn current_ctx_mut(&mut self) -> &mut QueryContext {
523        self.ctx_stack.last_mut().unwrap()
524    }
525
526    #[inline]
527    fn current_clauses_mut(&mut self) -> &mut QueryClauses {
528        self.q_stack.last_mut().unwrap()
529    }
530
531    #[inline]
532    fn enter_plan(&mut self) {
533        self.plan_stack.push(LogicalPlan::default());
534    }
535
536    #[inline]
537    fn exit_plan(&mut self) -> LogicalPlan<BindingsOp> {
538        self.plan_stack.pop().expect("environment level")
539    }
540
541    #[inline]
542    fn curr_plan(&mut self) -> &mut LogicalPlan<BindingsOp> {
543        self.plan_stack.last_mut().expect("plan")
544    }
545
546    #[inline]
547    fn enter_benv(&mut self) {
548        self.bexpr_stack.push(vec![]);
549    }
550
551    #[inline]
552    fn exit_benv(&mut self) -> Vec<logical::OpId> {
553        self.bexpr_stack.pop().expect("bexpr level")
554    }
555
556    #[inline]
557    fn push_bexpr(&mut self, bexpr: logical::OpId) {
558        self.bexpr_stack.last_mut().unwrap().push(bexpr);
559    }
560
561    #[inline]
562    fn enter_env(&mut self) {
563        self.vexpr_stack.push(vec![]);
564    }
565
566    #[inline]
567    fn exit_env(&mut self) -> Vec<(NodeId, ValueExpr)> {
568        self.vexpr_stack.pop().expect("environment level")
569    }
570
571    #[inline]
572    fn push_vexpr(&mut self, vexpr: ValueExpr) {
573        let id = self.id_stack.last().unwrap();
574        self.vexpr_stack.last_mut().unwrap().push((*id, vexpr));
575    }
576
577    #[inline]
578    fn push_lit(&mut self, lit: logical::Lit) {
579        self.push_vexpr(ValueExpr::Lit(Box::new(lit)));
580    }
581
582    #[inline]
583    fn enter_call(&mut self) {
584        self.arg_stack.push(vec![]);
585    }
586
587    #[inline]
588    fn exit_call(&mut self) -> Vec<CallArgument> {
589        self.arg_stack.pop().expect("environment level")
590    }
591
592    #[inline]
593    fn push_call_arg(&mut self, arg: CallArgument) {
594        self.arg_stack.last_mut().unwrap().push(arg);
595    }
596
597    #[inline]
598    fn enter_path(&mut self) {
599        self.path_stack.push(vec![]);
600        self.ctx_stack.push(QueryContext::Query);
601    }
602
603    #[inline]
604    fn exit_path(&mut self) -> Vec<PathComponent> {
605        self.ctx_stack.pop();
606        self.path_stack.pop().expect("path level")
607    }
608
609    #[inline]
610    fn push_path_step(&mut self, step: PathComponent) {
611        self.path_stack.last_mut().unwrap().push(step);
612    }
613
614    #[inline]
615    fn enter_sort(&mut self) {
616        self.sort_stack.push(vec![]);
617        self.ctx_stack.push(QueryContext::Order);
618    }
619
620    #[inline]
621    fn exit_sort(&mut self) -> Vec<logical::SortSpec> {
622        self.ctx_stack.pop();
623        self.sort_stack.pop().expect("sort specs")
624    }
625
626    #[inline]
627    fn push_sort_spec(&mut self, spec: logical::SortSpec) {
628        self.sort_stack.last_mut().unwrap().push(spec);
629    }
630}
631
632// SQL (and therefore PartiQL) text (and therefore AST) is not lexically-scoped as is the
633// case with most programming languages with which we are familiar.
634//
635// In order to properly process expressions (especially the name references), this visitor essentially
636// processes the AST in a kind of post-order traversal, where most node processing is performed after
637// that node's children. The `exit_<x>` calls correspond to the post-order processing.
638// Often it is necessary to do a little bit of work (preparing data structures into which children
639// will collect) in the `enter_<x>` methods. The children of the node are visited in between the
640// `enter_<x>` and `exit_<x>` calls.
641//
642// For 'leaf' nodes of the tree (e.g. variable references, etc.), the node doesn't have any children,
643// so there is nothing done between the `enter_<x>` and `exit_<x>` calls.
644// By convention, processing for them is done in the `enter_<x>` calls here.
645//
646impl<'ast> Visitor<'ast> for AstToLogical<'_> {
647    fn enter_ast_node(&mut self, id: NodeId) -> Traverse {
648        self.id_stack.push(id);
649        Traverse::Continue
650    }
651    fn exit_ast_node(&mut self, id: NodeId) -> Traverse {
652        let cur_node = self.id_stack.pop();
653        eq_or_fault!(self, cur_node, Some(id), "id_stack node id != id");
654        Traverse::Continue
655    }
656
657    fn enter_item(&mut self, _item: &'ast Item) -> Traverse {
658        not_yet_implemented_fault!(self, "Item");
659    }
660
661    fn enter_ddl(&mut self, _ddl: &'ast Ddl) -> Traverse {
662        not_yet_implemented_fault!(self, "Ddl".to_string());
663    }
664
665    fn enter_ddl_op(&mut self, _ddl_op: &'ast DdlOp) -> Traverse {
666        not_yet_implemented_fault!(self, "DdlOp".to_string());
667    }
668
669    fn enter_create_table(&mut self, _create_table: &'ast CreateTable) -> Traverse {
670        not_yet_implemented_fault!(self, "CreateTable".to_string());
671    }
672
673    fn enter_drop_table(&mut self, _drop_table: &'ast DropTable) -> Traverse {
674        not_yet_implemented_fault!(self, "DropTable".to_string());
675    }
676
677    fn enter_create_index(&mut self, _create_index: &'ast CreateIndex) -> Traverse {
678        not_yet_implemented_fault!(self, "CreateIndex".to_string());
679    }
680
681    fn enter_drop_index(&mut self, _drop_index: &'ast DropIndex) -> Traverse {
682        not_yet_implemented_fault!(self, "DropIndex".to_string());
683    }
684
685    fn enter_dml(&mut self, _dml: &'ast Dml) -> Traverse {
686        not_yet_implemented_fault!(self, "Dml".to_string());
687    }
688
689    fn enter_dml_op(&mut self, _dml_op: &'ast DmlOp) -> Traverse {
690        not_yet_implemented_fault!(self, "DmlOp".to_string());
691    }
692
693    fn enter_insert(&mut self, _insert: &'ast Insert) -> Traverse {
694        not_yet_implemented_fault!(self, "Insert".to_string());
695    }
696
697    fn enter_insert_value(&mut self, _insert_value: &'ast InsertValue) -> Traverse {
698        not_yet_implemented_fault!(self, "InsertValue".to_string());
699    }
700
701    fn enter_set(&mut self, _set: &'ast Set) -> Traverse {
702        not_yet_implemented_fault!(self, "Set".to_string());
703    }
704
705    fn enter_assignment(&mut self, _assignment: &'ast Assignment) -> Traverse {
706        not_yet_implemented_fault!(self, "Assignment".to_string());
707    }
708
709    fn enter_remove(&mut self, _remove: &'ast Remove) -> Traverse {
710        not_yet_implemented_fault!(self, "Remove".to_string());
711    }
712
713    fn enter_delete(&mut self, _delete: &'ast Delete) -> Traverse {
714        not_yet_implemented_fault!(self, "Delete".to_string());
715    }
716
717    fn enter_on_conflict(&mut self, _on_conflict: &'ast OnConflict) -> Traverse {
718        not_yet_implemented_fault!(self, "OnConflict".to_string());
719    }
720
721    fn enter_top_level_query(&mut self, _query: &'ast ast::TopLevelQuery) -> Traverse {
722        self.enter_benv();
723        Traverse::Continue
724    }
725    fn exit_top_level_query(&mut self, _query: &'ast ast::TopLevelQuery) -> Traverse {
726        let mut benv = self.exit_benv();
727        eq_or_fault!(self, benv.len(), 1, "Expect benv.len() == 1");
728        let out = benv.pop().unwrap();
729        let sink_id = self.curr_plan().add_operator(BindingsOp::Sink);
730        self.curr_plan().add_flow(out, sink_id);
731        Traverse::Continue
732    }
733
734    fn enter_query(&mut self, query: &'ast Query) -> Traverse {
735        self.enter_benv();
736        if let QuerySet::Select(_) = query.set.node {
737            self.enter_q();
738        }
739        Traverse::Continue
740    }
741
742    fn exit_query(&mut self, query: &'ast Query) -> Traverse {
743        let benv = self.exit_benv();
744        match query.set.node {
745            QuerySet::Select(_) => {
746                let clauses = self.exit_q();
747                let mut clauses = clauses.evaluation_order().into_iter();
748                if let Some(mut src_id) = clauses.next() {
749                    for dst_id in clauses {
750                        self.curr_plan().add_flow(src_id, dst_id);
751                        src_id = dst_id;
752                    }
753                    self.push_bexpr(src_id);
754                }
755            }
756            _ => {
757                true_or_fault!(
758                    self,
759                    (1..=3).contains(&benv.len()),
760                    "benv.len() is not between 1 and 3"
761                );
762                let mut out = *benv.first().unwrap();
763                benv.into_iter().skip(1).for_each(|op| {
764                    self.curr_plan().add_flow(out, op);
765                    out = op;
766                });
767                self.push_bexpr(out);
768            }
769        }
770        Traverse::Continue
771    }
772
773    fn enter_query_set(&mut self, _query_set: &'ast QuerySet) -> Traverse {
774        self.enter_env();
775        self.enter_benv();
776
777        match _query_set {
778            QuerySet::BagOp(_) => {}
779            QuerySet::Select(_) => {}
780            QuerySet::Expr(_) => {}
781            QuerySet::Values(_) => {
782                not_yet_implemented_fault!(self, "QuerySet::Values".to_string());
783            }
784            QuerySet::Table(_) => {
785                not_yet_implemented_fault!(self, "QuerySet::Table".to_string());
786            }
787        }
788        Traverse::Continue
789    }
790
791    fn exit_query_set(&mut self, query_set: &'ast QuerySet) -> Traverse {
792        let env = self.exit_env();
793        let mut benv = self.exit_benv();
794
795        match query_set {
796            QuerySet::BagOp(bag_op) => {
797                eq_or_fault!(self, benv.len(), 2, "qs benv.len() != 2");
798                let rid = benv.pop().unwrap();
799                let lid = benv.pop().unwrap();
800
801                let bag_operator = match bag_op.node.bag_op {
802                    BagOperator::Union => logical::BagOperator::Union,
803                    BagOperator::Except => logical::BagOperator::Except,
804                    BagOperator::Intersect => logical::BagOperator::Intersect,
805                    BagOperator::OuterUnion => logical::BagOperator::OuterUnion,
806                    BagOperator::OuterExcept => logical::BagOperator::OuterExcept,
807                    BagOperator::OuterIntersect => logical::BagOperator::OuterIntersect,
808                };
809                let setq = match bag_op.node.setq {
810                    Some(SetQuantifier::All) => logical::SetQuantifier::All,
811                    Some(SetQuantifier::Distinct) => logical::SetQuantifier::Distinct,
812                    None => logical::SetQuantifier::Distinct,
813                };
814
815                let id = self.curr_plan().add_operator(BindingsOp::BagOp(BagOp {
816                    bag_op: bag_operator,
817                    setq,
818                }));
819                self.curr_plan().add_flow_with_branch_num(lid, id, 0);
820                self.curr_plan().add_flow_with_branch_num(rid, id, 1);
821                self.push_bexpr(id);
822            }
823            QuerySet::Select(_) => {}
824            QuerySet::Expr(_) => {
825                eq_or_fault!(self, env.len(), 1, "env.len() != 1");
826                let (_, expr) = env.into_iter().next().unwrap();
827                let op = BindingsOp::ExprQuery(logical::ExprQuery { expr });
828                let id = self.curr_plan().add_operator(op);
829                self.push_bexpr(id);
830            }
831            QuerySet::Values(_) => {
832                not_yet_implemented_fault!(self, "QuerySet::Values".to_string());
833            }
834            QuerySet::Table(_) => {
835                not_yet_implemented_fault!(self, "QuerySet::Table".to_string());
836            }
837        }
838        Traverse::Continue
839    }
840
841    fn enter_bag_op_expr(&mut self, _set_expr: &'ast BagOpExpr) -> Traverse {
842        Traverse::Continue
843    }
844
845    fn exit_bag_op_expr(&mut self, _set_expr: &'ast BagOpExpr) -> Traverse {
846        Traverse::Continue
847    }
848
849    fn enter_exclusion(&mut self, _exclusion: &'ast Exclusion) -> Traverse {
850        not_yet_implemented_fault!(self, "EXCLUDE");
851    }
852
853    fn enter_select(&mut self, select: &'ast Select) -> Traverse {
854        if select.having.is_some() && select.group_by.is_none() {
855            self.errors.push(AstTransformError::HavingWithoutGroupBy);
856            Traverse::Stop
857        } else {
858            Traverse::Continue
859        }
860    }
861
862    fn exit_select(&mut self, _select: &'ast Select) -> Traverse {
863        // PartiQL permits SQL aggregations without a GROUP BY (e.g. SELECT SUM(t.a) FROM ...)
864        // What follows adds a GROUP BY clause with the rewrite `... GROUP BY true AS $__gk`
865        if !self.aggregate_exprs.last().unwrap().is_empty()
866            && self.current_clauses_mut().group_by_clause.is_none()
867        {
868            let exprs = FxHashMap::from_iter([(
869                "$__gk".to_string(),
870                ValueExpr::Lit(Box::new(logical::Lit::Bool(true))),
871            )]);
872            let group_by: BindingsOp = BindingsOp::GroupBy(logical::GroupBy {
873                strategy: logical::GroupingStrategy::GroupFull,
874                exprs,
875                aggregate_exprs: self.aggregate_exprs.last().unwrap().clone(),
876                group_as_alias: None,
877            });
878            let id = self.curr_plan().add_operator(group_by);
879            self.current_clauses_mut().group_by_clause.replace(id);
880        }
881        Traverse::Continue
882    }
883
884    fn enter_projection(&mut self, _projection: &'ast Projection) -> Traverse {
885        self.enter_benv();
886        self.enter_env();
887        Traverse::Continue
888    }
889
890    fn exit_projection(&mut self, projection: &'ast Projection) -> Traverse {
891        let benv = self.exit_benv();
892        eq_or_fault!(self, benv.len(), 0, "benv.len() != 0");
893
894        let env = self.exit_env();
895        eq_or_fault!(self, env.len(), 0, "env.len() != 0");
896
897        if let Some(SetQuantifier::Distinct) = projection.setq {
898            let id = self.curr_plan().add_operator(BindingsOp::Distinct);
899            self.current_clauses_mut().distinct.replace(id);
900        }
901        Traverse::Continue
902    }
903
904    fn enter_projection_kind(&mut self, _projection_kind: &'ast ProjectionKind) -> Traverse {
905        self.enter_benv();
906        self.enter_env();
907        Traverse::Continue
908    }
909
910    fn exit_projection_kind(&mut self, _projection_kind: &'ast ProjectionKind) -> Traverse {
911        let benv = self.exit_benv();
912        if !benv.is_empty() {
913            not_yet_implemented_fault!(self, "Subquery within project".to_string());
914        }
915        let env = self.exit_env();
916
917        let select: BindingsOp = match _projection_kind {
918            ProjectionKind::ProjectStar => logical::BindingsOp::ProjectAll(Default::default()),
919            ProjectionKind::ProjectList(_) => {
920                true_or_fault!(self, env.len().is_even(), "env.len() is not even");
921                let mut exprs = Vec::with_capacity(env.len() / 2);
922                let mut iter = env.into_iter();
923                while let Some((_, value)) = iter.next() {
924                    let (_, alias) = iter.next().unwrap();
925                    let alias = match alias {
926                        ValueExpr::Lit(lit) => match *lit {
927                            logical::Lit::String(s) => s.clone(),
928                            _ => {
929                                // Report error but allow visitor to continue
930                                self.errors.push(AstTransformError::IllegalState(
931                                    "Unexpected literal type".to_string(),
932                                ));
933                                String::new()
934                            }
935                        },
936                        _ => {
937                            // Report error but allow visitor to continue
938                            self.errors.push(AstTransformError::IllegalState(
939                                "Invalid alias type".to_string(),
940                            ));
941                            String::new()
942                        }
943                    };
944
945                    if !alias.is_empty() {
946                        if let ValueExpr::VarRef(name, _vrtype) = &value {
947                            self.projection_renames
948                                .last_mut()
949                                .expect("renames")
950                                .insert(alias.clone(), name.clone());
951                        }
952                    }
953                    exprs.push((alias, value));
954                }
955
956                logical::BindingsOp::Project(logical::Project { exprs })
957            }
958            ProjectionKind::ProjectPivot(_) => {
959                eq_or_fault!(self, env.len(), 2, "env.len() != 2");
960
961                let mut iter = env.into_iter();
962                let (_, key) = iter.next().unwrap();
963                let (_, value) = iter.next().unwrap();
964                logical::BindingsOp::Pivot(logical::Pivot { key, value })
965            }
966            ProjectionKind::ProjectValue(_) => {
967                eq_or_fault!(self, env.len(), 1, "env.len() != 1");
968
969                let (_, expr) = env.into_iter().next().unwrap();
970                logical::BindingsOp::ProjectValue(logical::ProjectValue { expr })
971            }
972        };
973        let id = self.curr_plan().add_operator(select);
974        self.current_clauses_mut().select_clause.replace(id);
975        Traverse::Continue
976    }
977
978    fn exit_project_expr(&mut self, _project_expr: &'ast ProjectExpr) -> Traverse {
979        let as_key: &name_resolver::Symbol = self
980            .key_registry
981            .aliases
982            .get(self.current_node())
983            .expect("alias");
984        // TODO intern strings
985        let as_key = match as_key {
986            name_resolver::Symbol::Known(sym) => sym.value.clone(),
987            name_resolver::Symbol::Unknown(id) => format!("_{id}"),
988        };
989        self.push_lit(logical::Lit::String(as_key));
990        Traverse::Continue
991    }
992
993    fn enter_bin_op(&mut self, _bin_op: &'ast BinOp) -> Traverse {
994        self.enter_env();
995        Traverse::Continue
996    }
997
998    fn exit_bin_op(&mut self, _bin_op: &'ast BinOp) -> Traverse {
999        let mut env = self.exit_env();
1000        eq_or_fault!(self, env.len(), 2, "env.len() != 2");
1001
1002        let (_, rhs) = env.pop().unwrap();
1003        let (_, lhs) = env.pop().unwrap();
1004        if _bin_op.kind == BinOpKind::Is {
1005            let is_type = match rhs {
1006                ValueExpr::Lit(lit) => match lit.as_ref() {
1007                    logical::Lit::Null => logical::Type::NullType,
1008                    logical::Lit::Missing => logical::Type::MissingType,
1009                    _ => {
1010                        not_yet_implemented_fault!(
1011                            self,
1012                            "Unsupported rhs literal for `IS`".to_string()
1013                        );
1014                    }
1015                },
1016                _ => {
1017                    not_yet_implemented_fault!(self, "Unsupported rhs for `IS`".to_string());
1018                }
1019            };
1020            self.push_vexpr(ValueExpr::IsTypeExpr(IsTypeExpr {
1021                not: false,
1022                expr: Box::new(lhs),
1023                is_type,
1024            }));
1025        } else {
1026            let op = match _bin_op.kind {
1027                BinOpKind::Add => logical::BinaryOp::Add,
1028                BinOpKind::Div => logical::BinaryOp::Div,
1029                BinOpKind::Exp => logical::BinaryOp::Exp,
1030                BinOpKind::Mod => logical::BinaryOp::Mod,
1031                BinOpKind::Mul => logical::BinaryOp::Mul,
1032                BinOpKind::Sub => logical::BinaryOp::Sub,
1033                BinOpKind::And => logical::BinaryOp::And,
1034                BinOpKind::Or => logical::BinaryOp::Or,
1035                BinOpKind::Concat => logical::BinaryOp::Concat,
1036                BinOpKind::Eq => logical::BinaryOp::Eq,
1037                BinOpKind::Gt => logical::BinaryOp::Gt,
1038                BinOpKind::Gte => logical::BinaryOp::Gteq,
1039                BinOpKind::Lt => logical::BinaryOp::Lt,
1040                BinOpKind::Lte => logical::BinaryOp::Lteq,
1041                BinOpKind::Ne => logical::BinaryOp::Neq,
1042                BinOpKind::Is => unreachable!(),
1043            };
1044            self.push_vexpr(ValueExpr::BinaryExpr(op, Box::new(lhs), Box::new(rhs)));
1045        }
1046        Traverse::Continue
1047    }
1048
1049    fn enter_uni_op(&mut self, _uni_op: &'ast UniOp) -> Traverse {
1050        self.enter_env();
1051        Traverse::Continue
1052    }
1053
1054    fn exit_uni_op(&mut self, _uni_op: &'ast UniOp) -> Traverse {
1055        let mut env = self.exit_env();
1056        eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1057
1058        let (_, expr) = env.pop().unwrap();
1059        let op = match _uni_op.kind {
1060            UniOpKind::Pos => logical::UnaryOp::Pos,
1061            UniOpKind::Neg => logical::UnaryOp::Neg,
1062            UniOpKind::Not => logical::UnaryOp::Not,
1063        };
1064        self.push_vexpr(ValueExpr::UnExpr(op, Box::new(expr)));
1065        Traverse::Continue
1066    }
1067
1068    fn enter_between(&mut self, _between: &'ast Between) -> Traverse {
1069        self.enter_env();
1070        Traverse::Continue
1071    }
1072
1073    fn exit_between(&mut self, _between: &'ast Between) -> Traverse {
1074        let mut env = self.exit_env();
1075        eq_or_fault!(self, env.len(), 3, "env.len() != 3");
1076
1077        let to = Box::new(env.pop().unwrap().1);
1078        let from = Box::new(env.pop().unwrap().1);
1079        let value = Box::new(env.pop().unwrap().1);
1080        self.push_vexpr(ValueExpr::BetweenExpr(BetweenExpr { value, from, to }));
1081        Traverse::Continue
1082    }
1083
1084    fn enter_in(&mut self, _in: &'ast ast::In) -> Traverse {
1085        self.enter_env();
1086        Traverse::Continue
1087    }
1088    fn exit_in(&mut self, _in: &'ast ast::In) -> Traverse {
1089        let mut env = self.exit_env();
1090        eq_or_fault!(self, env.len(), 2, "env.len() != 2");
1091
1092        let (_, rhs) = env.pop().unwrap();
1093        let (_, lhs) = env.pop().unwrap();
1094        self.push_vexpr(logical::ValueExpr::BinaryExpr(
1095            logical::BinaryOp::In,
1096            Box::new(lhs),
1097            Box::new(rhs),
1098        ));
1099        Traverse::Continue
1100    }
1101
1102    fn enter_like(&mut self, _like: &'ast Like) -> Traverse {
1103        self.enter_env();
1104        Traverse::Continue
1105    }
1106
1107    fn exit_like(&mut self, _like: &'ast Like) -> Traverse {
1108        let mut env = self.exit_env();
1109        true_or_fault!(
1110            self,
1111            (2..=3).contains(&env.len()),
1112            "env.len() is not between 2 and 3"
1113        );
1114        let escape_ve = if env.len() == 3 {
1115            env.pop().unwrap().1
1116        } else {
1117            ValueExpr::Lit(Box::new(logical::Lit::String(String::default())))
1118        };
1119        let pattern_ve = env.pop().unwrap().1;
1120        let value = Box::new(env.pop().unwrap().1);
1121
1122        let pattern = match (&pattern_ve, &escape_ve) {
1123            (ValueExpr::Lit(pattern_lit), ValueExpr::Lit(escape_lit)) => {
1124                match (pattern_lit.as_ref(), escape_lit.as_ref()) {
1125                    (logical::Lit::String(pattern), logical::Lit::String(escape)) => {
1126                        Pattern::Like(LikeMatch {
1127                            pattern: pattern.to_string(),
1128                            escape: escape.to_string(),
1129                        })
1130                    }
1131                    _ => Pattern::LikeNonStringNonLiteral(LikeNonStringNonLiteralMatch {
1132                        pattern: Box::new(pattern_ve),
1133                        escape: Box::new(escape_ve),
1134                    }),
1135                }
1136            }
1137            _ => Pattern::LikeNonStringNonLiteral(LikeNonStringNonLiteralMatch {
1138                pattern: Box::new(pattern_ve),
1139                escape: Box::new(escape_ve),
1140            }),
1141        };
1142
1143        let pattern = ValueExpr::PatternMatchExpr(PatternMatchExpr { value, pattern });
1144        self.push_vexpr(pattern);
1145        Traverse::Continue
1146    }
1147
1148    fn enter_call(&mut self, _call: &'ast Call) -> Traverse {
1149        self.enter_call();
1150        Traverse::Continue
1151    }
1152
1153    fn exit_call(&mut self, call: &'ast Call) -> Traverse {
1154        // TODO better argument validation/error messaging
1155        let args = self.exit_call();
1156        let name = call.func_name.value.to_lowercase();
1157
1158        let call_def_to_vexpr = |call_def: &CallDef| call_def.lookup(&args, &name);
1159
1160        let call_expr = self
1161            .fnsym_tab
1162            .lookup(&name)
1163            .map(call_def_to_vexpr)
1164            .or_else(|| {
1165                self.catalog
1166                    .get_function(&name)
1167                    .map(|e| e.resolve(&name, &args))
1168            })
1169            .map(|res| res.map_err(Into::into))
1170            .unwrap_or_else(|| Err(AstTransformError::UnsupportedFunction(name.clone())));
1171
1172        let expr = match call_expr {
1173            Ok(expr) => expr,
1174            Err(err) => {
1175                self.errors.push(err);
1176                ValueExpr::Lit(Box::new(logical::Lit::Missing)) // dummy expression to allow lowering to continue
1177            }
1178        };
1179        self.push_vexpr(expr);
1180        Traverse::Continue
1181    }
1182
1183    fn enter_call_arg(&mut self, _call_arg: &'ast CallArg) -> Traverse {
1184        self.enter_env();
1185        Traverse::Continue
1186    }
1187
1188    fn exit_call_arg(&mut self, _call_arg: &'ast CallArg) -> Traverse {
1189        let mut env = self.exit_env();
1190        match _call_arg {
1191            CallArg::Star() => {
1192                self.push_call_arg(CallArgument::Star);
1193            }
1194            CallArg::Positional(_) => {
1195                eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1196
1197                self.push_call_arg(CallArgument::Positional(env.pop().unwrap().1));
1198            }
1199            CallArg::Named(CallArgNamed { name, .. }) => {
1200                eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1201
1202                let name = name.value.to_lowercase();
1203                self.push_call_arg(CallArgument::Named(name, env.pop().unwrap().1));
1204            }
1205            CallArg::PositionalType(_) => {
1206                not_yet_implemented_fault!(self, "PositionalType call argument".to_string());
1207            }
1208            CallArg::NamedType(_) => {
1209                not_yet_implemented_fault!(self, "NamedType call argument".to_string());
1210            }
1211        }
1212        Traverse::Continue
1213    }
1214
1215    // Values & Value Constructors
1216
1217    fn enter_lit(&mut self, lit: &'ast Lit) -> Traverse {
1218        let val = match lit_to_lit(lit) {
1219            Ok(v) => v,
1220            Err(e) => {
1221                // Report error but allow visitor to continue
1222                self.errors.push(e);
1223                logical::Lit::Missing
1224            }
1225        };
1226        self.push_lit(val);
1227        Traverse::Continue
1228    }
1229
1230    fn enter_struct(&mut self, _struct: &'ast Struct) -> Traverse {
1231        self.enter_env();
1232        Traverse::Continue
1233    }
1234
1235    fn exit_struct(&mut self, _struct: &'ast Struct) -> Traverse {
1236        let env = self.exit_env();
1237        true_or_fault!(self, env.len().is_even(), "env.len() is not even");
1238
1239        let len = env.len() / 2;
1240        let mut attrs = Vec::with_capacity(len);
1241        let mut values = Vec::with_capacity(len);
1242
1243        let mut iter = env.into_iter();
1244        while let Some((_, attr)) = iter.next() {
1245            let (_, value) = iter.next().unwrap();
1246            attrs.push(attr);
1247            values.push(value);
1248        }
1249
1250        self.push_vexpr(ValueExpr::TupleExpr(TupleExpr { attrs, values }));
1251        Traverse::Continue
1252    }
1253
1254    fn enter_bag(&mut self, _bag: &'ast Bag) -> Traverse {
1255        self.enter_env();
1256        Traverse::Continue
1257    }
1258
1259    fn exit_bag(&mut self, _bag: &'ast Bag) -> Traverse {
1260        let elements = self.exit_env().into_iter().map(|(_, v)| v).collect();
1261        self.push_vexpr(ValueExpr::BagExpr(BagExpr { elements }));
1262        Traverse::Continue
1263    }
1264
1265    fn enter_list(&mut self, _list: &'ast List) -> Traverse {
1266        self.enter_env();
1267        Traverse::Continue
1268    }
1269
1270    fn exit_list(&mut self, _list: &'ast List) -> Traverse {
1271        let elements = self.exit_env().into_iter().map(|(_, v)| v).collect();
1272        self.push_vexpr(ValueExpr::ListExpr(ListExpr { elements }));
1273        Traverse::Continue
1274    }
1275
1276    fn enter_call_agg(&mut self, _call_agg: &'ast CallAgg) -> Traverse {
1277        self.enter_call();
1278        Traverse::Continue
1279    }
1280
1281    fn exit_call_agg(&mut self, call_agg: &'ast CallAgg) -> Traverse {
1282        // Relates to the SQL aggregation functions (e.g. AVG, COUNT, SUM) -- not the `COLL_`
1283        // functions
1284        let mut env = self.exit_call();
1285        let name = call_agg.func_name.value.to_lowercase();
1286
1287        // Rewrites the SQL aggregation function call to be a variable reference that the `GROUP BY`
1288        // clause will add to the binding tuples.
1289        // E.g. SELECT a, SUM(b) FROM t GROUP BY a
1290        //      SELECT a AS a, $__agg_1 AS b FROM t GROUP BY a
1291        let new_name = "$__agg".to_owned() + &self.agg_id.id();
1292        let new_binding_name = BindingsName::CaseSensitive(Cow::Owned(new_name.clone()));
1293        let new_expr = ValueExpr::VarRef(new_binding_name, VarRefType::Local);
1294        self.push_vexpr(new_expr);
1295
1296        true_or_fault!(self, !env.is_empty(), "env is empty");
1297        // Default set quantifier if the set quantifier keyword is omitted will be `ALL`
1298        let (setq, arg) = match env.pop().unwrap() {
1299            CallArgument::Positional(ve) => (logical::SetQuantifier::All, ve),
1300            CallArgument::Named(name, ve) => match name.as_ref() {
1301                "all" => (logical::SetQuantifier::All, ve),
1302                "distinct" => (logical::SetQuantifier::Distinct, ve),
1303                _ => {
1304                    self.errors.push(AstTransformError::IllegalState(
1305                        "Invalid set quantifier".to_string(),
1306                    ));
1307                    return Traverse::Stop;
1308                }
1309            },
1310            CallArgument::Star => (
1311                logical::SetQuantifier::All,
1312                ValueExpr::Lit(Box::new(logical::Lit::Int8(1))),
1313            ),
1314        };
1315
1316        let agg_expr = match name.as_str() {
1317            "avg" => AggregateExpression {
1318                name: new_name,
1319                expr: arg,
1320                func: AggAvg,
1321                setq,
1322            },
1323            "count" => AggregateExpression {
1324                name: new_name,
1325                expr: arg,
1326                func: AggCount,
1327                setq,
1328            },
1329            "max" => AggregateExpression {
1330                name: new_name,
1331                expr: arg,
1332                func: AggMax,
1333                setq,
1334            },
1335            "min" => AggregateExpression {
1336                name: new_name,
1337                expr: arg,
1338                func: AggMin,
1339                setq,
1340            },
1341            "sum" => AggregateExpression {
1342                name: new_name,
1343                expr: arg,
1344                func: AggSum,
1345                setq,
1346            },
1347            "any" | "some" => AggregateExpression {
1348                name: new_name,
1349                expr: arg,
1350                func: AggAny,
1351                setq,
1352            },
1353            "every" => AggregateExpression {
1354                name: new_name,
1355                expr: arg,
1356                func: AggEvery,
1357                setq,
1358            },
1359            _ => {
1360                // Include as an error but allow lowering to proceed for multiple error reporting
1361                self.errors
1362                    .push(AstTransformError::UnsupportedFunction(name));
1363                // continue lowering with `AggAvg` aggregation function
1364                AggregateExpression {
1365                    name: new_name,
1366                    expr: arg,
1367                    func: AggAvg,
1368                    setq,
1369                }
1370            }
1371        };
1372        self.aggregate_exprs.last_mut().unwrap().push(agg_expr);
1373        Traverse::Continue
1374    }
1375
1376    fn enter_var_ref(&mut self, var_ref: &'ast VarRef) -> Traverse {
1377        let is_path = matches!(self.current_ctx(), Some(QueryContext::Path));
1378        if !is_path {
1379            let options = self.resolve_varref(var_ref);
1380            self.push_vexpr(options);
1381        } else {
1382            let VarRef {
1383                name: SymbolPrimitive { value, case },
1384                qualifier: _,
1385            } = var_ref;
1386            let name = match case {
1387                CaseSensitivity::CaseSensitive => {
1388                    BindingsName::CaseSensitive(Cow::Owned(value.clone()))
1389                }
1390                CaseSensitivity::CaseInsensitive => {
1391                    BindingsName::CaseInsensitive(Cow::Owned(value.clone()))
1392                }
1393            };
1394            self.push_vexpr(ValueExpr::VarRef(name, VarRefType::Local));
1395        }
1396        Traverse::Continue
1397    }
1398
1399    fn exit_var_ref(&mut self, _var_ref: &'ast VarRef) -> Traverse {
1400        Traverse::Continue
1401    }
1402
1403    fn enter_path(&mut self, _path: &'ast Path) -> Traverse {
1404        self.enter_env();
1405        self.enter_path();
1406        Traverse::Continue
1407    }
1408
1409    fn exit_path(&mut self, _path: &'ast Path) -> Traverse {
1410        let mut env = self.exit_env();
1411        eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1412
1413        let steps = self.exit_path();
1414        let (_, root) = env.pop().unwrap();
1415
1416        self.push_vexpr(ValueExpr::Path(Box::new(root), steps));
1417        Traverse::Continue
1418    }
1419
1420    fn enter_path_step(&mut self, path_step: &'ast PathStep) -> Traverse {
1421        if let PathStep::PathIndex(expr) | PathStep::PathProject(expr) = path_step {
1422            self.enter_env();
1423            match *(expr.index) {
1424                Expr::VarRef(_) => {
1425                    // covers case of var refs along path: a.b.c <-- the "b" and "c" in "a.b.c" are local lookups only
1426                    let qc = self.ctx_stack.last_mut().unwrap();
1427                    *qc = QueryContext::Path;
1428                }
1429                _ => {
1430                    // covers case of a.b[c + 1] <-- the "c" in "c + 1" could be a dynamic lookup
1431                    let qc = self.ctx_stack.last_mut().unwrap();
1432                    *qc = QueryContext::Query;
1433                }
1434            }
1435        }
1436        Traverse::Continue
1437    }
1438
1439    fn exit_path_step(&mut self, path_step: &'ast PathStep) -> Traverse {
1440        let step = match path_step {
1441            PathStep::PathProject(_s) | PathStep::PathIndex(_s) => {
1442                let mut env = self.exit_env();
1443                eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1444
1445                let (_, path) = env.pop().unwrap();
1446                match path {
1447                    ValueExpr::Lit(val) => match *val {
1448                        logical::Lit::Int8(idx) => logical::PathComponent::Index(idx.into()),
1449                        logical::Lit::Int16(idx) => logical::PathComponent::Index(idx.into()),
1450                        logical::Lit::Int32(idx) => logical::PathComponent::Index(idx.into()),
1451                        logical::Lit::Int64(idx) => logical::PathComponent::Index(idx),
1452                        logical::Lit::String(k) => logical::PathComponent::Key(
1453                            BindingsName::CaseInsensitive(Cow::Owned(k)),
1454                        ),
1455                        expr => logical::PathComponent::IndexExpr(Box::new(ValueExpr::Lit(
1456                            Box::new(expr),
1457                        ))),
1458                    },
1459                    ValueExpr::VarRef(name, _) => logical::PathComponent::Key(name),
1460                    expr => {
1461                        // TODO if type is statically STRING, then use KeyExpr
1462                        logical::PathComponent::IndexExpr(Box::new(expr))
1463                    }
1464                }
1465            }
1466            PathStep::PathForEach => {
1467                not_yet_implemented_fault!(self, "PathStep::PathForEach".to_string());
1468            }
1469            PathStep::PathUnpivot => {
1470                not_yet_implemented_fault!(self, "PathStep::PathUnpivot".to_string());
1471            }
1472        };
1473
1474        self.push_path_step(step);
1475        Traverse::Continue
1476    }
1477
1478    fn enter_from_clause(&mut self, _from_clause: &'ast FromClause) -> Traverse {
1479        self.enter_benv();
1480        self.enter_env();
1481        Traverse::Continue
1482    }
1483
1484    fn exit_from_clause(&mut self, _from_clause: &'ast FromClause) -> Traverse {
1485        let mut benv = self.exit_benv();
1486        eq_or_fault!(self, benv.len(), 1, "benv.len() != 1");
1487
1488        let env = self.exit_env();
1489        eq_or_fault!(self, env.len(), 0, "env.len() != 0");
1490
1491        self.current_clauses_mut()
1492            .from_clause
1493            .replace(benv.pop().unwrap());
1494        Traverse::Continue
1495    }
1496
1497    fn enter_from_let(&mut self, from_let: &'ast FromLet) -> Traverse {
1498        *self.current_ctx_mut() = QueryContext::FromLet;
1499        self.enter_plan();
1500        self.enter_benv();
1501        self.enter_env();
1502
1503        let id = *self.current_node();
1504
1505        for sym in [&from_let.as_alias, &from_let.at_alias, &from_let.by_alias]
1506            .into_iter()
1507            .flatten()
1508        {
1509            self.aliases.insert(id, sym.clone());
1510        }
1511        Traverse::Continue
1512    }
1513
1514    fn exit_from_let(&mut self, from_let: &'ast FromLet) -> Traverse {
1515        *self.current_ctx_mut() = QueryContext::Query;
1516        let subplan = self.exit_plan();
1517        let benv = self.exit_benv();
1518        let mut env = self.exit_env();
1519        eq_or_fault!(self, env.len() + benv.len(), 1, "env.len()+benv.len() != 1");
1520
1521        let expr = if !benv.is_empty() {
1522            // Subquery in From Let
1523            let subq = logical::SubQueryExpr { plan: subplan };
1524            ValueExpr::SubQueryExpr(subq)
1525        } else {
1526            // Expression in From Let
1527            self.curr_plan().merge_plan(subplan); // merge in subplan, as there is no subquery
1528            env.pop().unwrap().1
1529        };
1530
1531        let FromLet {
1532            kind,
1533            as_alias,
1534            at_alias,
1535            ..
1536        } = from_let;
1537        let as_key = self.infer_id(&expr, as_alias).value;
1538        let at_key = at_alias
1539            .as_ref()
1540            .map(|SymbolPrimitive { value, case: _ }| value.clone());
1541
1542        let (bexpr, project_all_mode) = match kind {
1543            FromLetKind::Scan => (
1544                logical::BindingsOp::Scan(logical::Scan {
1545                    expr,
1546                    as_key,
1547                    at_key,
1548                }),
1549                ProjectAllMode::Unwrap,
1550            ),
1551            FromLetKind::Unpivot => (
1552                logical::BindingsOp::Unpivot(logical::Unpivot {
1553                    expr,
1554                    as_key,
1555                    at_key,
1556                }),
1557                ProjectAllMode::PassThrough,
1558            ),
1559            FromLetKind::GraphTable => (
1560                logical::BindingsOp::Scan(logical::Scan {
1561                    expr,
1562                    as_key,
1563                    at_key,
1564                }),
1565                ProjectAllMode::Unwrap,
1566            ),
1567        };
1568
1569        let id = self.curr_plan().add_operator(bexpr);
1570        self.push_bexpr(id);
1571
1572        if let Some(select_id) = self.current_clauses_mut().select_clause {
1573            if let Some(BindingsOp::ProjectAll(mode)) = self.curr_plan().operator_as_mut(select_id)
1574            {
1575                *mode = project_all_mode
1576            }
1577        }
1578
1579        Traverse::Continue
1580    }
1581
1582    fn enter_join(&mut self, _join: &'ast Join) -> Traverse {
1583        self.enter_benv();
1584        self.enter_env();
1585        Traverse::Continue
1586    }
1587
1588    fn exit_join(&mut self, join: &'ast Join) -> Traverse {
1589        let mut benv = self.exit_benv();
1590        eq_or_fault!(self, benv.len(), 2, "j benv.len() != 2");
1591
1592        let mut env = self.exit_env();
1593        true_or_fault!(
1594            self,
1595            (0..=1).contains(&env.len()),
1596            "env.len() is not between 0 and 1"
1597        );
1598
1599        let Join { kind, .. } = join;
1600
1601        let kind = match kind {
1602            JoinKind::Inner => logical::JoinKind::Inner,
1603            JoinKind::Left => logical::JoinKind::Left,
1604            JoinKind::Right => logical::JoinKind::Right,
1605            JoinKind::Full => logical::JoinKind::Full,
1606            JoinKind::Cross => logical::JoinKind::Cross,
1607        };
1608
1609        let on = env.pop().map(|(_, v)| v);
1610
1611        let rid = benv.pop().unwrap();
1612        let lid = benv.pop().unwrap();
1613        let left = Box::new(self.curr_plan().operator(lid).unwrap().clone());
1614        let right = Box::new(self.curr_plan().operator(rid).unwrap().clone());
1615        let join = logical::BindingsOp::Join(logical::Join {
1616            kind,
1617            left,
1618            right,
1619            on,
1620        });
1621        let join = self.curr_plan().add_operator(join);
1622        self.curr_plan().add_flow_with_branch_num(lid, join, 0);
1623        self.curr_plan().add_flow_with_branch_num(rid, join, 1);
1624        self.push_bexpr(join);
1625        Traverse::Continue
1626    }
1627
1628    fn enter_join_spec(&mut self, join_spec: &'ast JoinSpec) -> Traverse {
1629        match join_spec {
1630            JoinSpec::On(_) => {
1631                // visitor recurse into expr will put the condition in the current env
1632            }
1633            JoinSpec::Using(_) => {
1634                not_yet_implemented_fault!(self, "JoinSpec::Using".to_string());
1635            }
1636            JoinSpec::Natural => {
1637                not_yet_implemented_fault!(self, "JoinSpec::Natural".to_string());
1638            }
1639        };
1640        Traverse::Continue
1641    }
1642
1643    fn enter_where_clause(&mut self, _where_clause: &'ast ast::WhereClause) -> Traverse {
1644        self.enter_env();
1645        Traverse::Continue
1646    }
1647
1648    fn exit_where_clause(&mut self, _where_clause: &'ast ast::WhereClause) -> Traverse {
1649        let mut env = self.exit_env();
1650        eq_or_fault!(self, env.len(), 1, "env.len() != 1");
1651
1652        let filter = logical::BindingsOp::Filter(logical::Filter {
1653            expr: env.pop().unwrap().1,
1654        });
1655        let id = self.curr_plan().add_operator(filter);
1656
1657        self.current_clauses_mut().where_clause.replace(id);
1658        Traverse::Continue
1659    }
1660
1661    fn enter_having_clause(&mut self, _having_clause: &'ast ast::HavingClause) -> Traverse {
1662        self.enter_env();
1663        Traverse::Continue
1664    }
1665
1666    fn exit_having_clause(&mut self, _having_clause: &'ast ast::HavingClause) -> Traverse {
1667        let mut env = self.exit_env();
1668        eq_or_fault!(self, env.len(), 1, "env.len() is 1");
1669
1670        let having = BindingsOp::Having(logical::Having {
1671            expr: env.pop().unwrap().1,
1672        });
1673        let id = self.curr_plan().add_operator(having);
1674
1675        self.current_clauses_mut().having_clause.replace(id);
1676        Traverse::Continue
1677    }
1678
1679    fn enter_group_by_expr(&mut self, _group_by_expr: &'ast GroupByExpr) -> Traverse {
1680        self.enter_benv();
1681        self.enter_env();
1682        Traverse::Continue
1683    }
1684
1685    fn exit_group_by_expr(&mut self, group_by_expr: &'ast GroupByExpr) -> Traverse {
1686        let aggregate_exprs = self.aggregate_exprs.last().unwrap().clone();
1687        let benv = self.exit_benv();
1688        if !benv.is_empty() {
1689            {
1690                not_yet_implemented_fault!(self, "Subquery in group by".to_string());
1691            }
1692        }
1693        let env = self.exit_env();
1694        true_or_fault!(self, env.len().is_even(), "env.len() is not even");
1695
1696        let group_as_alias = group_by_expr
1697            .group_as_alias
1698            .as_ref()
1699            .map(|SymbolPrimitive { value, case: _ }| value.clone());
1700
1701        let strategy = match group_by_expr.strategy {
1702            None => logical::GroupingStrategy::GroupFull,
1703            Some(GroupingStrategy::GroupFull) => logical::GroupingStrategy::GroupFull,
1704            Some(GroupingStrategy::GroupPartial) => logical::GroupingStrategy::GroupPartial,
1705        };
1706
1707        // What follows is an approach to implement section 11.2.1 of the PartiQL spec
1708        // (https://partiql.org/assets/PartiQL-Specification.pdf#subsubsection.11.2.1)
1709        // "Grouping Attributes and Direct Use of Grouping Expressions"
1710        // Consider the query:
1711        //   SELECT t.a + 1 AS a FROM t GROUP BY t.a + 1 AS some_alias
1712        // Since the group by key expression (t.a + 1) is the same as the select list expression, we
1713        // can replace the query to be `SELECT some_alias AS a FROM t GROUP BY t.a + 1 AS some_alias`
1714        // This isn't quite correct as it doesn't deal with SELECT VALUE expressions and expressions
1715        // that are in the `HAVING` and `ORDER BY` clauses.
1716        let select_clause_op_id = self.current_clauses_mut().select_clause;
1717        if select_clause_op_id.is_none() {
1718            self.errors.push(AstTransformError::IllegalState(
1719                "select_clause_op_id is None".to_string(),
1720            ));
1721            return Traverse::Stop;
1722        }
1723        let mut errors = Vec::default();
1724        let select_clause = self
1725            .curr_plan()
1726            .operator_as_mut(select_clause_op_id.expect("select_clause_op_id not None"))
1727            .unwrap();
1728        let mut binding = Vec::new();
1729        let select_clause_exprs = match select_clause {
1730            BindingsOp::Project(ref mut project) => &mut project.exprs,
1731            BindingsOp::ProjectAll(_) => &mut binding,
1732            BindingsOp::ProjectValue(_) => &mut binding, // TODO: replacement of SELECT VALUE expressions
1733            _ => {
1734                self.errors.push(AstTransformError::IllegalState(
1735                    "Unexpected project type".to_string(),
1736                ));
1737                return Traverse::Stop;
1738            }
1739        };
1740        let mut exprs = FxHashMap::with_capacity_and_hasher(env.len() / 2, FxBuildHasher);
1741        let mut iter = env.into_iter();
1742
1743        while let Some((_, value)) = iter.next() {
1744            let (_, alias) = iter.next().unwrap();
1745            let alias = match alias {
1746                ValueExpr::Lit(lit) => match *lit {
1747                    logical::Lit::String(s) => s.clone(),
1748                    _ => {
1749                        // Report error but allow visitor to continue
1750                        errors.push(AstTransformError::IllegalState(
1751                            "Unexpected literal type".to_string(),
1752                        ));
1753                        String::new()
1754                    }
1755                },
1756                _ => {
1757                    errors.push(AstTransformError::IllegalState(
1758                        "Unexpected alias type".to_string(),
1759                    ));
1760                    return Traverse::Stop;
1761                }
1762            };
1763            for (alias, expr) in select_clause_exprs.iter_mut() {
1764                if *expr == value {
1765                    let new_binding_name = BindingsName::CaseSensitive(Cow::Owned(alias.clone()));
1766                    let new_expr = ValueExpr::VarRef(new_binding_name, VarRefType::Local);
1767                    *expr = new_expr;
1768                }
1769            }
1770            exprs.insert(alias, value);
1771        }
1772
1773        self.errors.extend(errors);
1774
1775        let group_by: BindingsOp = BindingsOp::GroupBy(logical::GroupBy {
1776            strategy,
1777            exprs,
1778            aggregate_exprs,
1779            group_as_alias,
1780        });
1781
1782        let id = self.curr_plan().add_operator(group_by);
1783        self.current_clauses_mut().group_by_clause.replace(id);
1784        Traverse::Continue
1785    }
1786
1787    fn exit_group_key(&mut self, _group_key: &'ast GroupKey) -> Traverse {
1788        let as_key: &name_resolver::Symbol = self
1789            .key_registry
1790            .aliases
1791            .get(self.current_node())
1792            .expect("alias");
1793        // TODO intern strings
1794        let as_key = match as_key {
1795            name_resolver::Symbol::Known(sym) => sym.value.clone(),
1796            name_resolver::Symbol::Unknown(id) => format!("_{id}"),
1797        };
1798        self.push_lit(logical::Lit::String(as_key));
1799        Traverse::Continue
1800    }
1801
1802    fn enter_order_by_expr(&mut self, _order_by_expr: &'ast OrderByExpr) -> Traverse {
1803        self.enter_sort();
1804        Traverse::Continue
1805    }
1806
1807    fn exit_order_by_expr(&mut self, _order_by_expr: &'ast OrderByExpr) -> Traverse {
1808        let specs = self.exit_sort();
1809        let order_by = logical::BindingsOp::OrderBy(logical::OrderBy { specs });
1810        let id = self.curr_plan().add_operator(order_by);
1811        if matches!(self.current_ctx(), Some(QueryContext::Query)) {
1812            self.current_clauses_mut().order_by_clause.replace(id);
1813        } else {
1814            self.push_bexpr(id);
1815        }
1816        Traverse::Continue
1817    }
1818
1819    fn enter_sort_spec(&mut self, _sort_spec: &'ast SortSpec) -> Traverse {
1820        self.enter_env();
1821        Traverse::Continue
1822    }
1823
1824    fn exit_sort_spec(&mut self, sort_spec: &'ast SortSpec) -> Traverse {
1825        let mut env = self.exit_env();
1826        eq_or_fault!(self, env.len(), 1, "env.len() is 1");
1827
1828        let (_, expr) = env.pop().unwrap();
1829        let order = match sort_spec
1830            .ordering_spec
1831            .as_ref()
1832            .unwrap_or(&OrderingSpec::Asc)
1833        {
1834            OrderingSpec::Asc => logical::SortSpecOrder::Asc,
1835            OrderingSpec::Desc => logical::SortSpecOrder::Desc,
1836        };
1837
1838        let null_order = match sort_spec.null_ordering_spec {
1839            None => match order {
1840                SortSpecOrder::Asc => logical::SortSpecNullOrder::Last,
1841                SortSpecOrder::Desc => logical::SortSpecNullOrder::First,
1842            },
1843            Some(NullOrderingSpec::First) => logical::SortSpecNullOrder::First,
1844            Some(NullOrderingSpec::Last) => logical::SortSpecNullOrder::Last,
1845        };
1846
1847        self.push_sort_spec(logical::SortSpec {
1848            expr,
1849            order,
1850            null_order,
1851        });
1852        Traverse::Continue
1853    }
1854
1855    fn enter_limit_offset_clause(
1856        &mut self,
1857        _limit_offset: &'ast ast::LimitOffsetClause,
1858    ) -> Traverse {
1859        self.enter_env();
1860        Traverse::Continue
1861    }
1862
1863    fn exit_limit_offset_clause(&mut self, limit_offset: &'ast ast::LimitOffsetClause) -> Traverse {
1864        let mut env = self.exit_env();
1865        true_or_fault!(
1866            self,
1867            (1..=2).contains(&env.len()),
1868            "env.len() is  not between 1 and 2"
1869        );
1870
1871        let offset = if limit_offset.offset.is_some() {
1872            env.pop().map(|(_, v)| v)
1873        } else {
1874            None
1875        };
1876        let limit = if limit_offset.limit.is_some() {
1877            env.pop().map(|(_, v)| v)
1878        } else {
1879            None
1880        };
1881
1882        let limit_offset = logical::BindingsOp::LimitOffset(logical::LimitOffset { limit, offset });
1883        let id = self.curr_plan().add_operator(limit_offset);
1884        if matches!(self.current_ctx(), Some(QueryContext::Query)) {
1885            self.current_clauses_mut().limit_offset_clause.replace(id);
1886        } else {
1887            self.push_bexpr(id);
1888        }
1889        Traverse::Continue
1890    }
1891
1892    fn enter_simple_case(&mut self, _simple_case: &'ast SimpleCase) -> Traverse {
1893        self.enter_env();
1894        Traverse::Continue
1895    }
1896
1897    fn exit_simple_case(&mut self, _simple_case: &'ast SimpleCase) -> Traverse {
1898        let mut env = self.exit_env();
1899        true_or_fault!(self, env.len() >= 2, "env.len < 2");
1900
1901        let default = if env.len().is_even() {
1902            Some(Box::new(env.pop().unwrap().1))
1903        } else {
1904            None
1905        };
1906
1907        let mut params = env.into_iter();
1908        let expr = Box::new(params.next().unwrap().1);
1909
1910        let cases = params
1911            .chunks(2)
1912            .into_iter()
1913            .map(|mut c| {
1914                let (_, when) = c.next().unwrap();
1915                let (_, then) = c.next().unwrap();
1916
1917                (Box::new(when), Box::new(then))
1918            })
1919            .collect_vec();
1920
1921        self.push_vexpr(ValueExpr::SimpleCase(logical::SimpleCase {
1922            expr,
1923            cases,
1924            default,
1925        }));
1926        Traverse::Continue
1927    }
1928
1929    fn enter_searched_case(&mut self, _searched_case: &'ast SearchedCase) -> Traverse {
1930        self.enter_env();
1931        Traverse::Continue
1932    }
1933
1934    fn exit_searched_case(&mut self, _searched_case: &'ast SearchedCase) -> Traverse {
1935        let mut env = self.exit_env();
1936        true_or_fault!(self, !env.is_empty(), "env is empty");
1937
1938        let default = if env.len().is_odd() {
1939            Some(Box::new(env.pop().unwrap().1))
1940        } else {
1941            None
1942        };
1943
1944        let cases = env
1945            .into_iter()
1946            .chunks(2)
1947            .into_iter()
1948            .map(|mut c| {
1949                let (_, when) = c.next().unwrap();
1950                let (_, then) = c.next().unwrap();
1951
1952                (Box::new(when), Box::new(then))
1953            })
1954            .collect_vec();
1955        self.push_vexpr(ValueExpr::SearchedCase(logical::SearchedCase {
1956            cases,
1957            default,
1958        }));
1959        Traverse::Continue
1960    }
1961
1962    fn enter_graph_match(&mut self, _graph_pattern: &'ast ast::GraphMatch) -> Traverse {
1963        self.enter_benv();
1964        self.enter_env();
1965        Traverse::Continue
1966    }
1967    fn exit_graph_match(&mut self, graph_match: &'ast ast::GraphMatch) -> Traverse {
1968        let benv = self.exit_benv();
1969        if !benv.is_empty() {
1970            {
1971                not_yet_implemented_fault!(self, "Subexpression in GRAPH MATCH".to_string());
1972            }
1973        }
1974        let mut env = self.exit_env();
1975
1976        let graph_reference = extract_vexpr_by_id(&mut env, graph_match.expr.id());
1977
1978        let graph_planner = crate::graph::GraphToLogical::new(env);
1979        match graph_planner.plan_graph_match(graph_match) {
1980            Ok(pattern) => {
1981                true_or_fault!(
1982                    self,
1983                    graph_reference.is_some(),
1984                    "could not find graph reference"
1985                );
1986                let graph_reference = Box::new(graph_reference.unwrap());
1987
1988                self.push_vexpr(ValueExpr::GraphMatch(Box::new(GraphMatchExpr {
1989                    value: graph_reference,
1990                    pattern,
1991                })));
1992                Traverse::Continue
1993            }
1994            Err(e) => {
1995                not_yet_implemented_err!(self, e);
1996                Traverse::Stop
1997            }
1998        }
1999    }
2000}
2001
2002fn lit_to_lit(lit: &Lit) -> Result<logical::Lit, AstTransformError> {
2003    fn tuple_pair(
2004        field: &ast::LitField,
2005    ) -> Option<Result<(String, logical::Lit), AstTransformError>> {
2006        let key = field.first.clone();
2007        match &field.second.node {
2008            Lit::Missing => None,
2009            value => match lit_to_lit(value) {
2010                Ok(value) => Some(Ok((key, value))),
2011                Err(e) => Some(Err(e)),
2012            },
2013        }
2014    }
2015
2016    let val = match lit {
2017        Lit::Null => logical::Lit::Null,
2018        Lit::Missing => logical::Lit::Missing,
2019        Lit::Int8Lit(n) => logical::Lit::Int8(*n),
2020        Lit::Int16Lit(n) => logical::Lit::Int16(*n),
2021        Lit::Int32Lit(n) => logical::Lit::Int32(*n),
2022        Lit::Int64Lit(n) => logical::Lit::Int64(*n),
2023        Lit::DecimalLit(d) => logical::Lit::Decimal(*d),
2024        Lit::NumericLit(n) => logical::Lit::Decimal(*n),
2025        Lit::RealLit(f) => logical::Lit::Double(OrderedFloat::from(*f as f64)),
2026        Lit::FloatLit(f) => logical::Lit::Double(OrderedFloat::from(*f as f64)),
2027        Lit::DoubleLit(f) => logical::Lit::Double(OrderedFloat::from(*f)),
2028        Lit::BoolLit(b) => logical::Lit::Bool(*b),
2029        Lit::EmbeddedDocLit(s, _typ) => {
2030            // TODO fix type for boxed variants
2031            logical::Lit::Variant(s.clone().into_bytes(), "Ion".to_string())
2032        }
2033        Lit::CharStringLit(s) => logical::Lit::String(s.clone()),
2034        Lit::NationalCharStringLit(s) => logical::Lit::String(s.clone()),
2035        Lit::BitStringLit(_) => {
2036            return Err(AstTransformError::NotYetImplemented(
2037                "Lit::BitStringLit".to_string(),
2038            ))
2039        }
2040        Lit::HexStringLit(_) => {
2041            return Err(AstTransformError::NotYetImplemented(
2042                "Lit::HexStringLit".to_string(),
2043            ))
2044        }
2045        Lit::BagLit(b) => {
2046            let bag: Result<_, _> = b.node.values.iter().map(lit_to_lit).collect();
2047            logical::Lit::Bag(bag?)
2048        }
2049        Lit::ListLit(l) => {
2050            let l: Result<_, _> = l.node.values.iter().map(lit_to_lit).collect();
2051            logical::Lit::List(l?)
2052        }
2053        Lit::StructLit(s) => {
2054            let tuple: Result<_, _> = s.node.fields.iter().filter_map(tuple_pair).collect();
2055            logical::Lit::Struct(tuple?)
2056        }
2057        Lit::TypedLit(_, _) => {
2058            return Err(AstTransformError::NotYetImplemented(
2059                "Lit::TypedLit".to_string(),
2060            ))
2061        }
2062    };
2063    Ok(val)
2064}
2065
2066#[cfg(test)]
2067mod tests {
2068    use super::*;
2069    use crate::LogicalPlanner;
2070    use assert_matches::assert_matches;
2071    use partiql_catalog::catalog::{MutableCatalog, PartiqlCatalog, TypeEnvEntry};
2072    use partiql_logical::BindingsOp::Project;
2073    use partiql_logical::ValueExpr;
2074    use partiql_types::PartiqlShape;
2075
2076    #[test]
2077    fn test_plan_non_existent_fns() {
2078        let catalog = PartiqlCatalog::default().to_shared_catalog();
2079        let statement = "foo(1, 2) + bar(3)";
2080        let parsed = partiql_parser::Parser::default()
2081            .parse(statement)
2082            .expect("Expect successful parse");
2083        let planner = LogicalPlanner::new(&catalog);
2084        let logical = planner.lower(&parsed);
2085        assert!(logical.is_err());
2086        let lowering_errs = logical.expect_err("Expect errs").errors;
2087        assert_eq!(lowering_errs.len(), 2);
2088        assert_matches!(
2089            lowering_errs.first(),
2090            Some(AstTransformError::UnsupportedFunction(fnc)) if fnc == "foo"
2091        );
2092        assert_matches!(
2093            lowering_errs.get(1),
2094            Some(AstTransformError::UnsupportedFunction(fnc)) if fnc == "bar"
2095        );
2096    }
2097
2098    #[test]
2099    fn test_plan_bad_num_arguments() {
2100        let catalog = PartiqlCatalog::default().to_shared_catalog();
2101        let statement = "abs(1, 2) + mod(3)";
2102        let parsed = partiql_parser::Parser::default()
2103            .parse(statement)
2104            .expect("Expect successful parse");
2105        let planner = LogicalPlanner::new(&catalog);
2106        let logical = planner.lower(&parsed);
2107        assert!(logical.is_err());
2108        let lowering_errs = logical.expect_err("Expect errs").errors;
2109        assert_eq!(lowering_errs.len(), 2);
2110        assert_matches!(
2111            lowering_errs.first(),
2112            Some(AstTransformError::InvalidNumberOfArguments(fnc)) if fnc == "abs"
2113        );
2114        assert_matches!(
2115            lowering_errs.get(1),
2116            Some(AstTransformError::InvalidNumberOfArguments(fnc)) if fnc == "mod"
2117        );
2118    }
2119
2120    #[test]
2121    fn test_plan_type_entry_in_catalog() {
2122        // Expected Logical Plan
2123        let mut expected_logical = LogicalPlan::new();
2124        let my_id = ValueExpr::Path(
2125            Box::new(ValueExpr::DynamicLookup(Box::new(vec![
2126                ValueExpr::VarRef(
2127                    BindingsName::CaseInsensitive("c".to_string().into()),
2128                    VarRefType::Local,
2129                ),
2130                ValueExpr::VarRef(
2131                    BindingsName::CaseInsensitive("c".to_string().into()),
2132                    VarRefType::Global,
2133                ),
2134            ]))),
2135            vec![PathComponent::Key(BindingsName::CaseInsensitive(
2136                "id".to_string().into(),
2137            ))],
2138        );
2139
2140        let my_name = ValueExpr::Path(
2141            Box::new(ValueExpr::DynamicLookup(Box::new(vec![ValueExpr::VarRef(
2142                BindingsName::CaseInsensitive("customers".to_string().into()),
2143                VarRefType::Global,
2144            )]))),
2145            vec![PathComponent::Key(BindingsName::CaseInsensitive(
2146                "name".to_string().into(),
2147            ))],
2148        );
2149
2150        let project = expected_logical.add_operator(Project(logical::Project {
2151            exprs: Vec::from([
2152                ("my_id".to_string(), my_id),
2153                ("my_name".to_string(), my_name),
2154            ]),
2155        }));
2156
2157        let scan = expected_logical.add_operator(BindingsOp::Scan(logical::Scan {
2158            expr: ValueExpr::DynamicLookup(Box::new(vec![ValueExpr::VarRef(
2159                BindingsName::CaseInsensitive("customers".to_string().into()),
2160                VarRefType::Global,
2161            )])),
2162            as_key: "c".to_string(),
2163            at_key: None,
2164        }));
2165        let sink = expected_logical.add_operator(BindingsOp::Sink);
2166        expected_logical.add_flow_with_branch_num(scan, project, 0);
2167        expected_logical.add_flow_with_branch_num(project, sink, 0);
2168
2169        let mut catalog = PartiqlCatalog::default();
2170        let _oid =
2171            catalog.add_type_entry(TypeEnvEntry::new("customers", &[], PartiqlShape::Dynamic));
2172        let catalog = catalog.to_shared_catalog();
2173        let statement = "SELECT c.id AS my_id, customers.name AS my_name FROM customers AS c";
2174        let parsed = partiql_parser::Parser::default()
2175            .parse(statement)
2176            .expect("Expect successful parse");
2177        let planner = LogicalPlanner::new(&catalog);
2178        let logical = planner.lower(&parsed).expect("Expect successful lowering");
2179        assert_eq!(expected_logical, logical);
2180
2181        println!("logical: {:?}", &logical);
2182    }
2183}