Skip to main content

nedb_engine/
sqlselect.rs

1// SPDX-FileCopyrightText: 2026 INTERCHAINED LLC
2// SPDX-License-Identifier: BUSL-1.1
3// NEDB · © 2026 INTERCHAINED LLC × Eth-Interchained × Vex (Claude Opus 5)
4
5//! A real SQL `SELECT` engine — expressions, aliases, `CASE`, functions, joins.
6//!
7//! # Why this module exists
8//!
9//! The pgwire layer translates SQL text into NQL text. That works beautifully
10//! for `SELECT col FROM t WHERE x = 1`, and it cannot be stretched any
11//! further: NQL has no expressions, no table aliases, no `CASE`, no scalar
12//! functions and no joins. Those are not missing features of the translation —
13//! they are things the target language cannot say.
14//!
15//! And they are exactly what catalogue introspection is made of. `psql`'s
16//! `\dt` is one statement containing a two-table `LEFT JOIN`, a nine-branch
17//! `CASE`, two scalar function calls, four qualified column references, an
18//! `IN` list, a `!~` regex and `ORDER BY 1,2`. Every one of those has to work
19//! or the command does not.
20//!
21//! So this is a small but genuine SQL evaluator: lexer, parser, expression
22//! evaluator, nested-loop join. It operates over rows supplied by a callback,
23//! which is what lets the same engine serve synthesised catalogue relations
24//! today and stored collections later.
25//!
26//! # What it is NOT
27//!
28//! It is not a query planner and does not pretend to be. The join is a nested
29//! loop, which is honest for catalogue relations (tens of rows) and would be
30//! wrong to point at a large collection without an index strategy. That
31//! boundary is enforced by the caller, not hidden here.
32//!
33//! # The rule this module follows
34//!
35//! Anything it cannot evaluate is REFUSED with an error naming the construct.
36//! It never guesses. A catalogue query that silently returns the wrong rows
37//! produces an empty or wrong table list, and a wrong table list is
38//! indistinguishable from a correct one until somebody's data appears to be
39//! missing.
40
41use crate::sqljoin::{self, JoinExec, Strategy};
42use crate::sqlplan::{Plan, Stage};
43use crate::sqlpush::Pushdown;
44
45use anyhow::{bail, Result};
46use serde_json::{Map, Value};
47
48// ─────────────────────────────────────────────────────────────────────────────
49// Phase 1 — the lexer
50// ─────────────────────────────────────────────────────────────────────────────
51
52#[derive(Debug, Clone, PartialEq)]
53pub enum Tok {
54    /// A bare identifier or keyword, with its canonical UPPERCASE form and the
55    /// raw spelling. Both are kept for the same reason NQL keeps both: a
56    /// column may legitimately be called `count` or `value`, and folding case
57    /// at the lexer would look up a key the data does not have.
58    Word { upper: String, raw: String },
59    /// A `"double quoted"` identifier. Case is significant and it is NEVER a
60    /// keyword — `"select"` is a column named select.
61    Quoted(String),
62    /// A `'single quoted'` string literal, with `''` already collapsed.
63    Str(String),
64    Num(f64),
65    Op(String),
66    Punct(char),
67    Eof,
68}
69
70impl Tok {
71    fn is_kw(&self, kw: &str) -> bool {
72        matches!(self, Tok::Word { upper, .. } if upper == kw)
73    }
74    /// The identifier text, for a token usable as a name.
75    #[allow(dead_code)] // kept as the counterpart to `is_kw`; used by earlier phases
76    fn ident(&self) -> Option<String> {
77        match self {
78            Tok::Word { raw, .. } => Some(raw.clone()),
79            Tok::Quoted(s) => Some(s.clone()),
80            _ => None,
81        }
82    }
83}
84
85/// Operators, longest first. Order is load-bearing: `!~*` must be matched
86/// before `!~`, which must be matched before `!=`, or each longer operator
87/// tokenises as a shorter one plus garbage.
88const OPERATORS: &[&str] = &[
89    "!~*", "!~", "~*", "<>", "!=", ">=", "<=", "||", "::",
90    "=", "<", ">", "~", "+", "-", "*", "/", "%",
91];
92
93pub fn lex(src: &str) -> Result<Vec<Tok>> {
94    let b: Vec<char> = src.chars().collect();
95    let mut out = vec![];
96    let mut i = 0usize;
97
98    while i < b.len() {
99        let c = b[i];
100
101        // whitespace
102        if c.is_whitespace() {
103            i += 1;
104            continue;
105        }
106
107        // `-- line comment`
108        if c == '-' && b.get(i + 1) == Some(&'-') {
109            while i < b.len() && b[i] != '\n' {
110                i += 1;
111            }
112            continue;
113        }
114
115        // `/* block comment */`, which SQL allows to nest.
116        if c == '/' && b.get(i + 1) == Some(&'*') {
117            let mut depth = 1usize;
118            i += 2;
119            while i < b.len() && depth > 0 {
120                if b[i] == '/' && b.get(i + 1) == Some(&'*') {
121                    depth += 1;
122                    i += 2;
123                } else if b[i] == '*' && b.get(i + 1) == Some(&'/') {
124                    depth -= 1;
125                    i += 2;
126                } else {
127                    i += 1;
128                }
129            }
130            if depth > 0 {
131                bail!("unterminated /* comment");
132            }
133            continue;
134        }
135
136        // 'string literal', where '' is one literal quote.
137        if c == '\'' {
138            i += 1;
139            let mut s = String::new();
140            loop {
141                match b.get(i) {
142                    None => bail!("unterminated string literal"),
143                    Some('\'') if b.get(i + 1) == Some(&'\'') => {
144                        s.push('\'');
145                        i += 2;
146                    }
147                    Some('\'') => {
148                        i += 1;
149                        break;
150                    }
151                    Some(ch) => {
152                        s.push(*ch);
153                        i += 1;
154                    }
155                }
156            }
157            out.push(Tok::Str(s));
158            continue;
159        }
160
161        // E'escape string' — Postgres spells a newline this way inside
162        // catalogue queries (`array_to_string(d.datacl, E'\n')`).
163        if (c == 'E' || c == 'e') && b.get(i + 1) == Some(&'\'') {
164            i += 2;
165            let mut s = String::new();
166            loop {
167                match b.get(i) {
168                    None => bail!("unterminated E'' string literal"),
169                    Some('\\') => {
170                        // Only the escapes that appear in real catalogue SQL.
171                        // An unknown escape keeps its literal character rather
172                        // than being dropped, so nothing silently vanishes.
173                        let esc = b.get(i + 1).copied().unwrap_or('\\');
174                        s.push(match esc {
175                            'n' => '\n',
176                            't' => '\t',
177                            'r' => '\r',
178                            '0' => '\0',
179                            other => other,
180                        });
181                        i += 2;
182                    }
183                    Some('\'') if b.get(i + 1) == Some(&'\'') => {
184                        s.push('\'');
185                        i += 2;
186                    }
187                    Some('\'') => {
188                        i += 1;
189                        break;
190                    }
191                    Some(ch) => {
192                        s.push(*ch);
193                        i += 1;
194                    }
195                }
196            }
197            out.push(Tok::Str(s));
198            continue;
199        }
200
201        // "quoted identifier", where "" is one literal quote.
202        if c == '"' {
203            i += 1;
204            let mut s = String::new();
205            loop {
206                match b.get(i) {
207                    None => bail!("unterminated quoted identifier"),
208                    Some('"') if b.get(i + 1) == Some(&'"') => {
209                        s.push('"');
210                        i += 2;
211                    }
212                    Some('"') => {
213                        i += 1;
214                        break;
215                    }
216                    Some(ch) => {
217                        s.push(*ch);
218                        i += 1;
219                    }
220                }
221            }
222            out.push(Tok::Quoted(s));
223            continue;
224        }
225
226        // number — digits, an optional fraction, an optional exponent.
227        if c.is_ascii_digit()
228            || (c == '.' && b.get(i + 1).map(|d| d.is_ascii_digit()).unwrap_or(false))
229        {
230            let start = i;
231            while i < b.len() && (b[i].is_ascii_digit() || b[i] == '.') {
232                i += 1;
233            }
234            if i < b.len() && (b[i] == 'e' || b[i] == 'E') {
235                let save = i;
236                i += 1;
237                if i < b.len() && (b[i] == '+' || b[i] == '-') {
238                    i += 1;
239                }
240                if i < b.len() && b[i].is_ascii_digit() {
241                    while i < b.len() && b[i].is_ascii_digit() {
242                        i += 1;
243                    }
244                } else {
245                    i = save; // `1e` is the number 1 followed by the name `e`
246                }
247            }
248            let text: String = b[start..i].iter().collect();
249            let n: f64 = text
250                .parse()
251                .map_err(|_| anyhow::anyhow!("not a number: {:?}", text))?;
252            out.push(Tok::Num(n));
253            continue;
254        }
255
256        // identifier / keyword. `$` is legal in a Postgres identifier.
257        if c.is_alphabetic() || c == '_' {
258            let start = i;
259            while i < b.len() && (b[i].is_alphanumeric() || b[i] == '_' || b[i] == '$') {
260                i += 1;
261            }
262            let raw: String = b[start..i].iter().collect();
263            out.push(Tok::Word { upper: raw.to_uppercase(), raw });
264            continue;
265        }
266
267        // operator — longest match wins.
268        let rest: String = b[i..].iter().take(3).collect();
269        if let Some(op) = OPERATORS.iter().find(|o| rest.starts_with(**o)) {
270            i += op.chars().count();
271            out.push(Tok::Op((*op).to_string()));
272            continue;
273        }
274
275        if matches!(c, '(' | ')' | ',' | ';' | '.' | '[' | ']') {
276            out.push(Tok::Punct(c));
277            i += 1;
278            continue;
279        }
280
281        // Refused rather than skipped. Skipping an unknown character is how a
282        // parser silently reads a different query than the one it was given.
283        bail!("unexpected character {:?} in SQL", c);
284    }
285
286    out.push(Tok::Eof);
287    Ok(out)
288}
289
290// ─────────────────────────────────────────────────────────────────────────────
291// Phase 2 — the AST
292// ─────────────────────────────────────────────────────────────────────────────
293
294#[derive(Debug, Clone, PartialEq)]
295pub enum Expr {
296    /// `nspname` or `n.nspname`. The qualifier is kept because a join makes
297    /// bare names ambiguous, and resolving an ambiguous name by guessing is
298    /// how a query silently reads the wrong table's column.
299    Column { qual: Option<String>, name: String },
300    Literal(Value),
301    /// `*` in `count(*)`, and in a bare select list.
302    Star,
303    /// `alias.*`
304    QualifiedStar(String),
305    Func { name: String, args: Vec<Expr> },
306    /// Both SQL spellings:
307    ///   simple   — `CASE x WHEN 'r' THEN 'table' ... ELSE ... END`
308    ///   searched — `CASE WHEN x = 'r' THEN 'table' ... ELSE ... END`
309    /// psql's `\dt` uses the simple form with nine branches.
310    Case {
311        operand: Option<Box<Expr>>,
312        whens: Vec<(Expr, Expr)>,
313        else_: Option<Box<Expr>>,
314    },
315    Binary { op: String, left: Box<Expr>, right: Box<Expr> },
316    Unary { op: String, expr: Box<Expr> },
317    /// `x [NOT] IN (a, b, c)`
318    InList { expr: Box<Expr>, list: Vec<Expr>, negated: bool },
319    /// `x IS [NOT] NULL`
320    IsNull { expr: Box<Expr>, negated: bool },
321    /// `x::type` — the cast is PARSED and then ignored at evaluation, because
322    /// this engine is dynamically typed. Ignoring it is safe for the shapes
323    /// catalogue SQL uses (`prattrs::int2[]`), and the alternative — refusing
324    /// every cast — would reject queries whose result the cast cannot change.
325    Cast { expr: Box<Expr>, ty: String },
326    /// `(SELECT ...)` used as a VALUE: one column, at most one row. Postgres's
327    /// `\dT` hinges on one (`(SELECT c.relkind = 'c' FROM pg_class c WHERE
328    /// c.oid = t.typrelid)`), and `\d <table>` on three.
329    Subquery(Box<Select>),
330    /// `[NOT] EXISTS (SELECT ...)` — never NULL, which is why it is its own
331    /// variant rather than `Subquery IS NOT NULL`.
332    Exists { query: Box<Select>, negated: bool },
333    /// `ARRAY(SELECT ...)` — the first column of every row, as one array.
334    /// `\dp`, `\dT+`, `\dD` and `\dy` all build one and hand it to
335    /// `array_to_string`.
336    ArrayQuery(Box<Select>),
337    /// `x [NOT] IN (SELECT ...)` — `InList` semantics over the first column.
338    InSubquery { expr: Box<Expr>, query: Box<Select>, negated: bool },
339    /// `x op ANY (...)` / `x op SOME (...)` / `x op ALL (...)`. The right side
340    /// evaluates to an array — an `ArrayQuery` when it was written as a
341    /// subquery — and `op` is applied element by element.
342    Quantified { op: String, left: Box<Expr>, all: bool, right: Box<Expr> },
343    /// `arr[i]` — one-based, as Postgres subscripts are.
344    Index { expr: Box<Expr>, index: Box<Expr> },
345    /// `ARRAY[a, b, c]` — an array literal.
346    ArrayLit(Vec<Expr>),
347    /// An aggregate call: `count(*)`, `array_agg(x ORDER BY y)`,
348    /// `string_agg(DISTINCT s, ',')`.
349    ///
350    /// A variant of its own rather than a `Func` whose name happens to be in a
351    /// list. "Is this an aggregate?" was a string comparison repeated at five
352    /// sites — the select-list check, the pushdown walker, the join-key
353    /// walker, the folder and the evaluator — and five copies of one rule is
354    /// five chances to disagree about it. It is now a question about SHAPE.
355    ///
356    /// It also carries the two modifiers only an aggregate has, and which
357    /// SQLAlchemy's primary-key reflection depends on:
358    /// `array_agg(CAST(attname AS TEXT) ORDER BY ord)` is meaningless without
359    /// the ordering — the array IS the column list, in key order.
360    Agg {
361        name: String,
362        args: Vec<Expr>,
363        /// Sorts the rows of the GROUP before the values are collected.
364        order_by: Vec<OrderBy>,
365        distinct: bool,
366    },
367}
368
369#[derive(Debug, Clone, PartialEq)]
370pub struct SelectItem {
371    pub expr: Expr,
372    /// The name the client sees. `None` means it is derived from the
373    /// expression, the way Postgres derives it.
374    pub alias: Option<String>,
375}
376
377#[derive(Debug, Clone, Copy, PartialEq, Eq)]
378pub enum JoinKind { Inner, Left, Right, Full, Cross }
379
380#[derive(Debug, Clone, PartialEq)]
381pub struct TableRef {
382    /// The table name as written, minus quoting. A `pg_catalog.` qualifier is
383    /// preserved here and resolved by the caller, because `information_schema`
384    /// table names collide with plausible user collection names.
385    ///
386    /// For a derived table this is the literal `(subquery)`, and for a table
387    /// function it is the function's bare name — both only ever shown in a
388    /// plan, never resolved as a relation.
389    pub name: String,
390    pub alias: Option<String>,
391    /// `FROM (SELECT ...) AS t` — the relation is the subquery's output.
392    /// psql's `\dd` is one seven-arm `UNION ALL` wrapped exactly this way.
393    pub sub: Option<Box<Select>>,
394    /// `FROM generate_series(0, n) s` / `FROM unnest(arr) AS t(x)` — a table
395    /// function with its arguments. Evaluated in the enclosing row's scope,
396    /// because psql writes `unnest(evttags)` over the OUTER row's column.
397    pub args: Option<Vec<Expr>>,
398    /// `AS t(x, y)` — column aliases for a derived table or table function.
399    pub col_aliases: Vec<String>,
400    /// `LATERAL (SELECT ...)` — the derived table may read the FROM items
401    /// before it, so it is re-evaluated once per row of those. psql's `\dP+`
402    /// sizes each partitioned table this way.
403    pub lateral: bool,
404    /// `FROM orders AS OF SYSTEM TIME 42` — read this relation at that NEDB
405    /// sequence instead of at the tip.
406    ///
407    /// It hangs on the TABLE rather than on the query for two reasons. It is
408    /// where PostgreSQL's own grammar would take it (`relation_expr`, the
409    /// production `table_ref` is built from), and it is the only placement that
410    /// can express the query worth having: one relation AS OF a past sequence
411    /// joined against another at the tip, which is how you ask what changed.
412    ///
413    /// A sequence, never a wall-clock time. NEDB's history is
414    /// sequence-addressed and never garbage-collected, so a seq is exact where
415    /// a timestamp would be approximate — the same refusal the translator has
416    /// always made, made in the same words.
417    pub as_of: Option<u64>,
418    /// `FROM orders VALID AS OF '2026-01-01'` — bi-temporal: what was believed
419    /// TRUE as of that date, as distinct from what the log SAID at a sequence.
420    /// A date string, because application-time validity is a wall-clock notion
421    /// where system time is a sequence.
422    pub valid_as_of: Option<String>,
423    /// `FROM orders SEARCH 'acme'` — full-text over the document's fields.
424    ///
425    /// Per-table like the others, which is the point: one relation searched
426    /// and another joined to it is a sentence SQL can now say.
427    pub search: Option<String>,
428}
429
430impl TableRef {
431    /// A plain named relation.
432    pub fn named(name: impl Into<String>, alias: Option<String>) -> Self {
433        TableRef { name: name.into(), alias, sub: None, args: None, col_aliases: vec![], lateral: false, as_of: None, valid_as_of: None, search: None }
434    }
435
436    /// How this table's columns are addressed: the alias when given, else the
437    /// table's own bare name, which is what SQL says.
438    pub fn binding(&self) -> String {
439        self.alias.clone().unwrap_or_else(|| {
440            self.name.rsplit('.').next().unwrap_or(&self.name).to_string()
441        })
442    }
443}
444
445/// `UNION` / `INTERSECT` / `EXCEPT`.
446#[derive(Debug, Clone, Copy, PartialEq, Eq)]
447pub enum SetOp { Union, Intersect, Except }
448
449/// One further arm of a compound query: `<op> [ALL] SELECT ...`.
450#[derive(Debug, Clone, PartialEq)]
451pub struct SetArm {
452    pub op: SetOp,
453    pub all: bool,
454    pub query: Select,
455}
456
457#[derive(Debug, Clone, PartialEq)]
458pub struct Join {
459    pub kind: JoinKind,
460    pub table: TableRef,
461    pub on: Option<Expr>,
462}
463
464#[derive(Debug, Clone, Copy, PartialEq)]
465pub enum Dir { Asc, Desc }
466
467#[derive(Debug, Clone, PartialEq)]
468pub struct OrderBy {
469    /// `ORDER BY 1` is an ORDINAL into the select list, not the number 1.
470    /// psql's `\dt` ends with `ORDER BY 1,2`, so reading it as a constant
471    /// would silently produce an unordered listing.
472    pub ordinal: Option<usize>,
473    pub expr: Option<Expr>,
474    pub dir: Dir,
475    /// Postgres defaults NULLS LAST for ASC and NULLS FIRST for DESC.
476    pub nulls_first: bool,
477}
478
479#[derive(Debug, Clone, PartialEq)]
480pub struct Select {
481    pub distinct: bool,
482    pub items: Vec<SelectItem>,
483    pub from: Option<TableRef>,
484    pub joins: Vec<Join>,
485    pub where_: Option<Expr>,
486    /// `GROUP BY` keys. Empty means "one group of everything" when the select
487    /// list aggregates, and no grouping at all when it does not.
488    pub group_by: Vec<Expr>,
489    /// `HAVING` — a predicate over each GROUP, evaluated after its aggregates
490    /// are reduced. Distinct from `WHERE`, which filters rows before grouping.
491    pub having: Option<Expr>,
492    /// `ORDER BY` / `LIMIT` / `OFFSET`. On a compound query (`set_ops`
493    /// non-empty) these apply to the COMBINED result, as SQL says, and every
494    /// arm carries none of its own.
495    pub order_by: Vec<OrderBy>,
496    pub limit: Option<usize>,
497    pub offset: Option<usize>,
498    /// The further arms of a `UNION` / `INTERSECT` / `EXCEPT`. Empty for an
499    /// ordinary SELECT. This SELECT's own clauses are the FIRST arm.
500    pub set_ops: Vec<SetArm>,
501}
502
503impl Select {
504    /// Every base relation this query reads, at any depth: the FROM list,
505    /// the joins, derived tables, and every subquery inside an expression or
506    /// a further set-operation arm.
507    ///
508    /// The caller that routes a statement to this engine decides by relation
509    /// name, so a name buried three levels down inside `\dd`'s derived table
510    /// has to surface here or the statement is routed to a path that cannot
511    /// parse it — and reports a confusing error from that path.
512    pub fn base_relations(&self) -> Vec<String> {
513        let mut out = vec![];
514        self.collect_relations(&mut out);
515        out
516    }
517
518    fn collect_relations(&self, out: &mut Vec<String>) {
519        fn table(t: &TableRef, out: &mut Vec<String>) {
520            if let Some(sub) = &t.sub {
521                sub.collect_relations(out);
522            } else if let Some(args) = &t.args {
523                for a in args {
524                    expr(a, out);
525                }
526            } else {
527                out.push(t.name.clone());
528            }
529        }
530        fn expr(e: &Expr, out: &mut Vec<String>) {
531            match e {
532                Expr::Subquery(q) | Expr::ArrayQuery(q) => q.collect_relations(out),
533                Expr::Exists { query, .. } => query.collect_relations(out),
534                Expr::InSubquery { expr: x, query, .. } => {
535                    expr(x, out);
536                    query.collect_relations(out);
537                }
538                Expr::Quantified { left, right, .. } => {
539                    expr(left, out);
540                    expr(right, out);
541                }
542                Expr::Index { expr: x, index } => {
543                    expr(x, out);
544                    expr(index, out);
545                }
546                Expr::ArrayLit(items) | Expr::InList { list: items, .. } => {
547                    if let Expr::InList { expr: x, .. } = e {
548                        expr(x, out);
549                    }
550                    for i in items {
551                        expr(i, out);
552                    }
553                }
554                Expr::Func { args, .. } => {
555                    for a in args {
556                        expr(a, out);
557                    }
558                }
559                Expr::Agg { args, order_by, .. } => {
560                    for a in args {
561                        expr(a, out);
562                    }
563                    for ob in order_by {
564                        if let Some(e) = &ob.expr {
565                            expr(e, out);
566                        }
567                    }
568                }
569                Expr::Case { operand, whens, else_ } => {
570                    if let Some(o) = operand {
571                        expr(o, out);
572                    }
573                    for (w, t) in whens {
574                        expr(w, out);
575                        expr(t, out);
576                    }
577                    if let Some(x) = else_ {
578                        expr(x, out);
579                    }
580                }
581                Expr::Binary { left, right, .. } => {
582                    expr(left, out);
583                    expr(right, out);
584                }
585                Expr::Unary { expr: x, .. } | Expr::Cast { expr: x, .. } | Expr::IsNull { expr: x, .. } => {
586                    expr(x, out)
587                }
588                Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => {}
589            }
590        }
591        if let Some(f) = &self.from {
592            table(f, out);
593        }
594        for j in &self.joins {
595            table(&j.table, out);
596            if let Some(on) = &j.on {
597                expr(on, out);
598            }
599        }
600        for item in &self.items {
601            expr(&item.expr, out);
602        }
603        if let Some(w) = &self.where_ {
604            expr(w, out);
605        }
606        for g in &self.group_by {
607            expr(g, out);
608        }
609        if let Some(h) = &self.having {
610            expr(h, out);
611        }
612        for ob in &self.order_by {
613            if let Some(e) = &ob.expr {
614                expr(e, out);
615            }
616        }
617        for arm in &self.set_ops {
618            arm.query.collect_relations(out);
619        }
620    }
621}
622
623// ─────────────────────────────────────────────────────────────────────────────
624// Phase 2b — the parser
625// ─────────────────────────────────────────────────────────────────────────────
626
627/// Binding power for a binary operator. Higher binds tighter.
628///
629/// Written as a table rather than as nested recursive-descent functions so the
630/// precedence is READABLE and auditable in one place — a hand-rolled cascade
631/// is where operator precedence bugs hide, and a precedence bug in a WHERE
632/// clause silently returns the wrong rows.
633fn binding_power(op: &str) -> Option<u8> {
634    Some(match op {
635        "OR" => 1,
636        "AND" => 2,
637        // Comparison and pattern matching sit at the same level, and are
638        // non-associative in Postgres. Left association here is harmless
639        // because chaining them is a type error anyway.
640        "=" | "!=" | "<>" | "<" | "<=" | ">" | ">=" | "~" | "~*" | "!~" | "!~*"
641        | "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => 4,
642        "||" => 5,
643        "+" | "-" => 6,
644        "*" | "/" | "%" => 7,
645        _ => return None,
646    })
647}
648
649struct Parser {
650    toks: Vec<Tok>,
651    pos: usize,
652}
653
654impl Parser {
655    fn peek(&self) -> &Tok {
656        self.toks.get(self.pos).unwrap_or(&Tok::Eof)
657    }
658    fn peek_at(&self, n: usize) -> &Tok {
659        self.toks.get(self.pos + n).unwrap_or(&Tok::Eof)
660    }
661    fn next(&mut self) -> Tok {
662        let t = self.peek().clone();
663        self.pos += 1;
664        t
665    }
666    fn eat_kw(&mut self, kw: &str) -> bool {
667        if self.peek().is_kw(kw) {
668            self.pos += 1;
669            true
670        } else {
671            false
672        }
673    }
674    fn expect_kw(&mut self, kw: &str) -> Result<()> {
675        if self.eat_kw(kw) {
676            Ok(())
677        } else {
678            bail!("expected {} , got {:?}", kw, self.peek())
679        }
680    }
681    fn eat_punct(&mut self, c: char) -> bool {
682        if matches!(self.peek(), Tok::Punct(p) if *p == c) {
683            self.pos += 1;
684            true
685        } else {
686            false
687        }
688    }
689    fn expect_punct(&mut self, c: char) -> Result<()> {
690        if self.eat_punct(c) {
691            Ok(())
692        } else {
693            bail!("expected {:?}, got {:?}", c, self.peek())
694        }
695    }
696    fn eat_op(&mut self, op: &str) -> bool {
697        if matches!(self.peek(), Tok::Op(o) if o == op) {
698            self.pos += 1;
699            true
700        } else {
701            false
702        }
703    }
704
705    // ── expressions ─────────────────────────────────────────────────────────
706
707    fn parse_expr(&mut self) -> Result<Expr> {
708        self.parse_bin(0)
709    }
710
711    /// Precedence climbing. One loop, one table, no cascade of near-identical
712    /// functions to keep in sync.
713    fn parse_bin(&mut self, min_bp: u8) -> Result<Expr> {
714        let mut left = self.parse_unary()?;
715
716        loop {
717            // A word operator (AND / OR / LIKE / NOT LIKE) and a symbol
718            // operator are both binary here; normalise to one string.
719            // `OPERATOR(pg_catalog.~)` — Postgres's explicit operator
720            // qualification, which psql generates throughout `\d`. It names
721            // exactly the operator it wraps, so the schema is dropped and the
722            // symbol is used directly.
723            if self.peek().is_kw("OPERATOR") && matches!(self.peek_at(1), Tok::Punct('(')) {
724                let save = self.pos;
725                self.pos += 2;
726                // Skip any `schema.` qualification before the symbol.
727                let mut sym = None;
728                while sym.is_none() {
729                    match self.next() {
730                        Tok::Op(o) => sym = Some(o),
731                        Tok::Word { .. } | Tok::Punct('.') => continue,
732                        _ => break,
733                    }
734                }
735                match sym {
736                    Some(o) if binding_power(&o).is_some() && self.eat_punct(')') => {
737                        let bp = binding_power(&o).unwrap();
738                        if bp < min_bp {
739                            self.pos = save;
740                            break;
741                        }
742                        let right = self.parse_bin(bp + 1)?;
743                        left = Expr::Binary {
744                            op: o,
745                            left: Box::new(left),
746                            right: Box::new(right),
747                        };
748                        continue;
749                    }
750                    // Not an operator we know: rewind so the caller reports
751                    // the real position rather than a half-consumed clause.
752                    _ => {
753                        self.pos = save;
754                        break;
755                    }
756                }
757            }
758
759            let (op, width) = match self.peek() {
760                Tok::Op(o) if binding_power(o).is_some() => (o.clone(), 1usize),
761                Tok::Word { upper, .. } if upper == "AND" || upper == "OR" => (upper.clone(), 1),
762                Tok::Word { upper, .. } if upper == "LIKE" || upper == "ILIKE" => (upper.clone(), 1),
763                Tok::Word { upper, .. } if upper == "NOT" => {
764                    // `NOT LIKE` / `NOT ILIKE` / `NOT IN` / `NOT BETWEEN`.
765                    match self.peek_at(1) {
766                        Tok::Word { upper: u2, .. } if u2 == "LIKE" || u2 == "ILIKE" => {
767                            (format!("NOT {}", u2), 2)
768                        }
769                        _ => break,
770                    }
771                }
772                _ => break,
773            };
774
775            let bp = match binding_power(&op) {
776                Some(bp) if bp >= min_bp => bp,
777                _ => break,
778            };
779            self.pos += width;
780
781            // `x op ANY (...)` / `SOME` / `ALL` — a quantified comparison.
782            // psql's `\dp` writes `oid = ANY (polroles)`, `\dX` writes
783            // `'d' = any(es.stxkind)`. The right side is either an array value
784            // or a subquery, and the subquery form is read as ARRAY(SELECT)
785            // so one evaluator serves both.
786            let quant = match self.peek() {
787                Tok::Word { upper, .. }
788                    if matches!(upper.as_str(), "ANY" | "SOME" | "ALL")
789                        && matches!(self.peek_at(1), Tok::Punct('(')) =>
790                {
791                    Some(upper == "ALL")
792                }
793                _ => None,
794            };
795            if let Some(all) = quant {
796                self.pos += 2; // the word and the `(`
797                let right = if self.peek().is_kw("SELECT") {
798                    Expr::ArrayQuery(Box::new(self.parse_query()?))
799                } else {
800                    self.parse_expr()?
801                };
802                self.expect_punct(')')?;
803                left = Expr::Quantified { op, left: Box::new(left), all, right: Box::new(right) };
804                continue;
805            }
806
807            // Left-associative: the right side binds tighter than this level.
808            let right = self.parse_bin(bp + 1)?;
809            left = Expr::Binary { op, left: Box::new(left), right: Box::new(right) };
810        }
811
812        Ok(left)
813    }
814
815    fn parse_postfix(&mut self, mut e: Expr) -> Result<Expr> {
816        loop {
817            // `arr[i]` — a subscript. psql's publication query writes
818            // `prattrs[s]`.
819            if matches!(self.peek(), Tok::Punct('[')) {
820                self.pos += 1;
821                let index = self.parse_expr()?;
822                self.expect_punct(']')?;
823                e = Expr::Index { expr: Box::new(e), index: Box::new(index) };
824                continue;
825            }
826
827            // IS [NOT] NULL
828            if self.peek().is_kw("IS") {
829                self.pos += 1;
830                let negated = self.eat_kw("NOT");
831                // `IS [NOT] DISTINCT FROM` — the null-safe comparison, which
832                // psql's `\dconfig` uses. Never UNKNOWN: two NULLs are not
833                // distinct, a NULL and a value are.
834                if self.eat_kw("DISTINCT") {
835                    self.expect_kw("FROM")?;
836                    // Binds like a comparison: the operand is parsed above AND.
837                    let rhs = self.parse_bin(5)?;
838                    e = Expr::Binary {
839                        op: if negated { "IS NOT DISTINCT FROM".into() } else { "IS DISTINCT FROM".into() },
840                        left: Box::new(e),
841                        right: Box::new(rhs),
842                    };
843                    continue;
844                }
845                if !self.eat_kw("NULL") {
846                    // `IS TRUE` / `IS FALSE` are the other legal spellings.
847                    if self.eat_kw("TRUE") {
848                        e = Expr::Binary {
849                            op: "=".into(),
850                            left: Box::new(e),
851                            right: Box::new(Expr::Literal(Value::Bool(!negated))),
852                        };
853                        continue;
854                    }
855                    if self.eat_kw("FALSE") {
856                        e = Expr::Binary {
857                            op: "=".into(),
858                            left: Box::new(e),
859                            right: Box::new(Expr::Literal(Value::Bool(negated))),
860                        };
861                        continue;
862                    }
863                    bail!("expected NULL, TRUE or FALSE after IS, got {:?}", self.peek());
864                }
865                e = Expr::IsNull { expr: Box::new(e), negated };
866                continue;
867            }
868
869            // [NOT] IN (...)
870            let negated_in = if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("IN") {
871                self.pos += 2;
872                true
873            } else if self.peek().is_kw("IN") {
874                self.pos += 1;
875                false
876            } else {
877                // [NOT] BETWEEN a AND b
878                let negated_between =
879                    if self.peek().is_kw("NOT") && self.peek_at(1).is_kw("BETWEEN") {
880                        self.pos += 2;
881                        true
882                    } else if self.peek().is_kw("BETWEEN") {
883                        self.pos += 1;
884                        false
885                    } else {
886                        break;
887                    };
888                // BETWEEN's bounds bind tighter than AND, so the bounds are
889                // parsed at a level above AND — otherwise `BETWEEN a AND b`
890                // swallows the AND as a boolean operator.
891                let low = self.parse_bin(3)?;
892                self.expect_kw("AND")?;
893                let high = self.parse_bin(3)?;
894                let ge = Expr::Binary {
895                    op: ">=".into(),
896                    left: Box::new(e.clone()),
897                    right: Box::new(low),
898                };
899                let le = Expr::Binary {
900                    op: "<=".into(),
901                    left: Box::new(e),
902                    right: Box::new(high),
903                };
904                let both = Expr::Binary {
905                    op: "AND".into(),
906                    left: Box::new(ge),
907                    right: Box::new(le),
908                };
909                e = if negated_between {
910                    Expr::Unary { op: "NOT".into(), expr: Box::new(both) }
911                } else {
912                    both
913                };
914                continue;
915            };
916
917            self.expect_punct('(')?;
918            // `x IN (SELECT ...)` — the list is a subquery's first column.
919            if self.peek().is_kw("SELECT") {
920                let query = Box::new(self.parse_query()?);
921                self.expect_punct(')')?;
922                e = Expr::InSubquery { expr: Box::new(e), query, negated: negated_in };
923                continue;
924            }
925            let mut list = vec![];
926            if !self.eat_punct(')') {
927                loop {
928                    list.push(self.parse_expr()?);
929                    if self.eat_punct(',') {
930                        continue;
931                    }
932                    self.expect_punct(')')?;
933                    break;
934                }
935            }
936            e = Expr::InList { expr: Box::new(e), list, negated: negated_in };
937        }
938        Ok(e)
939    }
940
941    fn parse_unary(&mut self) -> Result<Expr> {
942        if self.peek().is_kw("NOT") {
943            self.pos += 1;
944            // NOT binds looser than comparison, so its operand is parsed at
945            // the comparison level: `NOT a = b` is `NOT (a = b)`.
946            let e = self.parse_bin(3)?;
947            return Ok(Expr::Unary { op: "NOT".into(), expr: Box::new(e) });
948        }
949        if self.eat_op("-") {
950            let e = self.parse_unary()?;
951            return Ok(Expr::Unary { op: "-".into(), expr: Box::new(e) });
952        }
953        if self.eat_op("+") {
954            return self.parse_unary();
955        }
956        let atom = self.parse_atom()?;
957        let cast = self.parse_casts(atom)?;
958        // Postfix forms (`IS NULL`, `IN (...)`, `BETWEEN a AND b`) bind to the
959        // OPERAND, before any binary operator is considered.
960        //
961        // They used to be applied after the binary loop in `parse_bin`, which
962        // meant that once `IN (...)` was consumed the loop had already exited
963        // and the rest of the predicate was left unparsed. psql's `\dt` is
964        // `WHERE c.relkind IN (...) AND n.nspname <> '...' AND ...`, so
965        // everything after the IN list silently became "trailing tokens" — and
966        // a WHERE clause that loses its later conjuncts returns TOO MANY rows,
967        // confidently.
968        self.parse_postfix(cast)
969    }
970
971    /// `expr::type`, possibly repeated and possibly `type[]`, and `COLLATE`.
972    fn parse_casts(&mut self, mut e: Expr) -> Result<Expr> {
973        loop {
974            // `COLLATE "C"` — psql writes it throughout `\d`. NEDB has one
975            // collation, so it cannot change the answer; it is consumed rather
976            // than refused, because refusing a clause that provably has no
977            // effect would reject a query whose result is already correct.
978            if self.peek().is_kw("COLLATE") {
979                self.pos += 1;
980                match self.next() {
981                    Tok::Word { .. } | Tok::Quoted(_) => {}
982                    other => bail!("expected a collation name after COLLATE, got {:?}", other),
983                }
984                // A schema-qualified collation: `pg_catalog."C"`.
985                while self.eat_punct('.') {
986                    match self.next() {
987                        Tok::Word { .. } | Tok::Quoted(_) => {}
988                        other => bail!("expected a name after '.', got {:?}", other),
989                    }
990                }
991                continue;
992            }
993            if !self.eat_op("::") {
994                break;
995            }
996            let mut ty = match self.next() {
997                Tok::Word { raw, .. } => raw,
998                Tok::Quoted(s) => s,
999                other => bail!("expected a type name after ::, got {:?}", other),
1000            };
1001            // A schema-qualified type: `pg_catalog.int2`.
1002            while self.eat_punct('.') {
1003                match self.next() {
1004                    Tok::Word { raw, .. } => ty = raw,
1005                    Tok::Quoted(s) => ty = s,
1006                    other => bail!("expected a type name after ., got {:?}", other),
1007                }
1008            }
1009            // An array type: `int2[]`.
1010            while self.eat_punct('[') {
1011                self.expect_punct(']')?;
1012                ty.push_str("[]");
1013            }
1014            e = Expr::Cast { expr: Box::new(e), ty };
1015        }
1016        Ok(e)
1017    }
1018
1019
1020    fn parse_atom(&mut self) -> Result<Expr> {
1021        // ( expr ) — or a SUBQUERY, which is named rather than reported as a
1022        // stray parenthesis.
1023        //
1024        // "expected ')', got SELECT" is a parser internal and tells the reader
1025        // nothing about what to change. `\d` and `\dp` both hinge on
1026        // subqueries, so this is the message somebody will actually read.
1027        if self.eat_punct('(') {
1028            // A scalar subquery. It may carry its own ORDER BY / LIMIT, and
1029            // may itself be a UNION, so it is a full query.
1030            if self.peek().is_kw("SELECT") {
1031                let q = self.parse_query()?;
1032                self.expect_punct(')')?;
1033                return Ok(Expr::Subquery(Box::new(q)));
1034            }
1035            let e = self.parse_expr()?;
1036            self.expect_punct(')')?;
1037            return Ok(e);
1038        }
1039
1040        // `ARRAY(SELECT ...)` and `ARRAY[a, b]`. The first appears throughout
1041        // psql's `\dp`, `\dT+`, `\dD` and `\dy`; it is a subquery wearing a
1042        // function's clothes, and its value is the first column of every row.
1043        if self.peek().is_kw("ARRAY") && matches!(self.peek_at(1), Tok::Punct('(') | Tok::Punct('[')) {
1044            self.pos += 1;
1045            if self.eat_punct('(') {
1046                if !self.peek().is_kw("SELECT") {
1047                    bail!("ARRAY(...) takes a subquery; for a list of values write ARRAY[...]");
1048                }
1049                let q = self.parse_query()?;
1050                self.expect_punct(')')?;
1051                return Ok(Expr::ArrayQuery(Box::new(q)));
1052            }
1053            self.expect_punct('[')?;
1054            let mut items = vec![];
1055            if !self.eat_punct(']') {
1056                loop {
1057                    items.push(self.parse_expr()?);
1058                    if self.eat_punct(',') {
1059                        continue;
1060                    }
1061                    self.expect_punct(']')?;
1062                    break;
1063                }
1064            }
1065            return Ok(Expr::ArrayLit(items));
1066        }
1067
1068        // `EXISTS (SELECT ...)`. `NOT EXISTS` arrives here through
1069        // `parse_unary`'s NOT and is wrapped there, which is correct because
1070        // EXISTS is never NULL and NOT of a boolean is exact.
1071        if self.peek().is_kw("EXISTS") && matches!(self.peek_at(1), Tok::Punct('(')) {
1072            self.pos += 2;
1073            if !self.peek().is_kw("SELECT") {
1074                bail!("EXISTS (...) takes a subquery");
1075            }
1076            let q = self.parse_query()?;
1077            self.expect_punct(')')?;
1078            return Ok(Expr::Exists { query: Box::new(q), negated: false });
1079        }
1080
1081        // `CAST(expr AS type)` — the standard spelling of `expr::type`, which
1082        // psql's `\dT+` and `\dd` both use. Recorded the same way.
1083        if self.peek().is_kw("CAST") && matches!(self.peek_at(1), Tok::Punct('(')) {
1084            self.pos += 2;
1085            let inner = self.parse_expr()?;
1086            self.expect_kw("AS")?;
1087            let mut ty = match self.next() {
1088                Tok::Word { raw, .. } => raw,
1089                Tok::Quoted(s) => s,
1090                other => bail!("expected a type name in CAST, got {:?}", other),
1091            };
1092            while self.eat_punct('.') {
1093                match self.next() {
1094                    Tok::Word { raw, .. } => ty = raw,
1095                    Tok::Quoted(s) => ty = s,
1096                    other => bail!("expected a type name after ., got {:?}", other),
1097                }
1098            }
1099            while self.eat_punct('[') {
1100                self.expect_punct(']')?;
1101                ty.push_str("[]");
1102            }
1103            self.expect_punct(')')?;
1104            return Ok(Expr::Cast { expr: Box::new(inner), ty });
1105        }
1106
1107        // CASE
1108        if self.peek().is_kw("CASE") {
1109            return self.parse_case();
1110        }
1111
1112        match self.next() {
1113            Tok::Num(n) => Ok(Expr::Literal(from_f64(n))),
1114            Tok::Str(s) => Ok(Expr::Literal(Value::String(s))),
1115            Tok::Op(o) if o == "*" => Ok(Expr::Star),
1116            Tok::Quoted(name) => self.parse_name_tail(None, name),
1117            Tok::Word { upper, raw } => match upper.as_str() {
1118                "NULL" => Ok(Expr::Literal(Value::Null)),
1119                "TRUE" => Ok(Expr::Literal(Value::Bool(true))),
1120                "FALSE" => Ok(Expr::Literal(Value::Bool(false))),
1121                // `CURRENT_SCHEMA` and friends are functions spelled without
1122                // parentheses. Treated as zero-argument calls so one evaluator
1123                // handles both spellings.
1124                "CURRENT_SCHEMA" | "CURRENT_DATABASE" | "CURRENT_USER" | "SESSION_USER"
1125                | "CURRENT_CATALOG" | "USER" | "VERSION"
1126                    if !matches!(self.peek(), Tok::Punct('(')) =>
1127                {
1128                    Ok(Expr::Func { name: upper.to_lowercase(), args: vec![] })
1129                }
1130                _ => self.parse_name_tail(None, raw),
1131            },
1132            other => bail!("unexpected {:?} in an expression", other),
1133        }
1134    }
1135
1136    /// After an identifier: `.more`, `(args)`, or nothing.
1137    ///
1138    /// This is where `pg_catalog.pg_get_userbyid(x)` and `n.nspname` and a
1139    /// bare `relname` all get told apart, and the rule is positional: the LAST
1140    /// dotted part before a `(` is the function name; before anything else it
1141    /// is the column, and the part before it is the qualifier.
1142    fn parse_name_tail(&mut self, _schema: Option<String>, first: String) -> Result<Expr> {
1143        let mut parts = vec![first];
1144        while self.eat_punct('.') {
1145            // `c.*`
1146            if self.eat_op("*") {
1147                return Ok(Expr::QualifiedStar(parts.pop().unwrap_or_default()));
1148            }
1149            match self.next() {
1150                Tok::Word { raw, .. } => parts.push(raw),
1151                Tok::Quoted(s) => parts.push(s),
1152                other => bail!("expected a name after '.', got {:?}", other),
1153            }
1154        }
1155
1156        // A call: the last part is the function, any earlier parts are its
1157        // schema and are dropped — `pg_catalog.pg_get_userbyid` is the same
1158        // function as `pg_get_userbyid`.
1159        if matches!(self.peek(), Tok::Punct('(')) {
1160            self.pos += 1;
1161            let name = parts.pop().unwrap_or_default().to_lowercase();
1162            let agg = is_aggregate(&name);
1163            // `count(DISTINCT x)` — only legal on an aggregate.
1164            let distinct = agg && self.eat_kw("DISTINCT");
1165            let mut args = vec![];
1166            let mut order_by = vec![];
1167            if !self.eat_punct(')') {
1168                loop {
1169                    // `count(*)`
1170                    if self.eat_op("*") {
1171                        args.push(Expr::Star);
1172                    } else {
1173                        args.push(self.parse_expr()?);
1174                    }
1175                    if self.eat_punct(',') {
1176                        continue;
1177                    }
1178                    // `array_agg(x ORDER BY y DESC)` — the aggregate's own
1179                    // ordering, INSIDE the call. Without it SQLAlchemy's
1180                    // primary-key reflection does not parse.
1181                    if agg && self.peek().is_kw("ORDER") {
1182                        self.pos += 1;
1183                        self.expect_kw("BY")?;
1184                        order_by = self.parse_sort_list()?;
1185                    }
1186                    self.expect_punct(')')?;
1187                    break;
1188                }
1189            }
1190            if agg {
1191                return Ok(Expr::Agg { name, args, order_by, distinct });
1192            }
1193            return Ok(Expr::Func { name, args });
1194        }
1195
1196        let name = parts.pop().unwrap_or_default();
1197        // Only the IMMEDIATE qualifier matters: in `public.orders.id` the
1198        // binding is `orders`, and the schema is not part of how a column is
1199        // addressed.
1200        let qual = parts.pop();
1201        Ok(Expr::Column { qual, name })
1202    }
1203
1204    fn parse_case(&mut self) -> Result<Expr> {
1205        self.expect_kw("CASE")?;
1206        // A simple CASE has an operand; a searched CASE goes straight to WHEN.
1207        let operand = if self.peek().is_kw("WHEN") {
1208            None
1209        } else {
1210            Some(Box::new(self.parse_expr()?))
1211        };
1212        let mut whens = vec![];
1213        while self.eat_kw("WHEN") {
1214            let cond = self.parse_expr()?;
1215            self.expect_kw("THEN")?;
1216            let then = self.parse_expr()?;
1217            whens.push((cond, then));
1218        }
1219        if whens.is_empty() {
1220            bail!("CASE needs at least one WHEN branch");
1221        }
1222        let else_ = if self.eat_kw("ELSE") {
1223            Some(Box::new(self.parse_expr()?))
1224        } else {
1225            None
1226        };
1227        self.expect_kw("END")?;
1228        Ok(Expr::Case { operand, whens, else_ })
1229    }
1230
1231    // ── the statement ───────────────────────────────────────────────────────
1232
1233    fn parse_table_ref(&mut self) -> Result<TableRef> {
1234        let lateral = self.eat_kw("LATERAL");
1235        // `( SELECT ... ) AS t` — a derived table. psql's `\dd` is one.
1236        if self.eat_punct('(') {
1237            if !self.peek().is_kw("SELECT") {
1238                bail!("expected a subquery after '(' in FROM, got {:?}", self.peek());
1239            }
1240            let sub = self.parse_query()?;
1241            self.expect_punct(')')?;
1242            let (alias, col_aliases) = self.parse_table_alias()?;
1243            if alias.is_none() {
1244                bail!("a subquery in FROM must have an alias");
1245            }
1246            return Ok(TableRef {
1247                name: "(subquery)".into(),
1248                alias,
1249                sub: Some(Box::new(sub)),
1250                args: None,
1251                col_aliases,
1252                lateral,
1253                as_of: None,
1254                valid_as_of: None,
1255                search: None,
1256            });
1257        }
1258        if lateral {
1259            bail!("LATERAL applies to a subquery in FROM; write LATERAL (SELECT ...)");
1260        }
1261
1262        let mut parts = vec![match self.next() {
1263            Tok::Word { raw, .. } => raw,
1264            Tok::Quoted(s) => s,
1265            other => bail!("expected a table name, got {:?}", other),
1266        }];
1267        while self.eat_punct('.') {
1268            match self.next() {
1269                Tok::Word { raw, .. } => parts.push(raw),
1270                Tok::Quoted(s) => parts.push(s),
1271                other => bail!("expected a name after '.', got {:?}", other),
1272            }
1273        }
1274        let name = parts.join(".");
1275
1276        // `generate_series(0, n) s` / `unnest(arr) AS t(x)` — a table
1277        // function. The schema qualification is dropped, as for scalar calls.
1278        if self.eat_punct('(') {
1279            let mut args = vec![];
1280            if !self.eat_punct(')') {
1281                loop {
1282                    args.push(self.parse_expr()?);
1283                    if self.eat_punct(',') {
1284                        continue;
1285                    }
1286                    self.expect_punct(')')?;
1287                    break;
1288                }
1289            }
1290            let fname = name.rsplit('.').next().unwrap_or(&name).to_lowercase();
1291            let (alias, col_aliases) = self.parse_table_alias()?;
1292            return Ok(TableRef { name: fname, alias, sub: None, args: Some(args), col_aliases, lateral: false, as_of: None, valid_as_of: None, search: None });
1293        }
1294
1295        // `AS OF SYSTEM TIME <seq>` is read BEFORE the alias, because `AS` is
1296        // the first token of both this and `AS <alias>`. The word after `AS`
1297        // decides which one it is, and `parse_table_alias` would otherwise
1298        // consume `OF` as the alias and leave `SYSTEM TIME 42` in the stream.
1299        let as_of = if self.peek().is_kw("AS") && self.peek_at(1).is_kw("OF") {
1300            self.next();
1301            self.next();
1302            self.expect_kw("SYSTEM")?;
1303            self.expect_kw("TIME")?;
1304            match self.next() {
1305                Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Some(n as u64),
1306                other => bail!(
1307                    "AS OF SYSTEM TIME takes a NEDB sequence number here, not a timestamp \
1308                     (got {:?}). NEDB's history is sequence-addressed and never \
1309                     garbage-collected, so a seq is exact where a wall-clock time would be \
1310                     approximate",
1311                    other
1312                ),
1313            }
1314        } else {
1315            None
1316        };
1317        // ── NQL's own verbs, as table-level qualifiers ──────────────────────
1318        //
1319        // This is what "NQL folded into neSQL" means concretely: the verbs NQL
1320        // has and SQL has no spelling for become qualifiers on the relation,
1321        // so a SQL statement can SAY them and the SQL evaluator can compose
1322        // joins, subqueries and set operations AROUND them. The NQL engine
1323        // still executes them — the resolver renders them straight back into
1324        // the NQL it asks for — so there is one implementation, not two.
1325        //
1326        // Both are UNRESERVED, and deliberately: `VALID` only starts a clause
1327        // when `AS OF` follows, and `SEARCH` only when a string literal
1328        // follows. So a collection aliased `search`, or a column named
1329        // `valid`, keeps working — the same discipline the PostgreSQL grammar
1330        // uses with `unreserved_keyword`, and the reason adding a verb does
1331        // not break somebody's existing data.
1332        let valid_as_of = if self.peek().is_kw("VALID")
1333            && self.peek_at(1).is_kw("AS")
1334            && self.peek_at(2).is_kw("OF")
1335        {
1336            self.next();
1337            self.next();
1338            self.next();
1339            match self.next() {
1340                Tok::Str(s) => Some(s),
1341                other => bail!(
1342                    "VALID AS OF takes a date string here (got {:?}). System time is a \
1343                     sequence and application-time validity is a date — they are different \
1344                     questions, so they take different arguments",
1345                    other
1346                ),
1347            }
1348        } else {
1349            None
1350        };
1351
1352        let search = if self.peek().is_kw("SEARCH") && matches!(self.peek_at(1), Tok::Str(_)) {
1353            self.next();
1354            match self.next() {
1355                Tok::Str(s) => Some(s),
1356                // Unreachable given the lookahead above, but an unreachable
1357                // branch that bails is cheaper than one that panics.
1358                other => bail!("SEARCH takes a string here, got {:?}", other),
1359            }
1360        } else {
1361            None
1362        };
1363
1364        let (alias, col_aliases) = self.parse_table_alias()?;
1365        Ok(TableRef { name, alias, sub: None, args: None, col_aliases, lateral: false, as_of, valid_as_of, search })
1366    }
1367
1368    /// `AS alias`, or a bare alias, optionally followed by `(col, col)`.
1369    ///
1370    /// A bare alias must not swallow a keyword that starts the next clause,
1371    /// or `FROM t WHERE x` reads `t` aliased as `WHERE`.
1372    fn parse_table_alias(&mut self) -> Result<(Option<String>, Vec<String>)> {
1373        let alias = if self.eat_kw("AS") {
1374            match self.next() {
1375                Tok::Word { raw, .. } => Some(raw),
1376                Tok::Quoted(s) => Some(s),
1377                other => bail!("expected an alias after AS, got {:?}", other),
1378            }
1379        } else {
1380            match self.peek().clone() {
1381                Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1382                    self.pos += 1;
1383                    Some(raw)
1384                }
1385                Tok::Quoted(s) => {
1386                    self.pos += 1;
1387                    Some(s)
1388                }
1389                _ => None,
1390            }
1391        };
1392        let mut col_aliases = vec![];
1393        if alias.is_some() && self.eat_punct('(') {
1394            loop {
1395                match self.next() {
1396                    Tok::Word { raw, .. } => col_aliases.push(raw),
1397                    Tok::Quoted(s) => col_aliases.push(s),
1398                    other => bail!("expected a column alias, got {:?}", other),
1399                }
1400                if self.eat_punct(',') {
1401                    continue;
1402                }
1403                self.expect_punct(')')?;
1404                break;
1405            }
1406        }
1407        Ok((alias, col_aliases))
1408    }
1409
1410    /// A full query: one or more SELECT bodies joined by set operations, then
1411    /// the ORDER BY / LIMIT / OFFSET that apply to the whole.
1412    ///
1413    /// The tail is parsed HERE and not in the body, because after `a UNION b
1414    /// ORDER BY 1` the ORDER BY sorts the union — attaching it to `b` would
1415    /// sort one arm and leave the result unordered while reporting success.
1416    fn parse_query(&mut self) -> Result<Select> {
1417        let mut first = self.parse_select_body()?;
1418        loop {
1419            let op = if self.eat_kw("UNION") {
1420                SetOp::Union
1421            } else if self.eat_kw("INTERSECT") {
1422                SetOp::Intersect
1423            } else if self.eat_kw("EXCEPT") {
1424                SetOp::Except
1425            } else {
1426                break;
1427            };
1428            let all = self.eat_kw("ALL");
1429            if !all {
1430                let _ = self.eat_kw("DISTINCT");
1431            }
1432            // A parenthesised arm: `UNION (SELECT ...)`.
1433            let query = if self.eat_punct('(') {
1434                let q = self.parse_query()?;
1435                self.expect_punct(')')?;
1436                q
1437            } else {
1438                self.parse_select_body()?
1439            };
1440            first.set_ops.push(SetArm { op, all, query });
1441        }
1442        self.parse_query_tail(&mut first)?;
1443        Ok(first)
1444    }
1445
1446    /// A comma-separated sort list — shared by the query tail and by an
1447    /// aggregate's own `ORDER BY`, so the two cannot disagree about how a
1448    /// sort key, a direction or a NULLS placement is spelled.
1449    fn parse_sort_list(&mut self) -> Result<Vec<OrderBy>> {
1450        let mut out = vec![];
1451        loop {
1452            // `ORDER BY 1` is an ORDINAL into the select list, not the
1453            // literal 1. Reading it as a constant sorts every row equally
1454            // and silently yields an unordered result.
1455            let (ordinal, expr) = match self.peek().clone() {
1456                Tok::Num(n)
1457                    if n.fract() == 0.0
1458                        && n >= 1.0
1459                        && !matches!(self.peek_at(1), Tok::Op(_)) =>
1460                {
1461                    self.pos += 1;
1462                    (Some(n as usize), None)
1463                }
1464                _ => (None, Some(self.parse_expr()?)),
1465            };
1466            let dir = if self.eat_kw("DESC") {
1467                Dir::Desc
1468            } else {
1469                let _ = self.eat_kw("ASC");
1470                Dir::Asc
1471            };
1472            // Postgres defaults NULLS LAST for ASC, NULLS FIRST for DESC.
1473            let mut nulls_first = matches!(dir, Dir::Desc);
1474            if self.eat_kw("NULLS") {
1475                if self.eat_kw("FIRST") {
1476                    nulls_first = true;
1477                } else if self.eat_kw("LAST") {
1478                    nulls_first = false;
1479                } else {
1480                    bail!("expected FIRST or LAST after NULLS, got {:?}", self.peek());
1481                }
1482            }
1483            out.push(OrderBy { ordinal, expr, dir, nulls_first });
1484            if self.eat_punct(',') {
1485                continue;
1486            }
1487            break;
1488        }
1489        Ok(out)
1490    }
1491
1492    fn parse_query_tail(&mut self, sel: &mut Select) -> Result<()> {
1493        let mut order_by = vec![];
1494        if self.eat_kw("ORDER") {
1495            self.expect_kw("BY")?;
1496            order_by = self.parse_sort_list()?;
1497        }
1498
1499        let mut limit = None;
1500        let mut offset = None;
1501        // Either order, and either may appear alone.
1502        loop {
1503            if self.eat_kw("LIMIT") {
1504                if self.eat_kw("ALL") {
1505                    limit = None;
1506                } else {
1507                    limit = Some(self.parse_count("LIMIT")?);
1508                }
1509                continue;
1510            }
1511            if self.eat_kw("OFFSET") {
1512                offset = Some(self.parse_count("OFFSET")?);
1513                let _ = self.eat_kw("ROW") || self.eat_kw("ROWS");
1514                continue;
1515            }
1516            break;
1517        }
1518        sel.order_by = order_by;
1519        sel.limit = limit;
1520        sel.offset = offset;
1521        Ok(())
1522    }
1523
1524    /// One `SELECT ... FROM ... WHERE ...` body, without the query tail.
1525    fn parse_select_body(&mut self) -> Result<Select> {
1526        self.expect_kw("SELECT")?;
1527        let distinct = self.eat_kw("DISTINCT");
1528        if distinct && self.peek().is_kw("ON") {
1529            bail!("DISTINCT ON is not supported");
1530        }
1531        let _ = self.eat_kw("ALL");
1532
1533        let mut items = vec![];
1534        loop {
1535            let expr = self.parse_expr()?;
1536            // `AS "Name"`, or a bare alias that is not a clause keyword.
1537            let alias = if self.eat_kw("AS") {
1538                match self.next() {
1539                    Tok::Word { raw, .. } => Some(raw),
1540                    Tok::Quoted(s) => Some(s),
1541                    other => bail!("expected an alias after AS, got {:?}", other),
1542                }
1543            } else {
1544                match self.peek().clone() {
1545                    Tok::Word { upper, raw } if !is_clause_keyword(&upper) => {
1546                        self.pos += 1;
1547                        Some(raw)
1548                    }
1549                    Tok::Quoted(s) => {
1550                        self.pos += 1;
1551                        Some(s)
1552                    }
1553                    _ => None,
1554                }
1555            };
1556            items.push(SelectItem { expr, alias });
1557            if self.eat_punct(',') {
1558                continue;
1559            }
1560            break;
1561        }
1562
1563        let mut from = None;
1564        let mut joins = vec![];
1565        if self.eat_kw("FROM") {
1566            from = Some(self.parse_table_ref()?);
1567            loop {
1568                // A comma-separated FROM item is an implicit CROSS JOIN, and
1569                // it may INTERLEAVE with explicit joins: psql's `\dF+` writes
1570                // `FROM c LEFT JOIN n ON ..., p LEFT JOIN np ON ...`. Reading
1571                // the commas first and the joins second would parse that as
1572                // trailing tokens.
1573                if self.eat_punct(',') {
1574                    let table = self.parse_table_ref()?;
1575                    joins.push(Join { kind: JoinKind::Cross, table, on: None });
1576                    continue;
1577                }
1578                let kind = if self.peek().is_kw("JOIN") {
1579                    self.pos += 1;
1580                    JoinKind::Inner
1581                } else if self.peek().is_kw("INNER") && self.peek_at(1).is_kw("JOIN") {
1582                    self.pos += 2;
1583                    JoinKind::Inner
1584                } else if self.peek().is_kw("CROSS") && self.peek_at(1).is_kw("JOIN") {
1585                    self.pos += 2;
1586                    JoinKind::Cross
1587                } else if self.peek().is_kw("LEFT") {
1588                    self.pos += 1;
1589                    let _ = self.eat_kw("OUTER");
1590                    self.expect_kw("JOIN")?;
1591                    JoinKind::Left
1592                } else if self.peek().is_kw("RIGHT") {
1593                    self.pos += 1;
1594                    let _ = self.eat_kw("OUTER");
1595                    self.expect_kw("JOIN")?;
1596                    JoinKind::Right
1597                } else if self.peek().is_kw("FULL") {
1598                    self.pos += 1;
1599                    let _ = self.eat_kw("OUTER");
1600                    self.expect_kw("JOIN")?;
1601                    JoinKind::Full
1602                } else {
1603                    break;
1604                };
1605                let table = self.parse_table_ref()?;
1606                let on = if self.eat_kw("ON") {
1607                    Some(self.parse_expr()?)
1608                } else if self.peek().is_kw("USING") {
1609                    bail!("JOIN ... USING is not supported — write ON a.col = b.col");
1610                } else {
1611                    None
1612                };
1613                if on.is_none() && !matches!(kind, JoinKind::Cross) {
1614                    bail!("a {:?} JOIN needs an ON clause", kind);
1615                }
1616                joins.push(Join { kind, table, on });
1617            }
1618        }
1619
1620        let where_ = if self.eat_kw("WHERE") {
1621            Some(self.parse_expr()?)
1622        } else {
1623            None
1624        };
1625
1626        let mut group_by = vec![];
1627        if self.eat_kw("GROUP") {
1628            self.expect_kw("BY")?;
1629            if self.eat_kw("ALL") || self.eat_kw("DISTINCT") {
1630                bail!("GROUP BY ALL / DISTINCT is not supported — list the keys");
1631            }
1632            loop {
1633                if self.peek().is_kw("ROLLUP")
1634                    || self.peek().is_kw("CUBE")
1635                    || self.peek().is_kw("GROUPING")
1636                {
1637                    bail!("GROUP BY ROLLUP / CUBE / GROUPING SETS is not supported");
1638                }
1639                group_by.push(self.parse_expr()?);
1640                if self.eat_punct(',') {
1641                    continue;
1642                }
1643                break;
1644            }
1645        }
1646
1647        let having = if self.eat_kw("HAVING") {
1648            Some(self.parse_expr()?)
1649        } else {
1650            None
1651        };
1652        if having.is_some() && group_by.is_empty() && !items.iter().any(|i| has_aggregate(&i.expr))
1653        {
1654            bail!("HAVING needs a GROUP BY or an aggregate — it filters groups, not rows; \
1655                   use WHERE to filter rows");
1656        }
1657
1658        Ok(Select {
1659            distinct,
1660            items,
1661            from,
1662            joins,
1663            where_,
1664            group_by,
1665            having,
1666            order_by: vec![],
1667            limit: None,
1668            offset: None,
1669            set_ops: vec![],
1670        })
1671    }
1672
1673    fn parse_count(&mut self, what: &str) -> Result<usize> {
1674        match self.next() {
1675            Tok::Num(n) if n >= 0.0 && n.fract() == 0.0 => Ok(n as usize),
1676            other => bail!("{} expects a non-negative integer, got {:?}", what, other),
1677        }
1678    }
1679}
1680
1681/// Keywords that begin a clause, and so can never be a bare alias.
1682///
1683/// Without this, `FROM pg_class WHERE x` parses `pg_class` aliased as
1684/// `WHERE` — and then the predicate vanishes and every row comes back.
1685fn is_clause_keyword(upper: &str) -> bool {
1686    matches!(
1687        upper,
1688        "FROM" | "WHERE" | "GROUP" | "HAVING" | "ORDER" | "LIMIT" | "OFFSET"
1689            | "JOIN" | "LEFT" | "RIGHT" | "FULL" | "INNER" | "CROSS" | "OUTER"
1690            | "ON" | "USING" | "AND" | "OR" | "AS" | "UNION" | "INTERSECT"
1691            | "EXCEPT" | "FETCH" | "FOR" | "WINDOW" | "RETURNING" | "INTO"
1692            | "ASC" | "DESC" | "NULLS" | "IS" | "IN" | "NOT" | "LIKE" | "ILIKE"
1693            | "BETWEEN" | "THEN" | "WHEN" | "ELSE" | "END" | "CASE" | "DISTINCT"
1694            | "SELECT" | "WITH" | "ALL"
1695    )
1696}
1697
1698/// Parse one `SELECT` statement — possibly a compound one.
1699pub fn parse(sql: &str) -> Result<Select> {
1700    let toks = lex(sql)?;
1701    let mut p = Parser { toks, pos: 0 };
1702    // A statement wrapped in parentheses: `(SELECT ...) UNION (SELECT ...)`.
1703    let sel = if matches!(p.peek(), Tok::Punct('(')) && p.peek_at(1).is_kw("SELECT") {
1704        p.pos += 1;
1705        let mut first = p.parse_query()?;
1706        p.expect_punct(')')?;
1707        // Set operations may follow the parenthesised head.
1708        loop {
1709            let op = if p.eat_kw("UNION") {
1710                SetOp::Union
1711            } else if p.eat_kw("INTERSECT") {
1712                SetOp::Intersect
1713            } else if p.eat_kw("EXCEPT") {
1714                SetOp::Except
1715            } else {
1716                break;
1717            };
1718            let all = p.eat_kw("ALL");
1719            if !all {
1720                let _ = p.eat_kw("DISTINCT");
1721            }
1722            let query = if p.eat_punct('(') {
1723                let q = p.parse_query()?;
1724                p.expect_punct(')')?;
1725                q
1726            } else {
1727                p.parse_select_body()?
1728            };
1729            first.set_ops.push(SetArm { op, all, query });
1730        }
1731        p.parse_query_tail(&mut first)?;
1732        first
1733    } else {
1734        p.parse_query()?
1735    };
1736    let _ = p.eat_punct(';');
1737    if !matches!(p.peek(), Tok::Eof) {
1738        bail!("unexpected trailing tokens: {:?}", p.peek());
1739    }
1740    Ok(sel)
1741}
1742
1743// ─────────────────────────────────────────────────────────────────────────────
1744// Phase 3 — the evaluator
1745// ─────────────────────────────────────────────────────────────────────────────
1746
1747/// One row of a (possibly joined) result: an ordered list of
1748/// `(binding, row-or-NULL)`.
1749///
1750/// `None` is a LEFT JOIN's unmatched side. Keeping it as `None` rather than an
1751/// empty map is what makes `n.nspname IS NULL` answer correctly for a row
1752/// that had no match — an empty map would report the column as absent, which
1753/// looks identical but loses the distinction between "no such column" and "no
1754/// matching row".
1755pub struct Bound<'a> {
1756    pub parts: Vec<(String, Option<&'a Value>)>,
1757    /// What this row may reach beyond itself — see [`EvalCtx`].
1758    pub ctx: EvalCtx<'a>,
1759}
1760
1761/// What an expression may reach beyond its own row.
1762///
1763/// Both fields exist for subqueries. The resolver is what lets a subquery
1764/// RUN from inside an expression, and `outer` is the enclosing query's row, so
1765/// `WHERE attrelid = c.oid` inside `ARRAY(SELECT ... FROM pg_attribute a ...)`
1766/// can see `c` — a correlated subquery, which is what every one of psql's
1767/// subqueries is.
1768///
1769/// Scoping follows SQL: a name resolves in the innermost query that binds it,
1770/// and only then in the enclosing one. That matters for the bare column in
1771/// `\dp`'s `WHERE oid = ANY (polroles)`: `oid` is the inner `pg_roles`
1772/// row's, `polroles` is the outer `pg_policy` row's, and neither is qualified.
1773#[derive(Clone, Copy, Default)]
1774pub struct EvalCtx<'a> {
1775    pub resolver: Option<&'a Resolver<'a>>,
1776    pub outer: Option<&'a Bound<'a>>,
1777}
1778
1779impl<'a> Bound<'a> {
1780    /// A row with no enclosing scope and no way to run a subquery.
1781    pub fn new(parts: Vec<(String, Option<&'a Value>)>) -> Self {
1782        Bound { parts, ctx: EvalCtx::default() }
1783    }
1784
1785    /// Resolve a column reference.
1786    ///
1787    /// A qualified name looks only at its own binding, then at the enclosing
1788    /// query's. A bare name scans the bindings in order and takes the first
1789    /// that actually HAS the key — which is how SQL resolves an unambiguous
1790    /// bare column across a join — and falls back to the enclosing query.
1791    fn column(&self, qual: Option<&str>, name: &str) -> Value {
1792        match qual {
1793            Some(q) => {
1794                for (binding, row) in &self.parts {
1795                    if binding.eq_ignore_ascii_case(q) {
1796                        return row
1797                            .and_then(|r| r.get(name))
1798                            .cloned()
1799                            .unwrap_or(Value::Null);
1800                    }
1801                }
1802                match self.ctx.outer {
1803                    Some(o) if o.has_binding(q) => o.column(qual, name),
1804                    _ => Value::Null,
1805                }
1806            }
1807            None => {
1808                for (_, row) in &self.parts {
1809                    if let Some(v) = row.and_then(|r| r.get(name)) {
1810                        return v.clone();
1811                    }
1812                }
1813                match self.ctx.outer {
1814                    Some(o) => o.column(None, name),
1815                    None => Value::Null,
1816                }
1817            }
1818        }
1819    }
1820
1821    /// Is `qual` a binding in this row — or in an enclosing query's row — at
1822    /// all? Used to tell "unknown table alias" (a query bug, worth an error)
1823    /// from "column absent in this row" (ordinary schemaless behaviour, worth
1824    /// a NULL).
1825    fn has_binding(&self, qual: &str) -> bool {
1826        self.parts.iter().any(|(b, _)| b.eq_ignore_ascii_case(qual))
1827            || self.ctx.outer.is_some_and(|o| o.has_binding(qual))
1828    }
1829
1830    /// Every column of every bound row, for `SELECT *`.
1831    fn flatten(&self) -> Vec<(String, Value)> {
1832        let mut out = vec![];
1833        for (_, row) in &self.parts {
1834            if let Some(Value::Object(m)) = row {
1835                for (k, v) in m {
1836                    out.push((k.clone(), v.clone()));
1837                }
1838            }
1839        }
1840        out
1841    }
1842
1843    fn flatten_binding(&self, qual: &str) -> Vec<(String, Value)> {
1844        let mut out = vec![];
1845        for (binding, row) in &self.parts {
1846            if binding.eq_ignore_ascii_case(qual) {
1847                if let Some(Value::Object(m)) = row {
1848                    for (k, v) in m {
1849                        out.push((k.clone(), v.clone()));
1850                    }
1851                }
1852            }
1853        }
1854        out
1855    }
1856}
1857
1858/// SQL truth: three-valued. `None` is UNKNOWN.
1859///
1860/// This is not pedantry. A LEFT JOIN produces NULL columns, and a predicate
1861/// over NULL must be UNKNOWN rather than false — because `NOT UNKNOWN` is
1862/// UNKNOWN, not true. Collapsing UNKNOWN to false would make
1863/// `WHERE NOT (n.nspname = 'x')` include unmatched rows that Postgres
1864/// excludes, and the row counts would silently disagree.
1865type Truth = Option<bool>;
1866
1867fn truthy(v: &Value) -> Truth {
1868    match v {
1869        Value::Null => None,
1870        Value::Bool(b) => Some(*b),
1871        // A predicate position holding a non-boolean is a query error in
1872        // Postgres. Being lenient here would let `WHERE 1` mean something
1873        // different than it does there, so it is treated as UNKNOWN.
1874        _ => None,
1875    }
1876}
1877
1878/// Compare two values for ordering and equality.
1879///
1880/// Numbers compare numerically, strings lexicographically, booleans false <
1881/// true. A number and a numeric-looking string compare NUMERICALLY, because
1882/// catalogue rows carry oids as numbers while a client may quote them.
1883fn cmp_values(a: &Value, b: &Value) -> Option<std::cmp::Ordering> {
1884    use std::cmp::Ordering;
1885    match (a, b) {
1886        (Value::Null, _) | (_, Value::Null) => None,
1887        (Value::Number(x), Value::Number(y)) => {
1888            x.as_f64().partial_cmp(&y.as_f64())
1889        }
1890        (Value::String(x), Value::String(y)) => Some(x.cmp(y)),
1891        (Value::Bool(x), Value::Bool(y)) => Some(x.cmp(y)),
1892        // Mixed number/string: try numeric first, then fall back to text, so
1893        // `oid = '16384'` behaves the way a Postgres client expects.
1894        (Value::Number(x), Value::String(y)) => match y.parse::<f64>() {
1895            Ok(n) => x.as_f64().partial_cmp(&Some(n)),
1896            Err(_) => Some(as_text(a).cmp(&as_text(b))),
1897        },
1898        (Value::String(x), Value::Number(y)) => match x.parse::<f64>() {
1899            Ok(n) => Some(n).partial_cmp(&y.as_f64()),
1900            Err(_) => Some(as_text(a).cmp(&as_text(b))),
1901        },
1902        _ => {
1903            let (x, y) = (as_text(a), as_text(b));
1904            if x == y { Some(Ordering::Equal) } else { Some(x.cmp(&y)) }
1905        }
1906    }
1907}
1908
1909/// The text a user sees for a value — not its JSON encoding.
1910fn as_text(v: &Value) -> String {
1911    match v {
1912        Value::String(s) => s.clone(),
1913        Value::Null => String::new(),
1914        Value::Bool(b) => (if *b { "t" } else { "f" }).to_string(),
1915        // Postgres's array text form, so `polroles <> '{0}'` compares like
1916        // for like and `arr::text` reads as a client expects.
1917        Value::Array(items) => {
1918            let inner: Vec<String> = items
1919                .iter()
1920                .map(|i| match i {
1921                    Value::Null => "NULL".to_string(),
1922                    Value::String(s) if s.is_empty()
1923                        || s.chars().any(|c| c.is_whitespace() || matches!(c, ',' | '{' | '}' | '"' | '\\')) =>
1924                    {
1925                        format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
1926                    }
1927                    other => as_text(other),
1928                })
1929                .collect();
1930            format!("{{{}}}", inner.join(","))
1931        }
1932        other => other.to_string(),
1933    }
1934}
1935
1936fn num(v: &Value) -> Option<f64> {
1937    match v {
1938        Value::Number(n) => n.as_f64(),
1939        Value::String(s) => s.parse().ok(),
1940        Value::Bool(b) => Some(if *b { 1.0 } else { 0.0 }),
1941        _ => None,
1942    }
1943}
1944
1945/// Every number this engine PRODUCES goes through here, so that one rule
1946/// decides how numbers render.
1947///
1948/// An integral value becomes a JSON integer. Without this, the lexer's `f64`
1949/// leaked into the output and `SELECT 1` answered `1.0` — which a client reads
1950/// as the TEXT "1.0", where PostgreSQL says "1". The liveness probe every
1951/// driver opens with was the most visible casualty.
1952///
1953/// Note what this rule cannot do: PostgreSQL distinguishes `1` (integer) from
1954/// `1.0` (numeric with scale 1), and JSON has no numeric-with-scale type at
1955/// all, so that distinction is unrepresentable here whatever we choose.
1956/// Rendering integral values as integers is the only self-consistent option
1957/// available, and it is the one that matches the common case.
1958fn from_f64(f: f64) -> Value {
1959    if f.is_finite() && f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
1960        return Value::Number((f as i64).into());
1961    }
1962    serde_json::Number::from_f64(f).map(Value::Number).unwrap_or(Value::Null)
1963}
1964
1965/// Evaluate an expression against one bound row.
1966pub fn eval(e: &Expr, row: &Bound) -> Result<Value> {
1967    Ok(match e {
1968        Expr::Literal(v) => v.clone(),
1969
1970        Expr::Column { qual, name } => {
1971            // An unknown ALIAS is a query bug and is reported. An unknown
1972            // COLUMN in a known binding is NULL, because a schemaless
1973            // document may legitimately omit any field.
1974            if let Some(q) = qual {
1975                if !row.has_binding(q) {
1976                    bail!("no table or alias named {:?} in this query", q);
1977                }
1978            }
1979            row.column(qual.as_deref(), name)
1980        }
1981
1982        Expr::Cast { expr, .. } => eval(expr, row)?,
1983
1984        Expr::Star | Expr::QualifiedStar(_) => {
1985            bail!("`*` is only valid in a select list or as count(*)")
1986        }
1987
1988        Expr::Unary { op, expr } => {
1989            let v = eval(expr, row)?;
1990            match op.as_str() {
1991                "NOT" => match truthy(&v) {
1992                    // NOT UNKNOWN is UNKNOWN, not true.
1993                    None => Value::Null,
1994                    Some(b) => Value::Bool(!b),
1995                },
1996                "-" => match num(&v) {
1997                    Some(n) => from_f64(-n),
1998                    None => Value::Null,
1999                },
2000                other => bail!("unsupported unary operator {:?}", other),
2001            }
2002        }
2003
2004        Expr::Binary { op, left, right } => {
2005            // AND / OR short-circuit on the value that decides the result, and
2006            // follow SQL's three-valued truth tables:
2007            //   false AND unknown = false      true  OR unknown = true
2008            //   true  AND unknown = unknown    false OR unknown = unknown
2009            if op == "AND" {
2010                let l = truthy(&eval(left, row)?);
2011                if l == Some(false) {
2012                    return Ok(Value::Bool(false));
2013                }
2014                let r = truthy(&eval(right, row)?);
2015                return Ok(match (l, r) {
2016                    (_, Some(false)) => Value::Bool(false),
2017                    (Some(true), Some(true)) => Value::Bool(true),
2018                    _ => Value::Null,
2019                });
2020            }
2021            if op == "OR" {
2022                let l = truthy(&eval(left, row)?);
2023                if l == Some(true) {
2024                    return Ok(Value::Bool(true));
2025                }
2026                let r = truthy(&eval(right, row)?);
2027                return Ok(match (l, r) {
2028                    (_, Some(true)) => Value::Bool(true),
2029                    (Some(false), Some(false)) => Value::Bool(false),
2030                    _ => Value::Null,
2031                });
2032            }
2033
2034            let l = eval(left, row)?;
2035            let r = eval(right, row)?;
2036            apply_op(op, l, r)?
2037        }
2038
2039        Expr::IsNull { expr, negated } => {
2040            let v = eval(expr, row)?;
2041            // `IS NULL` is the one predicate that is never UNKNOWN — it always
2042            // answers true or false, which is exactly why it exists.
2043            Value::Bool(v.is_null() != *negated)
2044        }
2045
2046        Expr::InList { expr, list, negated } => {
2047            let v = eval(expr, row)?;
2048            if v.is_null() {
2049                return Ok(Value::Null);
2050            }
2051            let mut items = Vec::with_capacity(list.len());
2052            for item in list {
2053                items.push(eval(item, row)?);
2054            }
2055            in_values(&v, &items, *negated)?
2056        }
2057
2058        // ── subqueries ──────────────────────────────────────────────────────
2059        Expr::Subquery(q) => {
2060            let (cols, rows) = run_sub(q, row)?;
2061            if cols.len() != 1 {
2062                bail!("a subquery used as an expression must return exactly one \
2063                       column, this one returns {}", cols.len());
2064            }
2065            match rows.len() {
2066                0 => Value::Null,
2067                1 => rows[0].get(&cols[0].key).cloned().unwrap_or(Value::Null),
2068                n => bail!("more than one row returned by a subquery used as an \
2069                            expression ({} rows)", n),
2070            }
2071        }
2072        Expr::Exists { query, negated } => {
2073            let (_, rows) = run_sub(query, row)?;
2074            Value::Bool(!rows.is_empty() != *negated)
2075        }
2076        Expr::ArrayQuery(q) => Value::Array(first_column(q, row)?),
2077        Expr::InSubquery { expr, query, negated } => {
2078            let v = eval(expr, row)?;
2079            if v.is_null() {
2080                return Ok(Value::Null);
2081            }
2082            let items = first_column(query, row)?;
2083            in_values(&v, &items, *negated)?
2084        }
2085        Expr::Quantified { op, left, all, right } => {
2086            let l = eval(left, row)?;
2087            let r = eval(right, row)?;
2088            let items = match r {
2089                Value::Null => return Ok(Value::Null),
2090                Value::Array(items) => items,
2091                other => bail!(
2092                    "{} requires an array or a subquery on its right side, got {}",
2093                    if *all { "ALL" } else { "ANY" },
2094                    as_text(&other)
2095                ),
2096            };
2097            // ANY: true if any element compares true; false if all compare
2098            // false; else UNKNOWN. ALL is the dual. An empty array is false
2099            // for ANY and true for ALL, as SQL says.
2100            let mut saw_true = false;
2101            let mut saw_false = false;
2102            let mut saw_null = false;
2103            for item in items {
2104                match truthy(&apply_op(op, l.clone(), item)?) {
2105                    Some(true) => saw_true = true,
2106                    Some(false) => saw_false = true,
2107                    None => saw_null = true,
2108                }
2109            }
2110            if *all {
2111                if saw_false {
2112                    Value::Bool(false)
2113                } else if saw_null {
2114                    Value::Null
2115                } else {
2116                    Value::Bool(true)
2117                }
2118            } else if saw_true {
2119                Value::Bool(true)
2120            } else if saw_null {
2121                Value::Null
2122            } else {
2123                Value::Bool(false)
2124            }
2125        }
2126        Expr::Index { expr, index } => {
2127            let arr = eval(expr, row)?;
2128            let i = eval(index, row)?;
2129            match (arr, num(&i)) {
2130                (Value::Array(items), Some(n)) if n >= 1.0 => {
2131                    items.get(n as usize - 1).cloned().unwrap_or(Value::Null)
2132                }
2133                _ => Value::Null,
2134            }
2135        }
2136        Expr::ArrayLit(items) => {
2137            let mut out = Vec::with_capacity(items.len());
2138            for i in items {
2139                out.push(eval(i, row)?);
2140            }
2141            Value::Array(out)
2142        }
2143
2144        Expr::Case { operand, whens, else_ } => {
2145            let subject = match operand {
2146                Some(o) => Some(eval(o, row)?),
2147                None => None,
2148            };
2149            for (cond, then) in whens {
2150                let hit = match &subject {
2151                    // simple CASE: compare the operand to each WHEN value.
2152                    Some(sv) => {
2153                        let cv = eval(cond, row)?;
2154                        matches!(cmp_values(sv, &cv), Some(std::cmp::Ordering::Equal))
2155                    }
2156                    // searched CASE: each WHEN is a predicate, and UNKNOWN
2157                    // does not match.
2158                    None => truthy(&eval(cond, row)?) == Some(true),
2159                };
2160                if hit {
2161                    return eval(then, row);
2162                }
2163            }
2164            match else_ {
2165                Some(e) => eval(e, row)?,
2166                // A CASE with no matching branch and no ELSE is NULL, which is
2167                // exactly what psql's \dt relies on for an unknown relkind.
2168                None => Value::Null,
2169            }
2170        }
2171
2172        Expr::Func { name, args } => eval_func(name, args, row)?,
2173
2174        // An aggregate has no value for a single row — it is reduced over a
2175        // GROUP by the executor and replaced with a literal before the rest of
2176        // the expression is evaluated. Reaching here means a grouped statement
2177        // took an ungrouped path, which is a bug in this engine rather than in
2178        // the query, so it says so instead of inventing a number.
2179        Expr::Agg { name, .. } => bail!(
2180            "{}() is an aggregate and has no value for one row — it is reduced \
2181             over a GROUP. Reaching this point is an engine bug, not a problem \
2182             with the query",
2183            name
2184        ),
2185    })
2186}
2187
2188/// `x [NOT] IN (values)` over already-evaluated values.
2189fn in_values(v: &Value, items: &[Value], negated: bool) -> Result<Value> {
2190    let mut any_null = false;
2191    let mut found = false;
2192    for iv in items {
2193        if iv.is_null() {
2194            any_null = true;
2195            continue;
2196        }
2197        if matches!(cmp_values(v, iv), Some(std::cmp::Ordering::Equal)) {
2198            found = true;
2199            break;
2200        }
2201    }
2202    // `x NOT IN (1, NULL)` is UNKNOWN rather than true when x is not 1 —
2203    // because x might equal the NULL. Postgres agrees, and this is the
2204    // classic NOT IN trap.
2205    Ok(if found {
2206        Value::Bool(!negated)
2207    } else if any_null {
2208        Value::Null
2209    } else {
2210        Value::Bool(negated)
2211    })
2212}
2213
2214/// Run a subquery in the scope of `row`.
2215///
2216/// The row is the subquery's OUTER scope: its own relations bind first, and
2217/// anything they do not bind resolves against `row`. That is a correlated
2218/// subquery, evaluated the direct way — once per outer row. Honest for the
2219/// catalogue relations this engine serves (tens of rows squared), and the
2220/// executor refuses to route a large collection through it.
2221fn run_sub(q: &Select, row: &Bound) -> Result<(Vec<OutCol>, Vec<Value>)> {
2222    let Some(resolve) = row.ctx.resolver else {
2223        bail!("a subquery cannot run here: this evaluation has no relation resolver");
2224    };
2225    let (cols, rows, _) = execute_inner(q, resolve, Opts::default(), Some(row))?;
2226    Ok((cols, rows))
2227}
2228
2229/// The first column of a subquery's every row — what `ARRAY(SELECT ...)`,
2230/// `IN (SELECT ...)` and `= ANY (SELECT ...)` all consume.
2231fn first_column(q: &Select, row: &Bound) -> Result<Vec<Value>> {
2232    let (cols, rows) = run_sub(q, row)?;
2233    let Some(first) = cols.first() else {
2234        bail!("the subquery returns no columns");
2235    };
2236    Ok(rows
2237        .into_iter()
2238        .map(|r| r.get(&first.key).cloned().unwrap_or(Value::Null))
2239        .collect())
2240}
2241
2242/// A binary operator over two evaluated operands. Shared by `Expr::Binary`
2243/// and the element-wise `ANY` / `ALL`, so the two cannot disagree about what
2244/// `=` means.
2245fn apply_op(op: &str, l: Value, r: Value) -> Result<Value> {
2246    // Every comparison over NULL is UNKNOWN — including `NULL = NULL`.
2247    let compare = |ord: fn(std::cmp::Ordering) -> bool| -> Value {
2248        match cmp_values(&l, &r) {
2249            None => Value::Null,
2250            Some(o) => Value::Bool(ord(o)),
2251        }
2252    };
2253
2254    Ok(match op {
2255        "IS DISTINCT FROM" | "IS NOT DISTINCT FROM" => {
2256            let distinct = match (l.is_null(), r.is_null()) {
2257                (true, true) => false,
2258                (true, false) | (false, true) => true,
2259                (false, false) => !matches!(cmp_values(&l, &r), Some(std::cmp::Ordering::Equal)),
2260            };
2261            Value::Bool(distinct != op.starts_with("IS NOT"))
2262        }
2263        "=" => compare(|o| o.is_eq()),
2264                "!=" | "<>" => compare(|o| o.is_ne()),
2265                "<" => compare(|o| o.is_lt()),
2266                "<=" => compare(|o| o.is_le()),
2267                ">" => compare(|o| o.is_gt()),
2268                ">=" => compare(|o| o.is_ge()),
2269
2270                "~" | "~*" | "!~" | "!~*" => {
2271                    if l.is_null() || r.is_null() {
2272                        Value::Null
2273                    } else {
2274                        let pat = as_text(&r);
2275                        if let Some(why) = crate::nql::regex_error_pub(&pat) {
2276                            bail!(
2277                                "{} — in {:?}. The supported subset is ^ $ . | ( ) \
2278                                 [ ] * + ? and literal text",
2279                                why, pat
2280                            );
2281                        }
2282                        let hit = crate::nql::regex_match_pub(
2283                            &as_text(&l), &pat, op.ends_with('*'));
2284                        Value::Bool(hit != op.starts_with('!'))
2285                    }
2286                }
2287
2288                "LIKE" | "ILIKE" | "NOT LIKE" | "NOT ILIKE" => {
2289                    if l.is_null() || r.is_null() {
2290                        Value::Null
2291                    } else {
2292                        let hit = crate::nql::like_match_pub(
2293                            &as_text(&l), &as_text(&r), op.ends_with("ILIKE"));
2294                        Value::Bool(hit != op.starts_with("NOT"))
2295                    }
2296                }
2297
2298                // String concatenation. NULL propagates, as in Postgres.
2299                "||" => {
2300                    if l.is_null() || r.is_null() {
2301                        Value::Null
2302                    } else {
2303                        Value::String(format!("{}{}", as_text(&l), as_text(&r)))
2304                    }
2305                }
2306
2307                "+" | "-" | "*" | "/" | "%" => match (num(&l), num(&r)) {
2308                    (Some(a), Some(b)) => match op {
2309                        "+" => from_f64(a + b),
2310                        "-" => from_f64(a - b),
2311                        "*" => from_f64(a * b),
2312                        // Division by zero is an ERROR in Postgres, not
2313                        // infinity. Returning inf would be a wrong number.
2314                        "/" if b == 0.0 => bail!("division by zero"),
2315                        "/" => from_f64(a / b),
2316                        "%" if b == 0.0 => bail!("division by zero"),
2317                        "%" => from_f64(a % b),
2318                        _ => unreachable!(),
2319                    },
2320                    _ => Value::Null,
2321                },
2322
2323                other => bail!("unsupported operator {:?}", other),
2324    })
2325}
2326
2327/// Scalar functions.
2328///
2329/// Only what real clients actually call. An unknown function is REFUSED by
2330/// name rather than returning NULL — a NULL would flow into a result set as a
2331/// blank column and look like missing data rather than a missing feature.
2332fn eval_func(name: &str, args: &[Expr], row: &Bound) -> Result<Value> {
2333    // Evaluated lazily per arm, because `coalesce` must not error on a later
2334    // argument once an earlier one is non-null.
2335    let arg = |i: usize| -> Result<Value> {
2336        match args.get(i) {
2337            Some(e) => eval(e, row),
2338            None => Ok(Value::Null),
2339        }
2340    };
2341
2342    Ok(match name {
2343        // ── identity / session ──────────────────────────────────────────────
2344        // NEDB presents a single role and a single schema; reporting them
2345        // consistently is what lets a client's "who am I" probe succeed.
2346        "pg_get_userbyid" | "current_user" | "session_user" | "user" => {
2347            Value::String("nedb".into())
2348        }
2349        "current_schema" => Value::String("public".into()),
2350        "current_database" | "current_catalog" => Value::String("nedb".into()),
2351        "version" => Value::String(crate::pgwire::version_string()),
2352
2353        // ── visibility ──────────────────────────────────────────────────────
2354        // Every relation NEDB reports is in `public` and reachable on the
2355        // search path, so visibility is unconditionally true. Returning false
2356        // would hide every table from `\dt`.
2357        "pg_table_is_visible" | "pg_type_is_visible" | "pg_function_is_visible"
2358        | "pg_opclass_is_visible" | "pg_conversion_is_visible" => Value::Bool(true),
2359
2360        // ── encoding ────────────────────────────────────────────────────────
2361        "pg_encoding_to_char" => Value::String("UTF8".into()),
2362        "pg_get_expr" | "pg_get_indexdef" | "pg_get_constraintdef"
2363        | "pg_get_viewdef" | "pg_get_partkeydef" | "obj_description"
2364        | "col_description" | "shobj_description" => Value::Null,
2365
2366        // ── text ────────────────────────────────────────────────────────────
2367        "lower" => match arg(0)? {
2368            Value::Null => Value::Null,
2369            v => Value::String(as_text(&v).to_lowercase()),
2370        },
2371        "upper" => match arg(0)? {
2372            Value::Null => Value::Null,
2373            v => Value::String(as_text(&v).to_uppercase()),
2374        },
2375        "length" | "char_length" | "character_length" => match arg(0)? {
2376            Value::Null => Value::Null,
2377            v => from_f64(as_text(&v).chars().count() as f64),
2378        },
2379        "format_type" => match arg(0)? {
2380            Value::Null => Value::Null,
2381            v => Value::String(crate::pgcatalog::type_name_pub(
2382                num(&v).unwrap_or(25.0) as i32).to_string()),
2383        },
2384        "array_to_string" | "pg_catalog.array_to_string" => {
2385            // NEDB stores no arrays in the catalogue, so an ACL column is
2386            // NULL and joining it yields NULL — the same as Postgres for a
2387            // relation with default privileges.
2388            match arg(0)? {
2389                Value::Array(items) => {
2390                    let sep = as_text(&arg(1)?);
2391                    Value::String(
2392                        items.iter().map(as_text).collect::<Vec<_>>().join(&sep),
2393                    )
2394                }
2395                _ => Value::Null,
2396            }
2397        }
2398        "quote_ident" => Value::String(as_text(&arg(0)?)),
2399        "quote_literal" => Value::String(format!("'{}'", as_text(&arg(0)?).replace('\'', "''"))),
2400        // `format('%s FROM %s', a, b)` — psql's `\dX` builds a definition
2401        // with it. `%s` is text, `%I` an identifier, `%L` a quoted literal;
2402        // anything else is refused rather than passed through as garbage.
2403        "format" => {
2404            let fmt = as_text(&arg(0)?);
2405            let mut out = String::new();
2406            let mut next = 1usize;
2407            let mut chars = fmt.chars().peekable();
2408            while let Some(c) = chars.next() {
2409                if c != '%' {
2410                    out.push(c);
2411                    continue;
2412                }
2413                match chars.next() {
2414                    Some('%') => out.push('%'),
2415                    Some(spec @ ('s' | 'I' | 'L')) => {
2416                        let v = arg(next)?;
2417                        next += 1;
2418                        match (spec, &v) {
2419                            ('L', Value::Null) => out.push_str("NULL"),
2420                            ('L', v) => out.push_str(&format!("'{}'", as_text(v).replace('\'', "''"))),
2421                            (_, v) => out.push_str(&as_text(v)),
2422                        }
2423                    }
2424                    other => bail!("format(): unsupported conversion %{}", other.map(String::from).unwrap_or_default()),
2425                }
2426            }
2427            Value::String(out)
2428        }
2429
2430        // ── arrays ──────────────────────────────────────────────────────────
2431        // NULL for a NULL or empty array, as Postgres answers — which is what
2432        // makes psql's `CASE WHEN array_length(acl, 1) = 0` fall to its ELSE.
2433        "array_length" | "array_upper" | "cardinality" => match arg(0)? {
2434            Value::Array(items) if !items.is_empty() => from_f64(items.len() as f64),
2435            Value::Array(_) if name == "cardinality" => from_f64(0.0),
2436            _ => Value::Null,
2437        },
2438        "array_lower" => match arg(0)? {
2439            Value::Array(items) if !items.is_empty() => from_f64(1.0),
2440            _ => Value::Null,
2441        },
2442
2443        // ── sizes ───────────────────────────────────────────────────────────
2444        // NEDB does not track a per-collection on-disk size the way Postgres
2445        // tracks a heap's, and inventing one would be a plausible number that
2446        // is wrong. NULL renders as a blank cell in `\dt+`, which is the
2447        // truthful "not known" — the same policy the catalogue module states
2448        // for statistics.
2449        "pg_table_size" | "pg_total_relation_size" | "pg_relation_size"
2450        | "pg_indexes_size" | "pg_database_size" => Value::Null,
2451        "pg_size_pretty" => match num(&arg(0)?) {
2452            None => Value::Null,
2453            Some(n) => {
2454                let units = ["bytes", "kB", "MB", "GB", "TB", "PB"];
2455                let mut v = n;
2456                let mut u = 0usize;
2457                while v.abs() >= 10240.0 && u + 1 < units.len() {
2458                    v /= 1024.0;
2459                    u += 1;
2460                }
2461                Value::String(format!("{} {}", v.round() as i64, units[u]))
2462            }
2463        },
2464
2465        // ── more definition getters, all honestly NULL ──────────────────────
2466        // NEDB has no triggers, rules, statistics objects, functions or
2467        // publications, so every relation these are called on is empty and
2468        // the call is never reached with a real row. NULL keeps the query
2469        // shape valid without fabricating a definition.
2470        "pg_get_triggerdef" | "pg_get_ruledef" | "pg_get_statisticsobjdef"
2471        | "pg_get_statisticsobjdef_columns" | "pg_get_function_result"
2472        | "pg_get_function_arguments" | "pg_get_function_identity_arguments"
2473        | "pg_get_functiondef" | "pg_get_serial_sequence" | "pg_get_partition_constraintdef"
2474        | "pg_relation_filepath" | "pg_tablespace_location" => Value::Null,
2475        "pg_relation_is_publishable" => Value::Bool(true),
2476        "pg_statistics_obj_is_visible" | "pg_opfamily_is_visible" | "pg_collation_is_visible"
2477        | "pg_ts_config_is_visible" | "pg_ts_dict_is_visible" | "pg_ts_parser_is_visible"
2478        | "pg_ts_template_is_visible" | "has_table_privilege" | "has_schema_privilege"
2479        | "has_database_privilege" | "pg_has_role" => Value::Bool(true),
2480        // The settings a driver or psql actually asks for. Anything else is
2481        // refused by name, exactly as Postgres refuses an unrecognised one.
2482        "current_setting" => match arg(0)? {
2483            Value::Null => Value::Null,
2484            v => match as_text(&v).to_lowercase().as_str() {
2485                "server_version" => Value::String(crate::pgwire::version_string()),
2486                "server_encoding" | "client_encoding" => Value::String("UTF8".into()),
2487                "standard_conforming_strings" | "integer_datetimes" | "is_superuser" => {
2488                    Value::String("on".into())
2489                }
2490                "timezone" | "log_timezone" => Value::String("UTC".into()),
2491                "search_path" => Value::String("\"$user\", public".into()),
2492                "intervalstyle" => Value::String("postgres".into()),
2493                "datestyle" => Value::String("ISO, MDY".into()),
2494                "session_authorization" => Value::String("nedb".into()),
2495                "application_name" | "default_transaction_read_only" => Value::String(String::new()),
2496                "transaction_isolation" | "default_transaction_isolation" => {
2497                    Value::String("read committed".into())
2498                }
2499                "max_identifier_length" => Value::String("63".into()),
2500                other => {
2501                    // `current_setting(name, true)` returns NULL for a
2502                    // missing setting instead of erroring.
2503                    if truthy(&arg(1)?) == Some(true) {
2504                        Value::Null
2505                    } else {
2506                        bail!("unrecognized configuration parameter \"{}\"", other)
2507                    }
2508                }
2509            },
2510        },
2511        "pg_backend_pid" => from_f64(std::process::id() as f64),
2512        "pg_is_in_recovery" => Value::Bool(false),
2513        "txid_current" => from_f64(0.0),
2514        "now" | "current_timestamp" | "statement_timestamp" | "clock_timestamp" => {
2515            Value::String(now_iso())
2516        }
2517        "to_char" => match arg(0)? {
2518            Value::Null => Value::Null,
2519            v => Value::String(as_text(&v)),
2520        },
2521        "generate_series" | "unnest" => bail!(
2522            "{}() returns a set of rows — write it in FROM, not in the select list", name
2523        ),
2524
2525        // ── null handling ───────────────────────────────────────────────────
2526        "coalesce" => {
2527            let mut out = Value::Null;
2528            for a in args {
2529                let v = eval(a, row)?;
2530                if !v.is_null() {
2531                    out = v;
2532                    break;
2533                }
2534            }
2535            out
2536        }
2537        "nullif" => {
2538            let a = arg(0)?;
2539            let b = arg(1)?;
2540            if matches!(cmp_values(&a, &b), Some(std::cmp::Ordering::Equal)) {
2541                Value::Null
2542            } else {
2543                a
2544            }
2545        }
2546
2547        // ── casts spelled as functions ──────────────────────────────────────
2548        "int4" | "int8" | "int2" => match num(&arg(0)?) {
2549            Some(n) => from_f64(n.trunc()),
2550            None => Value::Null,
2551        },
2552        "text" => match arg(0)? {
2553            Value::Null => Value::Null,
2554            v => Value::String(as_text(&v)),
2555        },
2556
2557        other if is_aggregate(other) => bail!(
2558            "{}() is an aggregate, which is only meaningful over a whole result set — \
2559             it is evaluated by the executor, never per row",
2560            other
2561        ),
2562
2563        other => bail!(
2564            "the function {}() is not implemented. It is refused rather than \
2565             answered with NULL, because a NULL column reads as missing DATA \
2566             rather than a missing feature",
2567            other
2568        ),
2569    })
2570}
2571
2572/// An ISO-8601 wall-clock timestamp, for the handful of clients that ask.
2573fn now_iso() -> String {
2574    let secs = std::time::SystemTime::now()
2575        .duration_since(std::time::UNIX_EPOCH)
2576        .map(|d| d.as_secs())
2577        .unwrap_or(0);
2578    // Civil-from-days (Howard Hinnant's algorithm), UTC.
2579    let days = (secs / 86_400) as i64;
2580    let rem = secs % 86_400;
2581    let z = days + 719_468;
2582    let era = z.div_euclid(146_097);
2583    let doe = z.rem_euclid(146_097);
2584    let yoe = (doe - doe / 1460 + doe / 36_524 - doe / 146_096) / 365;
2585    let y = yoe + era * 400;
2586    let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
2587    let mp = (5 * doy + 2) / 153;
2588    let d = doy - (153 * mp + 2) / 5 + 1;
2589    let m = if mp < 10 { mp + 3 } else { mp - 9 };
2590    let y = if m <= 2 { y + 1 } else { y };
2591    format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}+00", y, m, d, rem / 3600, (rem % 3600) / 60, rem % 60)
2592}
2593
2594// ─────────────────────────────────────────────────────────────────────────────
2595// Aggregates without GROUP BY
2596// ─────────────────────────────────────────────────────────────────────────────
2597
2598const AGGREGATES: &[&str] = &[
2599    "count", "sum", "avg", "min", "max", "string_agg", "array_agg", "bool_and",
2600    "bool_or", "every",
2601];
2602
2603fn is_aggregate(name: &str) -> bool {
2604    AGGREGATES.iter().any(|a| a.eq_ignore_ascii_case(name))
2605}
2606
2607/// Does this expression call an aggregate at ITS level — not inside a
2608/// subquery, whose aggregates belong to the subquery?
2609pub fn has_aggregate(e: &Expr) -> bool {
2610    match e {
2611        Expr::Agg { .. } => true,
2612        Expr::Func { args, .. } => args.iter().any(has_aggregate),
2613        Expr::Binary { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2614        Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2615            has_aggregate(expr)
2616        }
2617        Expr::InList { expr, list, .. } => has_aggregate(expr) || list.iter().any(has_aggregate),
2618        Expr::Case { operand, whens, else_ } => {
2619            operand.as_deref().is_some_and(has_aggregate)
2620                || whens.iter().any(|(c, t)| has_aggregate(c) || has_aggregate(t))
2621                || else_.as_deref().is_some_and(has_aggregate)
2622        }
2623        Expr::Quantified { left, right, .. } => has_aggregate(left) || has_aggregate(right),
2624        Expr::Index { expr, index } => has_aggregate(expr) || has_aggregate(index),
2625        Expr::ArrayLit(items) => items.iter().any(has_aggregate),
2626        Expr::InSubquery { expr, .. } => has_aggregate(expr),
2627        Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => false,
2628        Expr::Column { .. } | Expr::Literal(_) | Expr::Star | Expr::QualifiedStar(_) => false,
2629    }
2630}
2631
2632/// Reduce one aggregate call over the rows of ONE group.
2633///
2634/// `order_by` sorts the group's rows before the values are collected, which is
2635/// the whole point of `array_agg(col ORDER BY ord)`: the array is a column
2636/// list and its ORDER is the answer. `distinct` de-duplicates the collected
2637/// values, not the rows.
2638fn aggregate(
2639    name: &str,
2640    args: &[Expr],
2641    order_by: &[OrderBy],
2642    distinct: bool,
2643    rows: &[JoinedRow],
2644    ctx: EvalCtx,
2645) -> Result<Value> {
2646    let lname = name.to_lowercase();
2647
2648    // The aggregate's own ORDER BY. Keys are precomputed so the comparator
2649    // cannot fail halfway through and leave a half-sorted group behind.
2650    let ordered: Vec<JoinedRow> = if order_by.is_empty() {
2651        rows.to_vec()
2652    } else {
2653        let mut keyed: Vec<(Vec<Value>, JoinedRow)> = Vec::with_capacity(rows.len());
2654        for r in rows {
2655            let b = bind(r, ctx);
2656            let mut key = vec![];
2657            for ob in order_by {
2658                // An ordinal inside an aggregate has no select list to index,
2659                // so it is refused rather than silently ignored.
2660                match (&ob.expr, ob.ordinal) {
2661                    (Some(e), _) => key.push(eval(e, &b)?),
2662                    (None, Some(n)) => bail!(
2663                        "ORDER BY {} inside an aggregate refers to a select-list \
2664                         position, which an aggregate does not have — name the \
2665                         column instead", n
2666                    ),
2667                    (None, None) => key.push(Value::Null),
2668                }
2669            }
2670            keyed.push((key, r.clone()));
2671        }
2672        keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, order_by));
2673        keyed.into_iter().map(|(_, r)| r).collect()
2674    };
2675    let rows: &[JoinedRow] = &ordered;
2676
2677    // `count(*)` and a bare `count()` count rows; everything else evaluates
2678    // its first argument per row and skips NULLs, as SQL aggregates do.
2679    if lname == "count" && (args.is_empty() || matches!(args[0], Expr::Star)) {
2680        return Ok(from_f64(rows.len() as f64));
2681    }
2682    let Some(target) = args.first() else {
2683        bail!("{}() needs an argument", name);
2684    };
2685    let mut vals: Vec<Value> = Vec::with_capacity(rows.len());
2686    let mut all_vals: Vec<Value> = Vec::with_capacity(rows.len());
2687    for r in rows {
2688        let v = eval(target, &bind(r, ctx))?;
2689        if !v.is_null() {
2690            vals.push(v.clone());
2691        }
2692        all_vals.push(v);
2693    }
2694    if distinct {
2695        let mut seen: Vec<String> = vec![];
2696        vals.retain(|v| {
2697            let k = format!("{:?}", v);
2698            if seen.contains(&k) { false } else { seen.push(k); true }
2699        });
2700        let mut seen2: Vec<String> = vec![];
2701        all_vals.retain(|v| {
2702            let k = format!("{:?}", v);
2703            if seen2.contains(&k) { false } else { seen2.push(k); true }
2704        });
2705    }
2706    Ok(match lname.as_str() {
2707        "count" => from_f64(vals.len() as f64),
2708        "sum" | "avg" => {
2709            let nums: Vec<f64> = vals.iter().filter_map(num).collect();
2710            if nums.is_empty() {
2711                Value::Null
2712            } else if lname == "sum" {
2713                from_f64(nums.iter().sum())
2714            } else {
2715                from_f64(nums.iter().sum::<f64>() / nums.len() as f64)
2716            }
2717        }
2718        "min" | "max" => {
2719            let mut best: Option<Value> = None;
2720            for v in vals {
2721                best = Some(match best {
2722                    None => v,
2723                    Some(b) => {
2724                        let take = match cmp_values(&v, &b) {
2725                            Some(o) if lname == "min" => o.is_lt(),
2726                            Some(o) => o.is_gt(),
2727                            None => false,
2728                        };
2729                        if take { v } else { b }
2730                    }
2731                });
2732            }
2733            best.unwrap_or(Value::Null)
2734        }
2735        "string_agg" => {
2736            if vals.is_empty() {
2737                Value::Null
2738            } else {
2739                // The separator is a constant in every real call, so it is
2740                // evaluated once with no row in scope.
2741                let sep = match args.get(1) {
2742                    Some(e) => as_text(&eval(e, &Bound { parts: vec![], ctx })?),
2743                    None => String::new(),
2744                };
2745                Value::String(vals.iter().map(as_text).collect::<Vec<_>>().join(&sep))
2746            }
2747        }
2748        // array_agg keeps NULLs, as Postgres does.
2749        "array_agg" => {
2750            if all_vals.is_empty() { Value::Null } else { Value::Array(all_vals) }
2751        }
2752        "bool_and" | "every" => {
2753            if vals.is_empty() {
2754                Value::Null
2755            } else {
2756                Value::Bool(vals.iter().all(|v| truthy(v) == Some(true)))
2757            }
2758        }
2759        "bool_or" => {
2760            if vals.is_empty() {
2761                Value::Null
2762            } else {
2763                Value::Bool(vals.iter().any(|v| truthy(v) == Some(true)))
2764            }
2765        }
2766        _ => unreachable!("is_aggregate gates this"),
2767    })
2768}
2769
2770/// Replace every aggregate call in `e` with the literal it reduces to, so the
2771/// remainder can be evaluated by the ordinary evaluator against no row at all.
2772///
2773/// A column outside an aggregate has no single value across the result set,
2774/// and Postgres refuses it with the message reproduced here rather than
2775/// picking a row arbitrarily.
2776fn fold_aggregates(
2777    e: &Expr,
2778    rows: &[JoinedRow],
2779    ctx: EvalCtx,
2780    keys: &[Expr],
2781) -> Result<Expr> {
2782    let fold = |x: &Expr| fold_aggregates(x, rows, ctx, keys);
2783    Ok(match e {
2784        Expr::Agg { name, args, order_by, distinct } => {
2785            Expr::Literal(aggregate(name, args, order_by, *distinct, rows, ctx)?)
2786        }
2787        Expr::Func { name, args } => Expr::Func {
2788            name: name.clone(),
2789            args: args.iter().map(&fold).collect::<Result<_>>()?,
2790        },
2791        // A GROUP BY key is constant within its group, so it is left alone and
2792        // evaluated against any row of the group. A column that is NOT a key
2793        // has no single value there, and Postgres's own message is the one
2794        // worth reproducing — picking an arbitrary row instead is how a
2795        // grouped query returns a confidently wrong answer.
2796        Expr::Column { .. } if keys.iter().any(|k| k == e) => e.clone(),
2797        Expr::Column { qual, name } => bail!(
2798            "column \"{}{}\" must appear in the GROUP BY clause or be used in an \
2799             aggregate function",
2800            qual.as_ref().map(|q| format!("{q}.")).unwrap_or_default(),
2801            name
2802        ),
2803        Expr::Binary { op, left, right } => Expr::Binary {
2804            op: op.clone(),
2805            left: Box::new(fold(left)?),
2806            right: Box::new(fold(right)?),
2807        },
2808        Expr::Unary { op, expr } => Expr::Unary {
2809            op: op.clone(),
2810            expr: Box::new(fold(expr)?),
2811        },
2812        Expr::Cast { expr, ty } => Expr::Cast {
2813            expr: Box::new(fold(expr)?),
2814            ty: ty.clone(),
2815        },
2816        Expr::IsNull { expr, negated } => Expr::IsNull {
2817            expr: Box::new(fold(expr)?),
2818            negated: *negated,
2819        },
2820        Expr::InList { expr, list, negated } => Expr::InList {
2821            expr: Box::new(fold(expr)?),
2822            list: list.iter().map(&fold).collect::<Result<_>>()?,
2823            negated: *negated,
2824        },
2825        Expr::Case { operand, whens, else_ } => Expr::Case {
2826            operand: match operand {
2827                Some(o) => Some(Box::new(fold(o)?)),
2828                None => None,
2829            },
2830            whens: whens
2831                .iter()
2832                .map(|(c, t)| Ok((fold(c)?, fold(t)?)))
2833                .collect::<Result<_>>()?,
2834            else_: match else_ {
2835                Some(x) => Some(Box::new(fold(x)?)),
2836                None => None,
2837            },
2838        },
2839        Expr::Quantified { op, left, all, right } => Expr::Quantified {
2840            op: op.clone(),
2841            left: Box::new(fold(left)?),
2842            all: *all,
2843            right: Box::new(fold(right)?),
2844        },
2845        Expr::Index { expr, index } => Expr::Index {
2846            expr: Box::new(fold(expr)?),
2847            index: Box::new(fold(index)?),
2848        },
2849        Expr::ArrayLit(items) => Expr::ArrayLit(
2850            items.iter().map(&fold).collect::<Result<_>>()?,
2851        ),
2852        Expr::InSubquery { expr, query, negated } => Expr::InSubquery {
2853            expr: Box::new(fold(expr)?),
2854            query: query.clone(),
2855            negated: *negated,
2856        },
2857        // A bare `*` in a grouped select list is the same error as a bare
2858        // column: it names every column, none of which is a key.
2859        Expr::Star | Expr::QualifiedStar(_) => {
2860            bail!("`*` cannot be mixed with an aggregate outside count(*)")
2861        }
2862        // Constants and subqueries evaluate the same way in either mode.
2863        Expr::Literal(_) | Expr::Subquery(_) | Expr::Exists { .. } | Expr::ArrayQuery(_) => {
2864            e.clone()
2865        }
2866    })
2867}
2868
2869// ─────────────────────────────────────────────────────────────────────────────
2870// Phase 4 — execution
2871// ─────────────────────────────────────────────────────────────────────────────
2872
2873/// Every relation in a query must be addressable by a DISTINCT name.
2874///
2875/// PostgreSQL rejects `FROM a JOIN a` with "table name a specified more than
2876/// once". This engine used to accept it and answer WRONGLY: a qualified
2877/// reference scans the bindings in order and takes the first match, so both
2878/// `a.x` and `a.y` read the same row, and `FROM emp JOIN emp ON emp.mgr =
2879/// emp.id` compared every row to ITSELF and returned no rows at all.
2880///
2881/// A silently empty result is the worst possible answer — it is
2882/// indistinguishable from "there is no such data". Refusing is strictly
2883/// better, and the supported spelling is one alias per relation.
2884fn validate_bindings(sel: &Select) -> Result<()> {
2885    let mut seen: Vec<String> = vec![];
2886    if let Some(f) = &sel.from {
2887        seen.push(f.binding());
2888    }
2889    for j in &sel.joins {
2890        seen.push(j.table.binding());
2891    }
2892    for (i, b) in seen.iter().enumerate() {
2893        if let Some(prev) = seen[..i].iter().find(|p| p.eq_ignore_ascii_case(b)) {
2894            bail!(
2895                "ambiguous relation binding: {:?} appears more than once; use \
2896                 aliases (for example `FROM {} JOIN {} AS {}2 ...`)",
2897                prev, prev, prev, prev
2898            );
2899        }
2900    }
2901    Ok(())
2902}
2903
2904/// One output column: the key it is stored under, and the name the client sees.
2905///
2906/// These are NOT always the same, and that is the whole point. PostgreSQL
2907/// permits duplicate output names — `SELECT e.name, e2.name` legitimately
2908/// returns two columns both called `name`, and generated SQL relies on it.
2909/// Rows here are JSON objects, so two columns sharing a key would share a
2910/// VALUE: the second write silently overwrote the first, and the query above
2911/// returned the same value twice while reporting two columns.
2912///
2913/// So the key is made unique and the display name is left alone. Renaming the
2914/// column instead would be worse — generated SQL asks for the name it wrote.
2915#[derive(Debug, Clone, PartialEq, Eq)]
2916pub struct OutCol {
2917    pub key: String,
2918    pub name: String,
2919}
2920
2921/// A key no user field can collide with, for the second and later columns
2922/// sharing a display name. `\u{1}` is not producible in a JSON field name by
2923/// any sane writer, and the index disambiguates even if one managed it.
2924fn unique_key(taken: &[OutCol], name: &str) -> String {
2925    if !taken.iter().any(|c| c.key == name) {
2926        return name.to_string();
2927    }
2928    format!("{name}\u{1}{}", taken.len())
2929}
2930
2931/// A joined row, owned: `(binding, row-or-NULL)` per source table.
2932type JoinedRow = Vec<(String, Option<Value>)>;
2933
2934fn bind<'a>(row: &'a JoinedRow, ctx: EvalCtx<'a>) -> Bound<'a> {
2935    Bound {
2936        parts: row.iter().map(|(b, v)| (b.clone(), v.as_ref())).collect(),
2937        ctx,
2938    }
2939}
2940
2941/// The name a client sees for a select item, when no `AS` was given.
2942///
2943/// Postgres derives it: a bare column keeps its column name, a function call
2944/// takes the function's name, and anything else becomes `?column?`. Matching
2945/// that matters because clients index result columns BY NAME — psycopg's
2946/// `RealDictCursor` and every ORM do — so inventing a different name breaks
2947/// code that would work against Postgres.
2948fn derived_name(e: &Expr) -> String {
2949    match e {
2950        Expr::Column { name, .. } => name.clone(),
2951        Expr::Func { name, .. } | Expr::Agg { name, .. } => name.clone(),
2952        Expr::Cast { expr, .. } => derived_name(expr),
2953        Expr::Case { .. } => "case".to_string(),
2954        Expr::ArrayQuery(_) | Expr::ArrayLit(_) => "array".to_string(),
2955        Expr::Exists { .. } => "exists".to_string(),
2956        // A scalar subquery is named after its single output column.
2957        Expr::Subquery(q) => q
2958            .items
2959            .first()
2960            .map(|i| i.alias.clone().unwrap_or_else(|| derived_name(&i.expr)))
2961            .unwrap_or_else(|| "?column?".to_string()),
2962        _ => "?column?".to_string(),
2963    }
2964}
2965
2966/// A relation, delivered one row at a time.
2967///
2968/// # The smallest interface that permits early termination
2969///
2970/// The previous contract handed back an owned `Vec<Value>`, which forced the
2971/// whole relation to exist before any work could start. That is fine until
2972/// execution can stop early — and once `LIMIT` can stop a join, a contract
2973/// that insists on materialising 8000 rows to return 20 becomes the
2974/// bottleneck. It was measured as exactly that: after the filter fusion in
2975/// #120, the hash path's remaining time was dominated by cloning relations
2976/// rather than probing them.
2977///
2978/// So this is deliberately two methods, not an async stream and not a
2979/// borrowing iterator with a lifetime parameter threaded through the whole
2980/// evaluator. Pull a row; stop whenever you like by dropping it.
2981///
2982/// [`size_hint`](Relation::size_hint) exists only so the join planner can
2983/// keep choosing a strategy from relation sizes. A source that genuinely does
2984/// not know returns `None`, and the planner then decides from what it does
2985/// know rather than pretending.
2986pub trait Relation {
2987    /// The next row, or `None` when exhausted.
2988    fn next_row(&mut self) -> Result<Option<Value>>;
2989
2990    /// Exact row count when the source knows it, `None` when it does not.
2991    fn size_hint(&self) -> Option<usize> {
2992        None
2993    }
2994}
2995
2996/// A relation backed by an already-materialised `Vec`.
2997///
2998/// Every current caller uses this, so the interface change on its own alters
2999/// no behaviour — it is what lets the executor become demand-driven ahead of
3000/// the storage layer, rather than requiring both to move at once.
3001pub struct VecRelation {
3002    iter: std::vec::IntoIter<Value>,
3003    len: usize,
3004}
3005
3006impl Relation for VecRelation {
3007    fn next_row(&mut self) -> Result<Option<Value>> {
3008        Ok(self.iter.next())
3009    }
3010    fn size_hint(&self) -> Option<usize> {
3011        Some(self.len)
3012    }
3013}
3014
3015/// Wrap a materialised relation.
3016pub fn from_vec(rows: Vec<Value>) -> Box<dyn Relation> {
3017    let len = rows.len();
3018    Box::new(VecRelation { iter: rows.into_iter(), len })
3019}
3020
3021/// Everything one execution needs from the outside world.
3022///
3023/// A callback rather than a concrete store, which is what lets this engine
3024/// serve synthesised catalogue relations today and stored collections later
3025/// without knowing the difference.
3026pub type Resolver<'r> = dyn Fn(&str) -> Result<Option<Box<dyn Relation>>> + 'r;
3027
3028/// Run a parsed `SELECT`, returning `(column names, rows)`.
3029///
3030/// Rows come back as JSON objects keyed by output column name, which is the
3031/// shape the wire encoder already consumes.
3032pub fn execute(sel: &Select, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
3033    let (cols, rows, _) = execute_explain(sel, resolve, JoinExec::Auto)?;
3034    Ok((cols, rows))
3035}
3036
3037/// Run a parsed `SELECT`, also reporting how each join was executed.
3038///
3039/// `exec` forces a join strategy, which exists so that differential tests can
3040/// drive the SAME query down BOTH paths — and so a benchmark can prove it
3041/// measured the path it claims to have measured rather than silently timing
3042/// the other one twice.
3043pub fn execute_explain(
3044    sel: &Select,
3045    resolve: &Resolver,
3046    exec: JoinExec,
3047) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3048    execute_with(sel, resolve, exec, true)
3049}
3050
3051/// Execution options. Every switch exists so a differential test can run the
3052/// SAME query with the optimisation on and off and compare — without that, a
3053/// test believing it exercised an optimisation could be measuring the
3054/// unoptimised path, and the equivalence suite would prove nothing.
3055#[derive(Debug, Clone, Copy)]
3056pub struct Opts {
3057    pub exec: JoinExec,
3058    pub pushdown: bool,
3059    /// Evaluate the `WHERE` clause inside the final join rather than as a
3060    /// separate pass. Semantically identical; it is what lets the row budget
3061    /// apply to a filtered join.
3062    pub fuse_filter: bool,
3063}
3064
3065impl Default for Opts {
3066    fn default() -> Self {
3067        Opts { exec: JoinExec::Auto, pushdown: true, fuse_filter: true }
3068    }
3069}
3070
3071impl Opts {
3072    pub fn exec(exec: JoinExec) -> Self {
3073        Opts { exec, ..Default::default() }
3074    }
3075}
3076
3077/// As [`execute_explain`], with predicate pushdown switchable.
3078///
3079/// The switch exists so differential tests can run the SAME query with and
3080/// without the rewrite and compare. Without it, a test believing it exercised
3081/// pushdown could be measuring the unoptimised path, and the equivalence suite
3082/// would prove nothing — the same reason `JoinExec` can force a strategy.
3083pub fn execute_with(
3084    sel: &Select,
3085    resolve: &Resolver,
3086    exec: JoinExec,
3087    pushdown: bool,
3088) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3089    execute_opts(sel, resolve, Opts { exec, pushdown, ..Default::default() })
3090}
3091
3092/// The full form.
3093pub fn execute_opts(
3094    sel: &Select,
3095    resolve: &Resolver,
3096    opts: Opts,
3097) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3098    execute_inner(sel, resolve, opts, None)
3099}
3100
3101/// A distinct-key for a projected row: its output values, in column order.
3102fn row_key(cols: &[OutCol], obj: &Map<String, Value>) -> String {
3103    cols.iter()
3104        .map(|c| format!("{:?}", obj.get(&c.key).unwrap_or(&Value::Null)))
3105        .collect::<Vec<_>>()
3106        .join("\u{1}")
3107}
3108
3109/// Combine the arms of a compound query.
3110///
3111/// Each arm runs as its own complete query, its columns are matched to the
3112/// first arm's BY POSITION (as SQL says — the names come from the first
3113/// arm), and the rows are combined per operator. `ORDER BY` / `LIMIT` then
3114/// apply to the whole, which is why the parser refused to attach them to the
3115/// last arm.
3116fn execute_set_ops<'a>(
3117    sel: &Select,
3118    resolve: &'a Resolver<'a>,
3119    opts: Opts,
3120    outer: Option<&'a Bound<'a>>,
3121) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3122    let ctx = EvalCtx { resolver: Some(resolve), outer };
3123    let mut head = sel.clone();
3124    head.set_ops.clear();
3125    head.order_by.clear();
3126    head.limit = None;
3127    head.offset = None;
3128    let (cols, rows, mut plan) = execute_inner(&head, resolve, opts, outer)?;
3129    let mut left: Vec<Map<String, Value>> = rows
3130        .into_iter()
3131        .map(|r| match r {
3132            Value::Object(m) => m,
3133            _ => Map::new(),
3134        })
3135        .collect();
3136
3137    for arm in &sel.set_ops {
3138        let (acols, arows, _) = execute_inner(&arm.query, resolve, opts, outer)?;
3139        let op_name = match arm.op {
3140            SetOp::Union => "UNION",
3141            SetOp::Intersect => "INTERSECT",
3142            SetOp::Except => "EXCEPT",
3143        };
3144        if acols.len() != cols.len() {
3145            bail!(
3146                "each {} query must have the same number of columns: {} vs {}",
3147                op_name, cols.len(), acols.len()
3148            );
3149        }
3150        // Positional remap onto the first arm's keys.
3151        let right: Vec<Map<String, Value>> = arows
3152            .into_iter()
3153            .map(|r| {
3154                let m = match r {
3155                    Value::Object(m) => m,
3156                    _ => Map::new(),
3157                };
3158                let mut out = Map::new();
3159                for (i, c) in cols.iter().enumerate() {
3160                    out.insert(c.key.clone(), m.get(&acols[i].key).cloned().unwrap_or(Value::Null));
3161                }
3162                out
3163            })
3164            .collect();
3165        let (nl, nr) = (left.len(), right.len());
3166        let right_keys: std::collections::HashSet<String> =
3167            right.iter().map(|m| row_key(&cols, m)).collect();
3168        let mut combined: Vec<Map<String, Value>> = match arm.op {
3169            SetOp::Union => {
3170                left.extend(right);
3171                left
3172            }
3173            SetOp::Intersect => left.into_iter().filter(|m| right_keys.contains(&row_key(&cols, m))).collect(),
3174            SetOp::Except => left.into_iter().filter(|m| !right_keys.contains(&row_key(&cols, m))).collect(),
3175        };
3176        if !arm.all {
3177            let mut seen = std::collections::HashSet::new();
3178            combined.retain(|m| seen.insert(row_key(&cols, m)));
3179        }
3180        plan.notes.push(format!(
3181            "{}{}: {} + {} rows -> {} (each arm planned separately; only the first arm's plan is shown)",
3182            op_name, if arm.all { " ALL" } else { "" }, nl, nr, combined.len()
3183        ));
3184        left = combined;
3185    }
3186
3187    // The combined rows have no source row; ORDER BY by name resolves against
3188    // the output row itself under an anonymous binding.
3189    let projected: Vec<(Map<String, Value>, JoinedRow)> = left
3190        .into_iter()
3191        .map(|m| {
3192            let src: JoinedRow = vec![(String::new(), Some(Value::Object(m.clone())))];
3193            (m, src)
3194        })
3195        .collect();
3196    let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3197    Ok((cols, out, plan))
3198}
3199
3200/// One query, in an optional enclosing scope. `outer` is `Some` for a
3201/// correlated subquery and `None` at the top level.
3202fn execute_inner<'a>(
3203    sel: &Select,
3204    resolve: &'a Resolver<'a>,
3205    opts: Opts,
3206    outer: Option<&'a Bound<'a>>,
3207) -> Result<(Vec<OutCol>, Vec<Value>, Plan)> {
3208    if !sel.set_ops.is_empty() {
3209        return execute_set_ops(sel, resolve, opts, outer);
3210    }
3211    let ctx = EvalCtx { resolver: Some(resolve), outer };
3212    let exec = opts.exec;
3213    let pushdown = opts.pushdown;
3214    let mut plan = Plan::default();
3215
3216    // ── 0a. semantic validation, before any work ────────────────────────────
3217    validate_bindings(sel)?;
3218
3219    // ── 0. the row budget ───────────────────────────────────────────────────
3220    //
3221    // The only safe rewrite available without a streaming executor: when the
3222    // final answer is a PREFIX of the join's output, the join may stop as soon
3223    // as it has produced enough rows.
3224    //
3225    // Every one of these conditions is load-bearing, and each corresponds to
3226    // an operation that can REDUCE the row count after the join — capping the
3227    // join's output early would then starve it:
3228    //
3229    //   * `ORDER BY` — the prefix depends on the sort, not on emission order.
3230    //   * `DISTINCT` — deduplication can shrink 100 rows to 3.
3231    //   * a `WHERE` clause — filtering happens after the join here.
3232    //   * more than one join — an intermediate cap can starve a later join.
3233    //
3234    // `OFFSET` is added to the budget rather than disqualifying it, because
3235    // the rows skipped still have to be produced.
3236    //
3237    // This is narrow on purpose. `SELECT ... JOIN ... LIMIT n` is the shape an
3238    // interactive client sends constantly, and it was measured taking 32ms to
3239    // return 20 rows out of an 8000-row join. A wider rewrite needs a
3240    // streaming executor, not a cleverer predicate.
3241    // Fusing the `WHERE` into the final join is what makes a filtered query
3242    // eligible: the join's own output is then already filtered, so its length
3243    // is a real count of final rows and stopping early keeps a true prefix.
3244    // Without the fusion a `WHERE` had to disqualify the budget entirely.
3245    let fuse = opts.fuse_filter && sel.where_.is_some() && !sel.joins.is_empty();
3246
3247    let budget: Option<usize> = match sel.limit {
3248        Some(lim)
3249            if sel.order_by.is_empty()
3250                && !sel.distinct
3251                && !sel.joins.is_empty()
3252                && (sel.where_.is_none() || fuse) =>
3253        {
3254            Some(lim.saturating_add(sel.offset.unwrap_or(0)))
3255        }
3256        _ => None,
3257    };
3258    plan.budget = budget;
3259
3260    // ── 0b. predicate pushdown ──────────────────────────────────────────────
3261    // Conjuncts of the WHERE clause that read exactly one relation are COPIED
3262    // to pre-filter that relation before the join. The WHERE clause below is
3263    // untouched and still runs afterwards — a copy, never a move, which is
3264    // what keeps this safe for outer joins. See `sqlpush` for the argument.
3265    let all_bindings: Vec<String> = sel
3266        .from
3267        .iter()
3268        .map(|t| t.binding())
3269        .chain(sel.joins.iter().map(|j| j.table.binding()))
3270        .collect();
3271    let nullable = crate::sqlpush::nullable_bindings(sel);
3272    let push = if pushdown {
3273        crate::sqlpush::plan(sel.where_.as_ref(), &all_bindings, &nullable)
3274    } else {
3275        Pushdown::default()
3276    };
3277    plan.refusals = push.refusals.clone();
3278
3279    let mut base_scan_at: Option<usize> = None;
3280    let mut base_prefilter_at: Option<usize> = None;
3281
3282    // ── 1. source rows, and the join ────────────────────────────────────────
3283    //
3284    // The driving relation is STREAMED when there is a join to feed it into,
3285    // so a query that stops early never asks the source for the rest. The
3286    // inner side of each join is materialised, because it genuinely has to
3287    // be: a hash join builds its table before probing, and a nested loop
3288    // re-scans it per left row.
3289    let mut left_src: Box<dyn LeftSource + 'a> = match &sel.from {
3290        None => {
3291            // `SELECT 1` with no FROM is one row with no columns — which is
3292            // how a client's liveness probe is written.
3293            Box::new(VecLeft { rows: vec![vec![]], at: 0 })
3294        }
3295        Some(t) => {
3296            let rel = fetch(t, resolve, ctx)?;
3297            let binding = t.binding();
3298            // Placeholder counts, patched once the pull is over. A streamed
3299            // relation cannot report its `actual rows` before it is read, and
3300            // inventing a number would be exactly the kind of plausible
3301            // fiction `EXPLAIN` must never contain.
3302            base_scan_at = Some(plan.stages.len());
3303            plan.push(Stage::Scan {
3304                table: t.name.clone(),
3305                binding: binding.clone(),
3306                rows: 0,
3307            });
3308            let preds = push.for_binding(&binding).cloned().unwrap_or_default();
3309            if !preds.is_empty() {
3310                base_prefilter_at = Some(plan.stages.len());
3311                plan.push(Stage::Prefilter {
3312                    binding: binding.clone(),
3313                    predicates: preds.len(),
3314                    in_rows: 0,
3315                    out_rows: 0,
3316                });
3317            }
3318            Box::new(StreamLeft { rel, binding, preds, pulled: 0, kept: 0, ctx })
3319        }
3320    };
3321
3322    // The bindings accumulated so far, tracked explicitly rather than read off
3323    // the first row. Reading a row cannot describe the shape when there are no
3324    // rows — which is exactly the case a `RIGHT JOIN` onto an EMPTY left
3325    // relation produces, and it made those rows come back missing their left
3326    // bindings entirely instead of carrying them as NULL.
3327    let mut left_bindings: Vec<String> = match &sel.from {
3328        None => vec![],
3329        Some(t) => vec![t.binding()],
3330    };
3331    let last = sel.joins.len().saturating_sub(1);
3332    let mut rows: Vec<JoinedRow> = vec![];
3333    let mut base_pulled: Option<usize> = None;
3334    let mut base_kept: Option<usize> = None;
3335
3336    for (ji, join) in sel.joins.iter().enumerate() {
3337        let is_last = ji == last;
3338        let rb = join.table.binding();
3339
3340        // `LATERAL (SELECT ...)` reads the rows to its left, so it cannot be
3341        // materialised once: it runs again for every left row, in that row's
3342        // scope. A nested loop by definition, and reported as one.
3343        if join.table.lateral {
3344            let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3345            let join_budget = if is_last { budget } else { None };
3346            let (out, removed, consumed, produced) = join_lateral(
3347                left_src.as_mut(), join, resolve, join_budget, post, ctx,
3348            )?;
3349            plan.push(Stage::Scan { table: join.table.name.clone(), binding: rb.clone(), rows: produced });
3350            plan.push(Stage::Join {
3351                kind: join.kind,
3352                table: join.table.name.clone(),
3353                binding: rb.clone(),
3354                strategy: Strategy::NestedLoop,
3355                keys: 0,
3356                left_rows: consumed,
3357                right_rows: produced,
3358                out_rows: out.len(),
3359                early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3360                post_filter_removed: post.map(|_| removed),
3361            });
3362            plan.notes.push(format!("LATERAL {}: the subquery ran once per left row ({} times)", rb, consumed));
3363            left_bindings.push(rb);
3364            if ji == 0 {
3365                if let Some((pulled, kept)) = left_src.stats() {
3366                    base_pulled = Some(pulled);
3367                    base_kept = Some(kept);
3368                }
3369            }
3370            left_src = Box::new(VecLeft { rows: out, at: 0 });
3371            continue;
3372        }
3373
3374        let right_rel = fetch(&join.table, resolve, ctx)?;
3375        let right_all = drain(right_rel)?;
3376        plan.push(Stage::Scan {
3377            table: join.table.name.clone(),
3378            binding: rb.clone(),
3379            rows: right_all.len(),
3380        });
3381        let right_rows = prefilter(right_all, &rb, &push, &mut plan, ctx)?;
3382
3383        // The filter can only be evaluated once every binding it reads is
3384        // bound, so it fuses into the FINAL join and nowhere earlier. The
3385        // budget likewise applies only there: capping an intermediate join
3386        // can starve a later one of rows it needed.
3387        let post = if fuse && is_last { sel.where_.as_ref() } else { None };
3388        let join_budget = if is_last { budget } else { None };
3389
3390        // The planner proposes; sizes decide. A join with no provable equality
3391        // key has nothing to hash on and stays on the reference path.
3392        let keys = sqljoin::hash_keys(join.on.as_ref(), &left_bindings, &rb);
3393        let left_hint = left_src.hint().unwrap_or(usize::MAX);
3394        let strategy = sqljoin::choose(exec, keys.len(), left_hint, right_rows.len());
3395
3396        let (out, removed, consumed) = match strategy {
3397            Strategy::NestedLoop => join_nested_loop(
3398                left_src.as_mut(), &left_bindings, join, &right_rows, &rb,
3399                join_budget, post, ctx,
3400            )?,
3401            Strategy::Hash => join_hash(
3402                left_src.as_mut(), &left_bindings, join, &right_rows, &rb, &keys,
3403                join_budget, post, ctx,
3404            )?,
3405        };
3406
3407        plan.push(Stage::Join {
3408            kind: join.kind,
3409            table: join.table.name.clone(),
3410            binding: rb.clone(),
3411            strategy,
3412            keys: keys.len(),
3413            left_rows: consumed,
3414            right_rows: right_rows.len(),
3415            out_rows: out.len(),
3416            early_stopped: join_budget.is_some_and(|b| out.len() >= b),
3417            post_filter_removed: post.map(|_| removed),
3418        });
3419        left_bindings.push(rb);
3420        // Read the streamed base's counts BEFORE the source is replaced.
3421        if ji == 0 {
3422            if let Some((pulled, kept)) = left_src.stats() {
3423                base_pulled = Some(pulled);
3424                base_kept = Some(kept);
3425            }
3426        }
3427        rows = out;
3428        // The next join reads this join's output, which is already whole.
3429        left_src = Box::new(VecLeft { rows: std::mem::take(&mut rows), at: 0 });
3430    }
3431
3432    // Recover the rows from the last source, and record what the streamed
3433    // base relation actually delivered.
3434    rows = left_src.take_rows();
3435    if let Some(i) = base_scan_at {
3436        if let (Some(pulled), Some(kept)) = (base_pulled, base_kept) {
3437            if let Some(Stage::Scan { rows: r, .. }) = plan.stages.get_mut(i) {
3438                *r = pulled;
3439            }
3440            if let Some(j) = base_prefilter_at {
3441                if let Some(Stage::Prefilter { in_rows, out_rows, .. }) =
3442                    plan.stages.get_mut(j)
3443                {
3444                    *in_rows = pulled;
3445                    *out_rows = kept;
3446                }
3447            }
3448        }
3449    }
3450
3451    // ── 2. WHERE ────────────────────────────────────────────────────────────
3452    if let Some(pred) = sel.where_.as_ref().filter(|_| !fuse) {
3453        let in_rows = rows.len();
3454        let mut kept = Vec::with_capacity(rows.len());
3455        for r in rows {
3456            // Only TRUE keeps a row. UNKNOWN excludes it, which is what makes
3457            // `WHERE n.nspname <> 'x'` drop a LEFT JOIN's unmatched rows the
3458            // way Postgres does.
3459            if truthy(&eval(pred, &bind(&r, ctx))?) == Some(true) {
3460                kept.push(r);
3461            }
3462        }
3463        rows = kept;
3464        plan.push(Stage::Filter { in_rows, out_rows: rows.len() });
3465    }
3466
3467    // ── 2b. GROUP BY and aggregates ─────────────────────────────────────────
3468    //
3469    // One path serves both shapes, because an aggregate with no `GROUP BY` IS
3470    // the single-group case — `SELECT count(*) FROM t` is one group holding
3471    // every row. Writing them separately is how the two drift into disagreeing
3472    // about an empty input: `count(*)` over no rows must be 0 for the whole
3473    // table and must produce NO row at all per group when there are no groups.
3474    //
3475    // Each call is reduced over its group's rows first, then the remainder of
3476    // the expression is evaluated with those reductions already in place — so
3477    // `sum(total) / count(*)` is ordinary arithmetic over two literals by the
3478    // time it is evaluated.
3479    let grouping = !sel.group_by.is_empty();
3480    let aggregating = grouping
3481        || sel.items.iter().any(|i| has_aggregate(&i.expr))
3482        || sel.having.as_ref().is_some_and(has_aggregate);
3483    if aggregating {
3484        // Partition. First-seen order is kept so a run is reproducible; SQL
3485        // promises no group order without `ORDER BY`, and a HashMap's would
3486        // change between runs of the same query on the same data.
3487        let mut groups: Vec<(Vec<Value>, Vec<JoinedRow>)> = vec![];
3488        if grouping {
3489            for r in rows {
3490                let b = bind(&r, ctx);
3491                let mut key = Vec::with_capacity(sel.group_by.len());
3492                for g in &sel.group_by {
3493                    key.push(eval(g, &b)?);
3494                }
3495                match groups.iter_mut().find(|(k, _)| {
3496                    k.len() == key.len()
3497                        && k.iter().zip(&key).all(|(a, b)| {
3498                            // NULLs group TOGETHER, which is what GROUP BY
3499                            // does even though `NULL = NULL` is UNKNOWN.
3500                            (a.is_null() && b.is_null())
3501                                || matches!(cmp_values(a, b), Some(std::cmp::Ordering::Equal))
3502                        })
3503                }) {
3504                    Some((_, bucket)) => bucket.push(r),
3505                    None => groups.push((key, vec![r])),
3506                }
3507            }
3508        } else {
3509            // No GROUP BY: exactly one group, even when it is empty. That is
3510            // what makes `count(*)` answer 0 rather than returning no row.
3511            groups.push((vec![], rows));
3512        }
3513
3514        let n_groups = groups.len();
3515        let mut cols: Vec<OutCol> = vec![];
3516        let mut projected: Vec<(Map<String, Value>, JoinedRow)> = vec![];
3517        let empty: JoinedRow = vec![];
3518
3519        for (gi, (_key, grows)) in groups.iter().enumerate() {
3520            // Every non-aggregate expression left after folding is a GROUP BY
3521            // key, which is constant across the group — so any row of it is
3522            // the right scope, and the folder has already refused anything
3523            // that is not.
3524            let scope = grows.first().unwrap_or(&empty);
3525
3526            if let Some(h) = &sel.having {
3527                let folded = fold_aggregates(h, grows, ctx, &sel.group_by)?;
3528                if truthy(&eval(&folded, &bind(scope, ctx))?) != Some(true) {
3529                    continue;
3530                }
3531            }
3532
3533            let mut obj = Map::new();
3534            for item in &sel.items {
3535                let folded = fold_aggregates(&item.expr, grows, ctx, &sel.group_by)?;
3536                let v = eval(&folded, &bind(scope, ctx))?;
3537                // Output columns are named once, from the first group.
3538                if gi == 0 {
3539                    let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3540                    let key = unique_key(&cols, &name);
3541                    obj.insert(key.clone(), v);
3542                    cols.push(OutCol { key, name });
3543                } else {
3544                    let idx = obj.len();
3545                    if let Some(c) = cols.get(idx) {
3546                        obj.insert(c.key.clone(), v);
3547                    }
3548                }
3549            }
3550            projected.push((obj, scope.clone()));
3551        }
3552
3553        plan.notes.push(if grouping {
3554            format!(
3555                "GroupAggregate on {} key(s): {} rows -> {} group(s){}",
3556                sel.group_by.len(),
3557                n_rows_before_group(&plan),
3558                n_groups,
3559                if sel.having.is_some() {
3560                    format!(", HAVING kept {}", projected.len())
3561                } else {
3562                    String::new()
3563                }
3564            )
3565        } else {
3566            format!("Aggregate over {} row(s) -> 1 row", groups[0].1.len())
3567        });
3568        plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3569        let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3570        return Ok((cols, out, plan));
3571    }
3572
3573    // ── 3. the output shape ─────────────────────────────────────────────────
3574    // Resolved from the ROWS when the select list contains a `*`, because only
3575    // the rows know what columns a schemaless source has. With no rows at all
3576    // a `*` yields no columns, which is the honest answer.
3577    //
3578    // From EVERY row, not the first one. Taking the first row's keys loses any
3579    // field that only later documents carry, and it loses it SILENTLY: two
3580    // documents `{a:1}` and `{a:2, later:"x"}` answered `SELECT *` with one
3581    // column, and `later` — which is right there in the store — simply did not
3582    // appear. A schemaless collection has no row that speaks for the others.
3583    // The translator has always taken the union (`columns_for`), and the
3584    // parity harness is what surfaced the disagreement.
3585    //
3586    // Ordering: the user's own fields in the DOCUMENT'S OWN ORDER, then the
3587    // `_`-prefixed provenance columns sorted. Document order is a deliberate
3588    // property — `serde_json`'s `preserve_order` feature is on crate-wide to
3589    // make it possible — and it is what Postgres does, where `*` follows
3590    // column definition order rather than the alphabet. Putting provenance
3591    // last keeps `_hash` from pushing `status` off the screen.
3592    //
3593    // The first attempt at this sorted the user's fields alphabetically to
3594    // match the TRANSLATOR, which had it backwards: the corpus pins document
3595    // order deliberately, so the translator was the one diverging. It now
3596    // sorts nothing but the provenance block either.
3597    //
3598    // `spans` records which output columns each select ITEM owns, so the
3599    // projection below never has to guess. The previous version walked a
3600    // single counter through both stages, and a `*` that skipped an
3601    // already-named column left the counter pointing at the wrong name — a
3602    // drift that happened to be masked by a fallback.
3603    let mut cols: Vec<OutCol> = vec![];
3604    let mut spans: Vec<(usize, usize)> = Vec::with_capacity(sel.items.len());
3605    for item in &sel.items {
3606        let start = cols.len();
3607        match &item.expr {
3608            Expr::Star => {
3609                let mut plain: Vec<String> = vec![];
3610                let mut meta: Vec<String> = vec![];
3611                for r in &rows {
3612                    for (n, _) in bind(r, ctx).flatten() {
3613                        let target = if n.starts_with('_') { &mut meta } else { &mut plain };
3614                        // A star never emits the same column twice.
3615                        if !target.contains(&n) {
3616                            target.push(n);
3617                        }
3618                    }
3619                }
3620                // Only the provenance block is sorted; the user's fields keep
3621                // the document's order.
3622                meta.sort();
3623                for n in plain.into_iter().chain(meta) {
3624                    if !cols.iter().any(|c| c.name == n) {
3625                        cols.push(OutCol { key: n.clone(), name: n });
3626                    }
3627                }
3628            }
3629            Expr::QualifiedStar(q) => {
3630                if let Some(first) = rows.first() {
3631                    for (n, _) in bind(first, ctx).flatten_binding(q) {
3632                        if !cols.iter().any(|c| c.name == n) {
3633                            cols.push(OutCol { key: n.clone(), name: n });
3634                        }
3635                    }
3636                }
3637            }
3638            _ => {
3639                let name = item.alias.clone().unwrap_or_else(|| derived_name(&item.expr));
3640                // Postgres permits duplicate output names and clients index
3641                // positionally as well as by name, so a collision is NOT
3642                // renamed — silently renaming a column is worse than a
3643                // duplicate, because generated SQL looks for the name it asked
3644                // for. Only the internal KEY is disambiguated.
3645                let key = unique_key(&cols, &name);
3646                cols.push(OutCol { key, name });
3647            }
3648        }
3649        spans.push((start, cols.len()));
3650    }
3651
3652    // ── 4. project ──────────────────────────────────────────────────────────
3653    // The source row is kept beside each projected row, because ORDER BY may
3654    // sort on an expression over columns that are NOT in the select list.
3655    let mut projected: Vec<(Map<String, Value>, JoinedRow)> = Vec::with_capacity(rows.len());
3656    for r in rows {
3657        let b = bind(&r, ctx);
3658        let mut obj = Map::new();
3659        for (i, item) in sel.items.iter().enumerate() {
3660            let (start, end) = spans[i];
3661            match &item.expr {
3662                Expr::Star => {
3663                    for (n, v) in b.flatten() {
3664                        if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3665                            obj.entry(c.key.clone()).or_insert(v);
3666                        }
3667                    }
3668                }
3669                Expr::QualifiedStar(q) => {
3670                    for (n, v) in b.flatten_binding(q) {
3671                        if let Some(c) = cols[start..end].iter().find(|c| c.name == n) {
3672                            obj.entry(c.key.clone()).or_insert(v);
3673                        }
3674                    }
3675                }
3676                _ => {
3677                    let v = eval(&item.expr, &b)?;
3678                    if let Some(c) = cols.get(start) {
3679                        obj.insert(c.key.clone(), v);
3680                    }
3681                }
3682            }
3683        }
3684        projected.push((obj, r));
3685    }
3686
3687    plan.push(Stage::Project { columns: cols.len(), out_rows: projected.len() });
3688
3689    // ── 5. DISTINCT ─────────────────────────────────────────────────────────
3690    if sel.distinct {
3691        let in_rows = projected.len();
3692        let mut seen: std::collections::HashSet<String> = std::collections::HashSet::new();
3693        // Keyed on the PROJECTED values in output order, which is what
3694        // DISTINCT means — not on the source rows.
3695        projected.retain(|(obj, _)| seen.insert(row_key(&cols, obj)));
3696        plan.push(Stage::Distinct { in_rows, out_rows: projected.len() });
3697    }
3698
3699    let out = finish(sel, &cols, projected, ctx, &mut plan)?;
3700    Ok((cols, out, plan))
3701}
3702
3703/// The row count the last stage reported, for the group note. Read back from
3704/// the plan rather than tracked separately, so the number in the note and the
3705/// number in the plan cannot disagree.
3706fn n_rows_before_group(plan: &Plan) -> usize {
3707    plan.stages
3708        .iter()
3709        .rev()
3710        .find_map(|st| match st {
3711            Stage::Filter { out_rows, .. } => Some(*out_rows),
3712            Stage::Join { out_rows, .. } => Some(*out_rows),
3713            Stage::Prefilter { out_rows, .. } => Some(*out_rows),
3714            Stage::Scan { rows, .. } => Some(*rows),
3715            _ => None,
3716        })
3717        .unwrap_or(0)
3718}
3719
3720/// Compare two precomputed sort-key tuples under a sort list.
3721///
3722/// One comparator for the query's `ORDER BY` and for an aggregate's own, so
3723/// `array_agg(x ORDER BY y DESC NULLS LAST)` and
3724/// `SELECT ... ORDER BY y DESC NULLS LAST` cannot order the same values
3725/// differently.
3726fn sort_keys(a: &[Value], b: &[Value], order_by: &[OrderBy]) -> std::cmp::Ordering {
3727    for (i, ob) in order_by.iter().enumerate() {
3728        let (Some(x), Some(y)) = (a.get(i), b.get(i)) else { continue };
3729        let ord = match (x.is_null(), y.is_null()) {
3730            (true, true) => std::cmp::Ordering::Equal,
3731            // NULL placement is a direction-independent choice, so it is
3732            // applied BEFORE the DESC reversal rather than being flipped by it.
3733            (true, false) => {
3734                return if ob.nulls_first {
3735                    std::cmp::Ordering::Less
3736                } else {
3737                    std::cmp::Ordering::Greater
3738                }
3739            }
3740            (false, true) => {
3741                return if ob.nulls_first {
3742                    std::cmp::Ordering::Greater
3743                } else {
3744                    std::cmp::Ordering::Less
3745                }
3746            }
3747            (false, false) => cmp_values(x, y).unwrap_or(std::cmp::Ordering::Equal),
3748        };
3749        let ord = if matches!(ob.dir, Dir::Desc) { ord.reverse() } else { ord };
3750        if !ord.is_eq() {
3751            return ord;
3752        }
3753    }
3754    std::cmp::Ordering::Equal
3755}
3756
3757/// `ORDER BY`, then `OFFSET` / `LIMIT` — the tail every query shape shares.
3758fn finish(
3759    sel: &Select,
3760    cols: &[OutCol],
3761    mut projected: Vec<(Map<String, Value>, JoinedRow)>,
3762    ctx: EvalCtx,
3763    plan: &mut Plan,
3764) -> Result<Vec<Value>> {
3765    // ── 6. ORDER BY ─────────────────────────────────────────────────────────
3766    if !sel.order_by.is_empty() {
3767        // Sort keys are precomputed so the comparator cannot fail halfway
3768        // through a sort — an error raised inside `sort_by` would leave the
3769        // rows in an arbitrary order and still return them.
3770        let mut keyed: Vec<(Vec<Value>, (Map<String, Value>, JoinedRow))> = vec![];
3771        for (obj, src) in projected {
3772            let mut key = vec![];
3773            for ob in &sel.order_by {
3774                let v = match (ob.ordinal, &ob.expr) {
3775                    (Some(n), _) => {
3776                        let c = cols.get(n - 1).ok_or_else(|| {
3777                            anyhow::anyhow!(
3778                                "ORDER BY {} is out of range: the select list has {} \
3779                                 column(s)", n, cols.len())
3780                        })?;
3781                        obj.get(&c.key).cloned().unwrap_or(Value::Null)
3782                    }
3783                    // `ORDER BY "Schema"` — a bare name that is an OUTPUT
3784                    // column sorts by the projected value, as SQL says; psql's
3785                    // `\dP+` orders by its aliases. Only when no output column
3786                    // has the name does it fall through to the source row.
3787                    (None, Some(Expr::Column { qual: None, name }))
3788                        if cols.iter().any(|c| c.name == *name) =>
3789                    {
3790                        let c = cols.iter().find(|c| c.name == *name).expect("checked");
3791                        obj.get(&c.key).cloned().unwrap_or(Value::Null)
3792                    }
3793                    (None, Some(e)) => {
3794                        // An ORDER BY expression may name a column that is not
3795                        // in the select list, so it is evaluated against the
3796                        // SOURCE row.
3797                        eval(e, &bind(&src, ctx))?
3798                    }
3799                    (None, None) => Value::Null,
3800                };
3801                key.push(v);
3802            }
3803            keyed.push((key, (obj, src)));
3804        }
3805
3806        keyed.sort_by(|a, b| sort_keys(&a.0, &b.0, &sel.order_by));
3807
3808        projected = keyed.into_iter().map(|(_, row)| row).collect();
3809        plan.push(Stage::Sort { keys: sel.order_by.len(), rows: projected.len() });
3810    }
3811
3812    // ── 7. OFFSET / LIMIT ───────────────────────────────────────────────────
3813    let mut out: Vec<Value> = projected
3814        .into_iter()
3815        .map(|(obj, _)| Value::Object(obj))
3816        .collect();
3817    let in_rows = out.len();
3818    if let Some(off) = sel.offset {
3819        out = if off >= out.len() { vec![] } else { out.split_off(off) };
3820    }
3821    if let Some(lim) = sel.limit {
3822        out.truncate(lim);
3823    }
3824    if sel.limit.is_some() || sel.offset.is_some() {
3825        plan.push(Stage::Limit {
3826            limit: sel.limit,
3827            offset: sel.offset,
3828            in_rows,
3829            out_rows: out.len(),
3830        });
3831    }
3832
3833    Ok(out)
3834}
3835
3836// ─────────────────────────────────────────────────────────────────────────────
3837// The two join implementations
3838// ─────────────────────────────────────────────────────────────────────────────
3839
3840/// Apply the post-join filter to one produced row.
3841///
3842/// # An `ON` predicate and a post-join `WHERE` predicate are NOT the same thing
3843///
3844/// The physical join evaluates both inside one loop, which is where the
3845/// performance comes from. It does NOT merge them, and the difference is
3846/// semantic law rather than a matter of taste:
3847///
3848/// ```text
3849///   LEFT JOIN ... ON a.x = b.x AND b.tag = 'q'     keeps every left row
3850///   LEFT JOIN ... ON a.x = b.x WHERE b.tag = 'q'   discards the outer rows
3851/// ```
3852///
3853/// So the order is fixed and each step sees only what it should:
3854///
3855/// 1. form the candidate pair
3856/// 2. evaluate `ON` — and this ALONE decides whether the row counts as
3857///    matched, for both the left row and the right row
3858/// 3. synthesise NULLs if the outer join requires it
3859/// 4. evaluate the post-join filter
3860/// 5. count the survivor toward the row budget
3861///
3862/// Step 2 is the load-bearing one. If the filter were allowed to influence
3863/// "matched", a left row whose only partner fails the filter would be
3864/// NULL-extended — and a filter like `WHERE b.tag IS NULL` would then ACCEPT
3865/// that synthesised row, inventing output that the unfused pipeline never
3866/// produces. It is the same trap that made the first predicate-pushdown
3867/// attempt wrong, in a different place.
3868fn keep_row(cand: &JoinedRow, post: Option<&Expr>, removed: &mut usize, ctx: EvalCtx) -> Result<bool> {
3869    let Some(p) = post else { return Ok(true) };
3870    // Only TRUE keeps a row, exactly as a standalone `WHERE` stage does.
3871    if truthy(&eval(p, &bind(cand, ctx))?) == Some(true) {
3872        Ok(true)
3873    } else {
3874        *removed += 1;
3875        Ok(false)
3876    }
3877}
3878
3879/// `RIGHT`/`FULL`: every right row that found no partner survives, with every
3880/// left binding NULL.
3881///
3882/// Shared by both strategies so the two cannot drift apart on the subtlest
3883/// part of outer-join semantics.
3884#[allow(clippy::too_many_arguments)]
3885fn emit_unmatched_right(
3886    out: &mut Vec<JoinedRow>,
3887    kind: JoinKind,
3888    left_bindings: &[String],
3889    right_rows: &[Value],
3890    right_matched: &[bool],
3891    rb: &str,
3892    post: Option<&Expr>,
3893    removed: &mut usize,
3894    ctx: EvalCtx,
3895) -> Result<()> {
3896    if !matches!(kind, JoinKind::Right | JoinKind::Full) {
3897        return Ok(());
3898    }
3899    for (ri, right) in right_rows.iter().enumerate() {
3900        if right_matched[ri] {
3901            continue;
3902        }
3903        let mut cand: JoinedRow = left_bindings.iter().map(|b| (b.clone(), None)).collect();
3904        cand.push((rb.to_string(), Some(right.clone())));
3905        // Outer rows face the post-join filter too — it is a `WHERE`, and a
3906        // `WHERE` applies to every row the join produced.
3907        if keep_row(&cand, post, removed, ctx)? {
3908            out.push(cand);
3909        }
3910    }
3911    Ok(())
3912}
3913
3914/// `LATERAL`: the right side is a subquery re-run for each left row, with
3915/// that row as its scope. INNER and CROSS keep matched pairs; LEFT keeps a
3916/// left row with a NULL right side when the subquery produced nothing.
3917///
3918/// Returns `(rows, removed by the post filter, left rows consumed, right rows
3919/// produced in total)`.
3920fn join_lateral(
3921    left_src: &mut dyn LeftSource,
3922    join: &Join,
3923    resolve: &Resolver,
3924    budget: Option<usize>,
3925    post: Option<&Expr>,
3926    ctx: EvalCtx,
3927) -> Result<(Vec<JoinedRow>, usize, usize, usize)> {
3928    let sub = join
3929        .table
3930        .sub
3931        .as_deref()
3932        .ok_or_else(|| anyhow::anyhow!("LATERAL requires a subquery"))?;
3933    let rb = join.table.binding();
3934    let mut out: Vec<JoinedRow> = vec![];
3935    let mut removed = 0usize;
3936    let mut consumed = 0usize;
3937    let mut produced = 0usize;
3938    while let Some(left) = {
3939        if budget.is_some_and(|b| out.len() >= b) { None } else { left_src.next_left()? }
3940    } {
3941        consumed += 1;
3942        let scope = bind(&left, ctx);
3943        let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), Some(&scope))?;
3944        produced += rows.len();
3945        let mut matched = false;
3946        for r in rows {
3947            let m = match r {
3948                Value::Object(m) => m,
3949                _ => Map::new(),
3950            };
3951            let mut named = Map::new();
3952            for (i, c) in cols.iter().enumerate() {
3953                let name = join.table.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
3954                named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
3955            }
3956            let mut cand = left.clone();
3957            cand.push((rb.clone(), Some(Value::Object(named))));
3958            let on_ok = match &join.on {
3959                Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
3960                None => true,
3961            };
3962            if !on_ok {
3963                continue;
3964            }
3965            matched = true;
3966            if keep_row(&cand, post, &mut removed, ctx)? {
3967                out.push(cand);
3968            }
3969        }
3970        if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
3971            let mut cand = left.clone();
3972            cand.push((rb.clone(), None));
3973            if keep_row(&cand, post, &mut removed, ctx)? {
3974                out.push(cand);
3975            }
3976        }
3977    }
3978    Ok((out, removed, consumed, produced))
3979}
3980
3981/// The reference strategy: consider every pair.
3982///
3983/// Quadratic, and kept forever anyway. It is the semantic fallback for
3984/// predicates the hash path cannot key on, the implementation of record for
3985/// non-equality joins, and the oracle the differential tests compare against.
3986#[allow(clippy::too_many_arguments)]
3987fn join_nested_loop(
3988    left_src: &mut dyn LeftSource,
3989    left_bindings: &[String],
3990    join: &Join,
3991    right_rows: &[Value],
3992    rb: &str,
3993    budget: Option<usize>,
3994    post: Option<&Expr>,
3995    ctx: EvalCtx,
3996) -> Result<(Vec<JoinedRow>, usize, usize)> {
3997    let mut out: Vec<JoinedRow> = vec![];
3998    let mut removed = 0usize;
3999    // Which right rows found a partner — only needed for RIGHT and FULL.
4000    let mut right_matched = vec![false; right_rows.len()];
4001
4002    let mut consumed = 0usize;
4003    while let Some(left) = {
4004        if budget.is_some_and(|b| out.len() >= b) {
4005            // Stop ASKING. With a streaming left side this is what keeps the
4006            // source from producing rows nobody will look at.
4007            None
4008        } else {
4009            left_src.next_left()?
4010        }
4011    } {
4012        consumed += 1;
4013        let left = &left;
4014        // Decided by the ON clause ALONE. See `keep_row` for why the
4015        // post-join filter must not touch this.
4016        let mut matched = false;
4017        for (ri, right) in right_rows.iter().enumerate() {
4018            let mut cand: JoinedRow = left.clone();
4019            cand.push((rb.to_string(), Some(right.clone())));
4020            let joins_here = match &join.on {
4021                // CROSS JOIN has no predicate: every pair survives.
4022                None => true,
4023                // An ON that evaluates to UNKNOWN does NOT join, exactly
4024                // as in SQL. Treating UNKNOWN as a match would invent
4025                // pairings out of missing data.
4026                Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4027            };
4028            if joins_here {
4029                matched = true;
4030                right_matched[ri] = true;
4031                if keep_row(&cand, post, &mut removed, ctx)? {
4032                    out.push(cand);
4033                }
4034            }
4035        }
4036        // LEFT/FULL: an unmatched left row survives with a NULL right.
4037        if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4038            let mut cand: JoinedRow = left.clone();
4039            cand.push((rb.to_string(), None));
4040            if keep_row(&cand, post, &mut removed, ctx)? {
4041                out.push(cand);
4042            }
4043        }
4044    }
4045
4046    // Right-outer rows are appended AFTER every left row, so once the budget
4047    // is met they sit beyond the prefix `LIMIT` will keep and cannot affect the
4048    // answer. Skipping them is the point of the budget; emitting them would be
4049    // correct but pointless work.
4050    if !budget.is_some_and(|b| out.len() >= b) {
4051        emit_unmatched_right(
4052            &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4053            &mut removed, ctx,
4054        )?;
4055    }
4056    Ok((out, removed, consumed))
4057}
4058
4059/// The fast strategy: bucket the right relation, probe it with the left.
4060///
4061/// The hash table is used ONLY to narrow the candidate set. Every surviving
4062/// pair is then evaluated against the complete, unmodified `ON` expression —
4063/// the same call the nested loop makes — so the two strategies answer with the
4064/// same expression evaluated on the same rows. See [`crate::sqljoin`] for why
4065/// bucketing alone would be unsound here.
4066#[allow(clippy::too_many_arguments)]
4067fn join_hash(
4068    left_src: &mut dyn LeftSource,
4069    left_bindings: &[String],
4070    join: &Join,
4071    right_rows: &[Value],
4072    rb: &str,
4073    keys: &[(Expr, Expr)],
4074    budget: Option<usize>,
4075    post: Option<&Expr>,
4076    ctx: EvalCtx,
4077) -> Result<(Vec<JoinedRow>, usize, usize)> {
4078    debug_assert!(!keys.is_empty(), "the planner must not choose Hash with no keys");
4079
4080    // ── build: the right relation, keyed ────────────────────────────────────
4081    let side = sqljoin::HashSide::build(right_rows.len(), |i| {
4082        // A right key reads only the right binding — that is what the planner
4083        // proved — so binding the row alone is sufficient and correct.
4084        let one: JoinedRow = vec![(rb.to_string(), Some(right_rows[i].clone()))];
4085        let b = bind(&one, ctx);
4086        let mut k = Vec::with_capacity(keys.len());
4087        for (_, right_expr) in keys {
4088            match sqljoin::hkey(&eval(right_expr, &b)?) {
4089                Some(h) => k.push(h),
4090                // A NULL anywhere in the key means this row joins nothing.
4091                None => return Ok(None),
4092            }
4093        }
4094        Ok(Some(k))
4095    })?;
4096
4097    // ── probe: the accumulated left rows ────────────────────────────────────
4098    let mut out: Vec<JoinedRow> = vec![];
4099    let mut removed = 0usize;
4100    let mut right_matched = vec![false; right_rows.len()];
4101
4102    let mut consumed = 0usize;
4103    while let Some(left) = {
4104        if budget.is_some_and(|b| out.len() >= b) {
4105            None
4106        } else {
4107            left_src.next_left()?
4108        }
4109    } {
4110        consumed += 1;
4111        let left = &left;
4112        let lb = bind(left, ctx);
4113        let mut lk = Vec::with_capacity(keys.len());
4114        let mut null_key = false;
4115        for (left_expr, _) in keys {
4116            match sqljoin::hkey(&eval(left_expr, &lb)?) {
4117                Some(h) => lk.push(h),
4118                None => {
4119                    null_key = true;
4120                    break;
4121                }
4122            }
4123        }
4124
4125        let mut matched = false;
4126        // A NULL key matches nothing, so the bucket is not consulted. A
4127        // shortcut, not a safeguard: the confirm step below would reject
4128        // those pairs anyway, since `NULL = NULL` is UNKNOWN.
4129        if !null_key {
4130            for &ri in side.probe(&lk) {
4131                let mut cand: JoinedRow = left.clone();
4132                cand.push((rb.to_string(), Some(right_rows[ri].clone())));
4133                // Confirm. The bucket only suggested this pair.
4134                let joins_here = match &join.on {
4135                    None => true,
4136                    Some(on) => truthy(&eval(on, &bind(&cand, ctx))?) == Some(true),
4137                };
4138                if joins_here {
4139                    matched = true;
4140                    right_matched[ri] = true;
4141                    if keep_row(&cand, post, &mut removed, ctx)? {
4142                        out.push(cand);
4143                    }
4144                }
4145            }
4146        }
4147        if !matched && matches!(join.kind, JoinKind::Left | JoinKind::Full) {
4148            let mut cand: JoinedRow = left.clone();
4149            cand.push((rb.to_string(), None));
4150            if keep_row(&cand, post, &mut removed, ctx)? {
4151                out.push(cand);
4152            }
4153        }
4154    }
4155
4156    // See the note in `join_nested_loop`: beyond the budget these rows cannot
4157    // survive the `LIMIT` prefix.
4158    if !budget.is_some_and(|b| out.len() >= b) {
4159        emit_unmatched_right(
4160            &mut out, join.kind, left_bindings, right_rows, &right_matched, rb, post,
4161            &mut removed, ctx,
4162        )?;
4163    }
4164    Ok((out, removed, consumed))
4165}
4166
4167/// Apply the pushed conjuncts for one relation, before it reaches the join.
4168///
4169/// Evaluated against the relation's own binding alone, which is exactly what
4170/// the planner proved is sufficient: a pushed conjunct references only this
4171/// relation, so binding it alone gives the same answer the post-join `WHERE`
4172/// will give for the same row.
4173fn prefilter(
4174    rows: Vec<Value>,
4175    binding: &str,
4176    push: &Pushdown,
4177    plan: &mut Plan,
4178    ctx: EvalCtx,
4179) -> Result<Vec<Value>> {
4180    let Some(preds) = push.for_binding(binding) else { return Ok(rows) };
4181    if preds.is_empty() {
4182        return Ok(rows);
4183    }
4184    let in_rows = rows.len();
4185    let mut kept = Vec::with_capacity(rows.len());
4186    for row in rows {
4187        let one: JoinedRow = vec![(binding.to_string(), Some(row))];
4188        let b = bind(&one, ctx);
4189        let mut keep = true;
4190        for p in preds {
4191            // Only TRUE keeps a row, exactly as in `WHERE`. Treating UNKNOWN
4192            // as a keep would make the pre-filter weaker than the filter it
4193            // duplicates, which is harmless; treating it as a drop when the
4194            // real filter would keep it would not be — so the two must agree,
4195            // and they do because this is the same evaluator call.
4196            if truthy(&eval(p, &b)?) != Some(true) {
4197                keep = false;
4198                break;
4199            }
4200        }
4201        if keep {
4202            // Unwrap the row back out of the single-binding wrapper.
4203            if let Some((_, Some(v))) = one.into_iter().next() {
4204                kept.push(v);
4205            }
4206        }
4207    }
4208    plan.push(Stage::Prefilter {
4209        binding: binding.to_string(),
4210        predicates: preds.len(),
4211        in_rows,
4212        out_rows: kept.len(),
4213    });
4214    Ok(kept)
4215}
4216
4217/// Materialise one FROM item: a named relation through the resolver, a
4218/// derived table by running its query, or a table function by evaluating it.
4219fn fetch(t: &TableRef, resolve: &Resolver, ctx: EvalCtx) -> Result<Box<dyn Relation>> {
4220    // `FROM (SELECT ...) AS t` — the relation IS the subquery's output, keyed
4221    // by output NAME (what the enclosing query addresses), with `AS t(a, b)`
4222    // renaming positionally.
4223    if let Some(sub) = &t.sub {
4224        let (cols, rows, _) = execute_inner(sub, resolve, Opts::default(), ctx.outer)?;
4225        let out = rows
4226            .into_iter()
4227            .map(|r| {
4228                let m = match r {
4229                    Value::Object(m) => m,
4230                    _ => Map::new(),
4231                };
4232                let mut named = Map::new();
4233                for (i, c) in cols.iter().enumerate() {
4234                    let name = t.col_aliases.get(i).cloned().unwrap_or_else(|| c.name.clone());
4235                    // Duplicate output names keep the FIRST, as an unqualified
4236                    // reference to an ambiguous name would resolve to.
4237                    named.entry(name).or_insert(m.get(&c.key).cloned().unwrap_or(Value::Null));
4238                }
4239                Value::Object(named)
4240            })
4241            .collect();
4242        return Ok(from_vec(out));
4243    }
4244
4245    // A table function. Its arguments may read the ENCLOSING row — psql's
4246    // `\dy` writes `unnest(evttags)` over the outer relation's column — so
4247    // they are evaluated in the outer scope.
4248    if let Some(args) = &t.args {
4249        let empty: JoinedRow = vec![];
4250        let scope = bind(&empty, ctx);
4251        let col = |i: usize, default: &str| -> String {
4252            t.col_aliases.get(i).cloned().unwrap_or_else(|| default.to_string())
4253        };
4254        let rows: Vec<Value> = match t.name.as_str() {
4255            "generate_series" => {
4256                let a = num(&eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_series() needs a start"))?, &scope)?);
4257                let b = num(&eval(args.get(1).ok_or_else(|| anyhow::anyhow!("generate_series() needs a stop"))?, &scope)?);
4258                let step = match args.get(2) {
4259                    Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4260                    None => 1.0,
4261                };
4262                match (a, b) {
4263                    // A NULL bound yields no rows, as Postgres answers.
4264                    (Some(a), Some(b)) if step != 0.0 => {
4265                        let mut out = vec![];
4266                        let mut x = a;
4267                        while (step > 0.0 && x <= b) || (step < 0.0 && x >= b) {
4268                            let mut m = Map::new();
4269                            m.insert(col(0, "generate_series"), from_f64(x));
4270                            out.push(Value::Object(m));
4271                            x += step;
4272                            if out.len() > 1_000_000 {
4273                                bail!("generate_series() would produce more than a million rows");
4274                            }
4275                        }
4276                        out
4277                    }
4278                    (Some(_), Some(_)) => bail!("generate_series() step cannot equal zero"),
4279                    _ => vec![],
4280                }
4281            }
4282            "unnest" => match eval(args.first().ok_or_else(|| anyhow::anyhow!("unnest() needs an array"))?, &scope)? {
4283                Value::Array(items) => items
4284                    .into_iter()
4285                    .map(|v| {
4286                        let mut m = Map::new();
4287                        m.insert(col(0, "unnest"), v);
4288                        Value::Object(m)
4289                    })
4290                    .collect(),
4291                // unnest(NULL) is no rows.
4292                _ => vec![],
4293            },
4294            // `generate_subscripts(arr, 1)` — the 1-based positions of an
4295            // array, which is how SQLAlchemy numbers the columns of a primary
4296            // key. An empty or non-array argument yields no rows, as Postgres
4297            // answers.
4298            "generate_subscripts" => {
4299                let dim = match args.get(1) {
4300                    Some(e) => num(&eval(e, &scope)?).unwrap_or(1.0),
4301                    None => 1.0,
4302                };
4303                match eval(args.first().ok_or_else(|| anyhow::anyhow!("generate_subscripts() needs an array"))?, &scope)? {
4304                    // Only one dimension is representable here: a JSON array
4305                    // is a list, not a matrix. Asking for dimension 2 is
4306                    // therefore genuinely empty rather than an error.
4307                    Value::Array(items) if dim == 1.0 => (1..=items.len())
4308                        .map(|i| {
4309                            let mut m = Map::new();
4310                            m.insert(col(0, "generate_subscripts"), from_f64(i as f64));
4311                            Value::Object(m)
4312                        })
4313                        .collect(),
4314                    _ => vec![],
4315                }
4316            }
4317            // NEDB has no partitioning, so a partition tree is empty for every
4318            // relation — the truthful answer, and what lets `\dP+` run.
4319            "pg_partition_tree" | "pg_partition_ancestors" => vec![],
4320            other => bail!(
4321                "the table function {}() is not implemented. It is refused rather \
4322                 than answered with no rows, because an empty relation reads as \
4323                 missing DATA rather than a missing feature",
4324                other
4325            ),
4326        };
4327        return Ok(from_vec(rows));
4328    }
4329
4330    match resolve(&t.name)? {
4331        Some(rel) => Ok(rel),
4332        // Named rather than silently empty: an unknown table that answered
4333        // with no rows would look exactly like an empty one.
4334        None => bail!("relation {:?} does not exist", t.name),
4335    }
4336}
4337
4338/// Where a join reads its LEFT rows from.
4339///
4340/// Both strategies consume the left side in a SINGLE forward pass — the
4341/// nested loop iterates it once, and the hash join probes with it once — so an
4342/// iterator is a natural fit and no rewinding is needed. That is what makes
4343/// the driving relation streamable while the inner side stays materialised.
4344trait LeftSource {
4345    fn next_left(&mut self) -> Result<Option<JoinedRow>>;
4346    /// Best guess at the row count, for the strategy planner.
4347    fn hint(&self) -> Option<usize>;
4348    /// Whatever rows remain, for a query with no join at all.
4349    fn take_rows(&mut self) -> Vec<JoinedRow>;
4350    /// `(pulled, kept)` when this is a streamed base relation.
4351    fn stats(&self) -> Option<(usize, usize)> {
4352        None
4353    }
4354}
4355
4356/// The driving relation, pulled on demand and pre-filtered inline.
4357///
4358/// Pulling lazily is the whole point: with a row budget, a `LIMIT 20` over a
4359/// join stops asking for rows long before the source is exhausted, so the
4360/// source never has to produce the rest.
4361struct StreamLeft<'a> {
4362    rel: Box<dyn Relation>,
4363    binding: String,
4364    preds: Vec<Expr>,
4365    ctx: EvalCtx<'a>,
4366    /// Rows actually requested from the source. Reported as the scan's
4367    /// `actual rows`, which for a streamed relation is the honest number —
4368    /// the total is not merely unknown, it is irrelevant to what happened.
4369    pulled: usize,
4370    kept: usize,
4371}
4372
4373impl<'a> LeftSource for StreamLeft<'a> {
4374    fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4375        while let Some(row) = self.rel.next_row()? {
4376            self.pulled += 1;
4377            let one: JoinedRow = vec![(self.binding.clone(), Some(row))];
4378            if !self.preds.is_empty() {
4379                let b = bind(&one, self.ctx);
4380                let mut keep = true;
4381                for p in &self.preds {
4382                    if truthy(&eval(p, &b)?) != Some(true) {
4383                        keep = false;
4384                        break;
4385                    }
4386                }
4387                if !keep {
4388                    continue;
4389                }
4390            }
4391            self.kept += 1;
4392            return Ok(Some(one));
4393        }
4394        Ok(None)
4395    }
4396    fn hint(&self) -> Option<usize> {
4397        // The source's own count, BEFORE the inline pre-filter. An
4398        // over-estimate, which only ever biases the planner toward the hash
4399        // path — and the two paths are proven equivalent, so a biased choice
4400        // costs time at worst and never correctness.
4401        self.rel.size_hint()
4402    }
4403    fn take_rows(&mut self) -> Vec<JoinedRow> {
4404        // Only reached when there is no join, and the base is materialised in
4405        // that case, so this drains what is left for completeness.
4406        let mut out = vec![];
4407        while let Ok(Some(r)) = self.next_left() {
4408            out.push(r);
4409        }
4410        out
4411    }
4412    fn stats(&self) -> Option<(usize, usize)> {
4413        Some((self.pulled, self.kept))
4414    }
4415}
4416
4417/// An already-materialised left side: the output of a previous join, or a
4418/// base relation in a query the streaming path does not cover.
4419struct VecLeft {
4420    rows: Vec<JoinedRow>,
4421    at: usize,
4422}
4423
4424impl LeftSource for VecLeft {
4425    fn next_left(&mut self) -> Result<Option<JoinedRow>> {
4426        let r = self.rows.get(self.at).cloned();
4427        if r.is_some() {
4428            self.at += 1;
4429        }
4430        Ok(r)
4431    }
4432    fn hint(&self) -> Option<usize> {
4433        Some(self.rows.len().saturating_sub(self.at))
4434    }
4435    fn take_rows(&mut self) -> Vec<JoinedRow> {
4436        let mut v = std::mem::take(&mut self.rows);
4437        if self.at > 0 {
4438            v = v.split_off(self.at);
4439        }
4440        self.at = 0;
4441        v
4442    }
4443}
4444
4445/// Pull a relation completely into memory.
4446///
4447/// Used for the INNER side of a join, which genuinely has to be whole: a hash
4448/// join must build its table before probing, and a nested loop re-scans it for
4449/// every left row. Streaming it would save nothing, so this says plainly that
4450/// it is being materialised on purpose rather than by omission.
4451fn drain(mut rel: Box<dyn Relation>) -> Result<Vec<Value>> {
4452    let mut out = Vec::with_capacity(rel.size_hint().unwrap_or(0));
4453    while let Some(row) = rel.next_row()? {
4454        out.push(row);
4455    }
4456    Ok(out)
4457}
4458
4459/// Parse and run in one call.
4460pub fn run(sql: &str, resolve: &Resolver) -> Result<(Vec<OutCol>, Vec<Value>)> {
4461    let sel = parse(sql)?;
4462    execute(&sel, resolve)
4463}
4464
4465#[cfg(test)]
4466mod lexer_tests {
4467    use super::*;
4468
4469    fn kinds(src: &str) -> Vec<Tok> {
4470        let mut t = lex(src).expect("lexes");
4471        t.pop(); // drop Eof
4472        t
4473    }
4474
4475    #[test]
4476    fn a_word_keeps_both_its_canonical_and_raw_spelling() {
4477        // A column may legitimately be called `count` or `value`; folding case
4478        // in the lexer would later look up a key the data does not have.
4479        assert_eq!(
4480            kinds("Select"),
4481            vec![Tok::Word { upper: "SELECT".into(), raw: "Select".into() }]
4482        );
4483    }
4484
4485    #[test]
4486    fn a_quoted_identifier_is_never_a_keyword() {
4487        assert_eq!(kinds(r#""select""#), vec![Tok::Quoted("select".into())]);
4488        // …and keeps its case, which is the whole point of quoting it.
4489        assert_eq!(kinds(r#""Name""#), vec![Tok::Quoted("Name".into())]);
4490    }
4491
4492    #[test]
4493    fn a_doubled_quote_is_one_literal_quote() {
4494        assert_eq!(kinds("'it''s'"), vec![Tok::Str("it's".into())]);
4495        assert_eq!(kinds(r#""a""b""#), vec![Tok::Quoted("a\"b".into())]);
4496    }
4497
4498    #[test]
4499    fn an_E_string_decodes_the_escapes_catalogue_sql_uses() {
4500        // `array_to_string(d.datacl, E'\n')` appears verbatim in psql's \l.
4501        assert_eq!(kinds(r"E'\n'"), vec![Tok::Str("\n".into())]);
4502        assert_eq!(kinds(r"E'a\tb'"), vec![Tok::Str("a\tb".into())]);
4503        // An unknown escape keeps its character rather than vanishing.
4504        assert_eq!(kinds(r"E'\q'"), vec![Tok::Str("q".into())]);
4505    }
4506
4507    #[test]
4508    fn operators_match_longest_first() {
4509        // Order is load-bearing: `!~*` must not tokenise as `!~` plus `*`.
4510        assert_eq!(kinds("!~*"), vec![Tok::Op("!~*".into())]);
4511        assert_eq!(kinds("!~"), vec![Tok::Op("!~".into())]);
4512        assert_eq!(kinds("~*"), vec![Tok::Op("~*".into())]);
4513        assert_eq!(kinds("<>"), vec![Tok::Op("<>".into())]);
4514        assert_eq!(kinds("!="), vec![Tok::Op("!=".into())]);
4515        assert_eq!(kinds(">="), vec![Tok::Op(">=".into())]);
4516        assert_eq!(kinds("::"), vec![Tok::Op("::".into())]);
4517        assert_eq!(kinds("||"), vec![Tok::Op("||".into())]);
4518        assert_eq!(kinds("~"), vec![Tok::Op("~".into())]);
4519    }
4520
4521    #[test]
4522    fn comments_are_skipped_including_nested_block_comments() {
4523        assert_eq!(kinds("1 -- trailing\n"), vec![Tok::Num(1.0)]);
4524        assert_eq!(kinds("1 /* a */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4525        // SQL block comments nest, unlike C's.
4526        assert_eq!(kinds("1 /* a /* b */ c */ 2"), vec![Tok::Num(1.0), Tok::Num(2.0)]);
4527        assert!(lex("1 /* unterminated").is_err());
4528    }
4529
4530    #[test]
4531    fn numbers_parse_including_fractions_and_exponents() {
4532        assert_eq!(kinds("42"), vec![Tok::Num(42.0)]);
4533        assert_eq!(kinds("4.5"), vec![Tok::Num(4.5)]);
4534        assert_eq!(kinds(".5"), vec![Tok::Num(0.5)]);
4535        assert_eq!(kinds("1e3"), vec![Tok::Num(1000.0)]);
4536        assert_eq!(kinds("1e-2"), vec![Tok::Num(0.01)]);
4537        // `1e` is the number 1 followed by an identifier, not a broken number.
4538        assert_eq!(
4539            kinds("1e"),
4540            vec![Tok::Num(1.0), Tok::Word { upper: "E".into(), raw: "e".into() }]
4541        );
4542    }
4543
4544    #[test]
4545    fn an_unterminated_literal_is_an_error_not_a_truncation() {
4546        assert!(lex("'abc").is_err());
4547        assert!(lex(r#""abc"#).is_err());
4548    }
4549
4550    #[test]
4551    fn an_unknown_character_is_REFUSED_rather_than_skipped() {
4552        // Skipping is how a parser silently reads a different query than the
4553        // one it was handed.
4554        let e = lex("SELECT 1 @ 2").unwrap_err().to_string();
4555        assert!(e.contains('@'), "{}", e);
4556    }
4557
4558    #[test]
4559    fn the_real_dn_query_lexes() {
4560        let sql = r#"SELECT n.nspname AS "Name",
4561          pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
4562        FROM pg_catalog.pg_namespace n
4563        WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
4564        ORDER BY 1;"#;
4565        let toks = lex(sql).expect("psql's \\dn must lex");
4566        assert!(toks.contains(&Tok::Quoted("Name".into())));
4567        assert!(toks.contains(&Tok::Op("!~".into())));
4568        assert!(toks.contains(&Tok::Op("<>".into())));
4569        assert!(toks.contains(&Tok::Str("^pg_".into())));
4570    }
4571
4572    #[test]
4573    fn the_real_dt_query_lexes() {
4574        let sql = r#"SELECT n.nspname as "Schema", c.relname as "Name",
4575          CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' END as "Type",
4576          pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
4577        FROM pg_catalog.pg_class c
4578             LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
4579             LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
4580        WHERE c.relkind IN ('r','p','')
4581              AND n.nspname <> 'pg_catalog'
4582              AND n.nspname !~ '^pg_toast'
4583          AND pg_catalog.pg_table_is_visible(c.oid)
4584        ORDER BY 1,2;"#;
4585        let toks = lex(sql).expect("psql's \\dt must lex");
4586        assert!(toks.iter().any(|t| t.is_kw("CASE")));
4587        assert!(toks.iter().any(|t| t.is_kw("LEFT")));
4588        assert!(toks.iter().any(|t| t.is_kw("JOIN")));
4589        // The empty string in `IN ('r','p','')` must survive as a real value.
4590        assert!(toks.contains(&Tok::Str(String::new())));
4591    }
4592}
4593
4594#[cfg(test)]
4595mod parser_tests {
4596    use super::*;
4597    use serde_json::json;
4598
4599    fn col(qual: Option<&str>, name: &str) -> Expr {
4600        Expr::Column { qual: qual.map(str::to_string), name: name.to_string() }
4601    }
4602
4603    #[test]
4604    fn a_bare_select_list_and_from() {
4605        let s = parse("SELECT a, b FROM t").unwrap();
4606        assert_eq!(s.items.len(), 2);
4607        assert_eq!(s.items[0].expr, col(None, "a"));
4608        assert_eq!(s.from.unwrap().name, "t");
4609    }
4610
4611    /// `AS OF SYSTEM TIME <seq>` hangs on the TABLE, which is what makes the
4612    /// query worth having expressible: one relation in the past joined against
4613    /// another at the tip.
4614    #[test]
4615    fn as_of_system_time_is_read_per_table() {
4616        let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 42").unwrap();
4617        assert_eq!(s.from.clone().unwrap().as_of, Some(42));
4618        assert_eq!(s.from.unwrap().alias, None);
4619
4620        // The alias still follows the temporal qualifier.
4621        let s = parse("SELECT o.total FROM orders AS OF SYSTEM TIME 42 o").unwrap();
4622        let t = s.from.unwrap();
4623        assert_eq!((t.as_of, t.alias.as_deref()), (Some(42), Some("o")));
4624
4625        // ...and `AS <alias>` is still an alias. `AS` starts both, and the
4626        // word after it is the only thing that tells them apart -- without
4627        // that lookahead `parse_table_alias` eats `OF` as the alias and leaves
4628        // `SYSTEM TIME 42` in the token stream.
4629        let t = parse("SELECT x FROM orders AS o").unwrap().from.unwrap();
4630        assert_eq!((t.as_of, t.alias.as_deref()), (None, Some("o")));
4631
4632        // Two relations, one in the past: the shape the per-table placement
4633        // exists for.
4634        let s = parse(
4635            "SELECT o.total, n.total FROM orders AS OF SYSTEM TIME 1 o \
4636             JOIN orders n ON o._id = n._id").unwrap();
4637        assert_eq!(s.from.unwrap().as_of, Some(1));
4638        assert_eq!(s.joins[0].table.as_of, None);
4639
4640        // A WHERE after the qualifier still parses.
4641        let s = parse("SELECT total FROM orders AS OF SYSTEM TIME 7 WHERE _id = '1'").unwrap();
4642        assert_eq!(s.from.unwrap().as_of, Some(7));
4643        assert!(s.where_.is_some());
4644    }
4645
4646    /// NQL's own verbs, said in SQL. This is what folding NQL into neSQL
4647    /// means: the SQL side PARSES them and composes joins and aggregates
4648    /// around them, while the NQL engine stays the one implementation that
4649    /// executes them.
4650    #[test]
4651    fn nqls_verbs_are_table_qualifiers_in_sql() {
4652        let t = parse("SELECT _id FROM orders SEARCH 'acme'").unwrap().from.unwrap();
4653        assert_eq!(t.search.as_deref(), Some("acme"));
4654
4655        let t = parse("SELECT _id FROM orders VALID AS OF '2026-01-01'").unwrap().from.unwrap();
4656        assert_eq!(t.valid_as_of.as_deref(), Some("2026-01-01"));
4657
4658        // All of them at once, in any order the writer chose, plus an alias.
4659        let t = parse(
4660            "SELECT o._id FROM orders AS OF SYSTEM TIME 9 VALID AS OF '2026-01-01' \
4661             SEARCH 'acme' o").unwrap().from.unwrap();
4662        assert_eq!(
4663            (t.as_of, t.valid_as_of.as_deref(), t.search.as_deref(), t.alias.as_deref()),
4664            (Some(9), Some("2026-01-01"), Some("acme"), Some("o")));
4665
4666        // Per-table, which is the point: search one relation, join another.
4667        let s = parse(
4668            "SELECT o._id FROM orders SEARCH 'acme' o JOIN drivers d ON o.driver = d._id")
4669            .unwrap();
4670        assert_eq!(s.from.unwrap().search.as_deref(), Some("acme"));
4671        assert_eq!(s.joins[0].table.search, None);
4672    }
4673
4674    /// The verbs are UNRESERVED, and this is the test that keeps them that way.
4675    ///
4676    /// Adding a keyword to a grammar breaks every query that already used the
4677    /// word as a name. PostgreSQL solves it with `unreserved_keyword`; here the
4678    /// equivalent is a lookahead — `VALID` only starts a clause when `AS OF`
4679    /// follows, `SEARCH` only when a string does — so somebody's collection
4680    /// aliased `search` keeps working after we ship a search verb.
4681    #[test]
4682    fn the_new_verbs_do_not_steal_names_that_already_worked() {
4683        let t = parse("SELECT search.total FROM orders search").unwrap().from.unwrap();
4684        assert_eq!((t.alias.as_deref(), t.search.as_deref()), (Some("search"), None));
4685
4686        let t = parse("SELECT valid.total FROM orders valid").unwrap().from.unwrap();
4687        assert_eq!((t.alias.as_deref(), t.valid_as_of.as_deref()), (Some("valid"), None));
4688
4689        // A COLUMN called `search` or `valid` is untouched either way.
4690        assert!(parse("SELECT search, valid FROM orders").is_ok());
4691        assert!(parse("SELECT _id FROM orders WHERE search = 'x'").is_ok());
4692
4693        // And `AS OF` is still not stolen by the alias path.
4694        let t = parse("SELECT x FROM orders AS valid").unwrap().from.unwrap();
4695        assert_eq!(t.alias.as_deref(), Some("valid"));
4696    }
4697
4698    #[test]
4699    fn a_verbs_argument_is_refused_when_it_is_the_wrong_kind_of_thing() {
4700        // `VALID AS OF 42` is a sequence where a date belongs. System time and
4701        // application-time validity are different questions, so they take
4702        // different arguments and the mistake is named rather than coerced.
4703        let e = parse("SELECT _id FROM orders VALID AS OF 42").unwrap_err().to_string();
4704        assert!(e.contains("date string"), "{}", e);
4705    }
4706
4707    #[test]
4708    fn a_wall_clock_as_of_is_refused_with_the_reason() {
4709        // NEDB's history is sequence-addressed, so a timestamp would be an
4710        // approximation of an exact thing. Same refusal the translator makes,
4711        // in the same words, because a client should not learn two answers.
4712        for sql in [
4713            "SELECT total FROM orders AS OF SYSTEM TIME '2026-01-01'",
4714            "SELECT total FROM orders AS OF SYSTEM TIME now()",
4715            "SELECT total FROM orders AS OF SYSTEM TIME -1",
4716            "SELECT total FROM orders AS OF SYSTEM TIME 1.5",
4717        ] {
4718            let e = parse(sql).unwrap_err().to_string();
4719            assert!(e.contains("sequence number"), "{} -> {}", sql, e);
4720        }
4721    }
4722
4723    #[test]
4724    fn a_clause_keyword_is_never_read_as_a_bare_alias() {
4725        // Without the guard, `FROM t WHERE x = 1` parses `t` aliased as
4726        // `WHERE`, the predicate vanishes, and EVERY row comes back — a
4727        // silently wrong answer of the worst kind.
4728        let s = parse("SELECT a FROM t WHERE a = 1").unwrap();
4729        assert_eq!(s.from.clone().unwrap().alias, None);
4730        assert!(s.where_.is_some(), "the WHERE clause must survive");
4731        let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4732        assert_eq!(s.from.unwrap().alias, None);
4733        assert_eq!(s.order_by.len(), 1);
4734    }
4735
4736    #[test]
4737    fn a_real_alias_is_kept_in_both_spellings() {
4738        assert_eq!(parse("SELECT a FROM t x").unwrap().from.unwrap().alias,
4739                   Some("x".to_string()));
4740        assert_eq!(parse("SELECT a FROM t AS x").unwrap().from.unwrap().alias,
4741                   Some("x".to_string()));
4742    }
4743
4744    #[test]
4745    fn a_tables_binding_is_its_alias_else_its_bare_name() {
4746        let t = TableRef::named("pg_catalog.pg_class", Some("c".into()));
4747        assert_eq!(t.binding(), "c");
4748        let t = TableRef::named("pg_catalog.pg_class", None);
4749        assert_eq!(t.binding(), "pg_class", "the schema is not how a column is addressed");
4750    }
4751
4752    #[test]
4753    fn a_qualified_column_keeps_only_its_immediate_qualifier() {
4754        assert_eq!(parse("SELECT n.nspname FROM x").unwrap().items[0].expr,
4755                   col(Some("n"), "nspname"));
4756        // In `public.orders.id` the binding is `orders`; the schema is not
4757        // part of how a column is addressed.
4758        assert_eq!(parse("SELECT public.orders.id FROM x").unwrap().items[0].expr,
4759                   col(Some("orders"), "id"));
4760    }
4761
4762    #[test]
4763    fn an_alias_may_be_a_quoted_string_with_significant_case() {
4764        let s = parse(r#"SELECT n.nspname AS "Name" FROM x"#).unwrap();
4765        assert_eq!(s.items[0].alias, Some("Name".to_string()));
4766    }
4767
4768    #[test]
4769    fn a_schema_qualified_function_drops_its_schema() {
4770        // `pg_catalog.pg_get_userbyid` is the same function as
4771        // `pg_get_userbyid`; the schema is not part of its identity here.
4772        let s = parse("SELECT pg_catalog.pg_get_userbyid(n.nspowner) FROM x").unwrap();
4773        match &s.items[0].expr {
4774            Expr::Func { name, args } => {
4775                assert_eq!(name, "pg_get_userbyid");
4776                assert_eq!(args.len(), 1);
4777                assert_eq!(args[0], col(Some("n"), "nspowner"));
4778            }
4779            other => panic!("{:?}", other),
4780        }
4781    }
4782
4783    #[test]
4784    fn operator_precedence_matches_sql() {
4785        // AND binds tighter than OR: `a OR b AND c` is `a OR (b AND c)`.
4786        // Getting this backwards silently returns the wrong rows.
4787        let s = parse("SELECT 1 FROM t WHERE a = 1 OR b = 2 AND c = 3").unwrap();
4788        match s.where_.unwrap() {
4789            Expr::Binary { op, right, .. } => {
4790                assert_eq!(op, "OR");
4791                assert!(matches!(*right, Expr::Binary { ref op, .. } if op == "AND"),
4792                        "AND must bind tighter than OR");
4793            }
4794            other => panic!("{:?}", other),
4795        }
4796        // Comparison binds tighter than AND.
4797        let s = parse("SELECT 1 FROM t WHERE a = 1 AND b = 2").unwrap();
4798        assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4799        // Multiplication binds tighter than addition.
4800        let s = parse("SELECT 1 + 2 * 3 FROM t").unwrap();
4801        match &s.items[0].expr {
4802            Expr::Binary { op, right, .. } => {
4803                assert_eq!(op, "+");
4804                assert!(matches!(**right, Expr::Binary { ref op, .. } if op == "*"));
4805            }
4806            other => panic!("{:?}", other),
4807        }
4808    }
4809
4810    #[test]
4811    fn parentheses_override_precedence() {
4812        let s = parse("SELECT 1 FROM t WHERE (a = 1 OR b = 2) AND c = 3").unwrap();
4813        match s.where_.unwrap() {
4814            Expr::Binary { op, left, .. } => {
4815                assert_eq!(op, "AND");
4816                assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "OR"));
4817            }
4818            other => panic!("{:?}", other),
4819        }
4820    }
4821
4822    #[test]
4823    fn in_and_is_null_and_between_parse_in_both_polarities() {
4824        let s = parse("SELECT 1 FROM t WHERE k IN ('r','p','')").unwrap();
4825        match s.where_.unwrap() {
4826            Expr::InList { list, negated, .. } => {
4827                assert_eq!(list.len(), 3);
4828                assert!(!negated);
4829                // The empty string in psql's `IN ('r','p','')` is a REAL value.
4830                assert_eq!(list[2], Expr::Literal(json!("")));
4831            }
4832            other => panic!("{:?}", other),
4833        }
4834        assert!(matches!(parse("SELECT 1 FROM t WHERE k NOT IN (1)").unwrap().where_.unwrap(),
4835                         Expr::InList { negated: true, .. }));
4836        assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NULL").unwrap().where_.unwrap(),
4837                         Expr::IsNull { negated: false, .. }));
4838        assert!(matches!(parse("SELECT 1 FROM t WHERE k IS NOT NULL").unwrap().where_.unwrap(),
4839                         Expr::IsNull { negated: true, .. }));
4840        // BETWEEN's bounds must not let AND escape as a boolean operator.
4841        let s = parse("SELECT 1 FROM t WHERE n BETWEEN 1 AND 5").unwrap();
4842        assert!(matches!(s.where_.unwrap(), Expr::Binary { ref op, .. } if op == "AND"));
4843    }
4844
4845    #[test]
4846    fn both_case_spellings_parse() {
4847        // simple CASE — what psql's \dt uses, with nine branches.
4848        let s = parse("SELECT CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
4849                       ELSE 'other' END FROM t").unwrap();
4850        match &s.items[0].expr {
4851            Expr::Case { operand, whens, else_ } => {
4852                assert!(operand.is_some());
4853                assert_eq!(whens.len(), 2);
4854                assert!(else_.is_some());
4855            }
4856            other => panic!("{:?}", other),
4857        }
4858        // searched CASE
4859        let s = parse("SELECT CASE WHEN k = 'r' THEN 1 END FROM t").unwrap();
4860        match &s.items[0].expr {
4861            Expr::Case { operand, whens, else_ } => {
4862                assert!(operand.is_none());
4863                assert_eq!(whens.len(), 1);
4864                assert!(else_.is_none());
4865            }
4866            other => panic!("{:?}", other),
4867        }
4868        // A CASE with no WHEN is malformed and must be refused.
4869        assert!(parse("SELECT CASE k END FROM t").is_err());
4870    }
4871
4872    #[test]
4873    fn every_join_flavour_parses_and_an_inner_join_demands_ON() {
4874        for (sql, kind) in [
4875            ("SELECT 1 FROM a JOIN b ON a.x = b.x", JoinKind::Inner),
4876            ("SELECT 1 FROM a INNER JOIN b ON a.x = b.x", JoinKind::Inner),
4877            ("SELECT 1 FROM a LEFT JOIN b ON a.x = b.x", JoinKind::Left),
4878            ("SELECT 1 FROM a LEFT OUTER JOIN b ON a.x = b.x", JoinKind::Left),
4879            ("SELECT 1 FROM a RIGHT JOIN b ON a.x = b.x", JoinKind::Right),
4880            ("SELECT 1 FROM a FULL OUTER JOIN b ON a.x = b.x", JoinKind::Full),
4881            ("SELECT 1 FROM a CROSS JOIN b", JoinKind::Cross),
4882        ] {
4883            let s = parse(sql).unwrap_or_else(|e| panic!("{}: {}", sql, e));
4884            assert_eq!(s.joins.len(), 1, "{}", sql);
4885            assert_eq!(s.joins[0].kind, kind, "{}", sql);
4886        }
4887        // A comma FROM list is an implicit cross join.
4888        let s = parse("SELECT 1 FROM a, b").unwrap();
4889        assert_eq!(s.joins[0].kind, JoinKind::Cross);
4890        // A join that needs a predicate must not silently become a cross
4891        // product — that turns two tables into n*m confidently wrong rows.
4892        assert!(parse("SELECT 1 FROM a LEFT JOIN b").is_err());
4893        assert!(parse("SELECT 1 FROM a JOIN b USING (x)").is_err());
4894    }
4895
4896    #[test]
4897    fn order_by_reads_a_number_as_an_ORDINAL() {
4898        // psql's \dt ends with `ORDER BY 1,2`. Reading those as the constants
4899        // 1 and 2 sorts every row equally and silently yields an unordered
4900        // listing that looks fine.
4901        let s = parse("SELECT a, b FROM t ORDER BY 1, 2 DESC").unwrap();
4902        assert_eq!(s.order_by.len(), 2);
4903        assert_eq!(s.order_by[0].ordinal, Some(1));
4904        assert_eq!(s.order_by[0].dir, Dir::Asc);
4905        assert_eq!(s.order_by[1].ordinal, Some(2));
4906        assert_eq!(s.order_by[1].dir, Dir::Desc);
4907        // An expression still parses as an expression.
4908        let s = parse("SELECT a FROM t ORDER BY lower(a) ASC").unwrap();
4909        assert!(s.order_by[0].ordinal.is_none());
4910        assert!(s.order_by[0].expr.is_some());
4911    }
4912
4913    #[test]
4914    fn null_ordering_defaults_the_way_postgres_defaults() {
4915        let s = parse("SELECT a FROM t ORDER BY a").unwrap();
4916        assert!(!s.order_by[0].nulls_first, "ASC defaults to NULLS LAST");
4917        let s = parse("SELECT a FROM t ORDER BY a DESC").unwrap();
4918        assert!(s.order_by[0].nulls_first, "DESC defaults to NULLS FIRST");
4919        let s = parse("SELECT a FROM t ORDER BY a NULLS FIRST").unwrap();
4920        assert!(s.order_by[0].nulls_first, "an explicit clause wins");
4921    }
4922
4923    #[test]
4924    fn limit_and_offset_parse_in_either_order() {
4925        let s = parse("SELECT a FROM t LIMIT 5 OFFSET 2").unwrap();
4926        assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4927        let s = parse("SELECT a FROM t OFFSET 2 LIMIT 5").unwrap();
4928        assert_eq!((s.limit, s.offset), (Some(5), Some(2)));
4929        let s = parse("SELECT a FROM t LIMIT ALL").unwrap();
4930        assert_eq!(s.limit, None);
4931    }
4932
4933    #[test]
4934    fn casts_parse_and_are_recorded_rather_than_rejected() {
4935        // `pr.prattrs::pg_catalog.int2[]` appears verbatim in psql's \d.
4936        let s = parse("SELECT x::int2 FROM t").unwrap();
4937        assert!(matches!(s.items[0].expr, Expr::Cast { .. }));
4938        let s = parse("SELECT x::pg_catalog.int2[] FROM t").unwrap();
4939        match &s.items[0].expr {
4940            Expr::Cast { ty, .. } => assert_eq!(ty, "int2[]"),
4941            other => panic!("{:?}", other),
4942        }
4943    }
4944
4945    #[test]
4946    fn star_and_qualified_star_parse() {
4947        assert_eq!(parse("SELECT * FROM t").unwrap().items[0].expr, Expr::Star);
4948        assert_eq!(parse("SELECT c.* FROM t c").unwrap().items[0].expr,
4949                   Expr::QualifiedStar("c".into()));
4950        // An aggregate is its OWN variant, not a Func whose name happens to
4951        // be in a list — so "is this an aggregate?" is a question about shape.
4952        match &parse("SELECT count(*) FROM t").unwrap().items[0].expr {
4953            Expr::Agg { name, args, order_by, distinct } => {
4954                assert_eq!(name, "count");
4955                assert_eq!(args, &vec![Expr::Star]);
4956                assert!(order_by.is_empty());
4957                assert!(!distinct);
4958            }
4959            other => panic!("{:?}", other),
4960        }
4961        // ...while an ordinary call stays a Func.
4962        assert!(matches!(
4963            &parse("SELECT lower(s) FROM t").unwrap().items[0].expr,
4964            Expr::Func { name, .. } if name == "lower"
4965        ));
4966    }
4967
4968    #[test]
4969    fn an_aggregate_carries_its_own_DISTINCT_and_ORDER_BY() {
4970        // `array_agg(CAST(attname AS TEXT) ORDER BY ord)` is how SQLAlchemy
4971        // reflects a primary key: the array IS the column list and its ORDER
4972        // is the answer, so the ordering cannot be dropped.
4973        match &parse("SELECT array_agg(a.attname ORDER BY a.ord) FROM t a").unwrap().items[0].expr {
4974            Expr::Agg { name, args, order_by, distinct } => {
4975                assert_eq!(name, "array_agg");
4976                assert_eq!(args.len(), 1);
4977                assert_eq!(order_by.len(), 1);
4978                assert!(matches!(order_by[0].dir, Dir::Asc));
4979                assert!(!distinct);
4980            }
4981            other => panic!("{:?}", other),
4982        }
4983        // Direction, NULLS placement and multiple keys all parse the same way
4984        // the query's own ORDER BY does — one shared sort-list parser.
4985        match &parse("SELECT string_agg(DISTINCT s, ',' ORDER BY b DESC NULLS LAST, c) FROM t").unwrap().items[0].expr {
4986            Expr::Agg { name, args, order_by, distinct } => {
4987                assert_eq!(name, "string_agg");
4988                assert_eq!(args.len(), 2, "the separator is an argument, not a sort key");
4989                assert_eq!(order_by.len(), 2);
4990                assert!(matches!(order_by[0].dir, Dir::Desc));
4991                assert!(!order_by[0].nulls_first, "NULLS LAST overrides the DESC default");
4992                assert!(matches!(order_by[1].dir, Dir::Asc));
4993                assert!(distinct);
4994            }
4995            other => panic!("{:?}", other),
4996        }
4997        // DISTINCT is an aggregate-only modifier; on a plain call it is not
4998        // consumed, so the call fails to parse rather than silently dropping it.
4999        assert!(parse("SELECT lower(DISTINCT s) FROM t").is_err());
5000    }
5001
5002    #[test]
5003    fn a_parenthesis_free_function_parses_as_a_zero_arg_call() {
5004        // `current_schema` is legal without parentheses.
5005        match &parse("SELECT current_schema FROM t").unwrap().items[0].expr {
5006            Expr::Func { name, args } => {
5007                assert_eq!(name, "current_schema");
5008                assert!(args.is_empty());
5009            }
5010            other => panic!("{:?}", other),
5011        }
5012    }
5013
5014    #[test]
5015    fn GROUP_BY_and_HAVING_parse_and_what_remains_is_refused_by_name() {
5016        // GROUP BY and HAVING used to be refused here. SQLAlchemy's column
5017        // and index reflection are built on both, so they now parse.
5018        let s = parse("SELECT a, count(*) FROM t GROUP BY a").unwrap();
5019        assert_eq!(s.group_by, vec![Expr::Column { qual: None, name: "a".into() }]);
5020        assert!(s.having.is_none());
5021
5022        let s = parse("SELECT a, b, count(*) FROM t GROUP BY a, b HAVING count(*) > 1").unwrap();
5023        assert_eq!(s.group_by.len(), 2);
5024        assert!(s.having.is_some());
5025
5026        // HAVING is a filter over GROUPS. With neither a GROUP BY nor an
5027        // aggregate there are no groups to filter, and silently treating it
5028        // as a WHERE would answer a different question than the one asked.
5029        let e = parse("SELECT a FROM t HAVING a > 1").unwrap_err().to_string();
5030        assert!(e.contains("HAVING needs a GROUP BY"), "{}", e);
5031
5032        for (sql, needle) in [
5033            ("SELECT DISTINCT ON (a) a FROM t", "DISTINCT ON"),
5034            ("SELECT a, count(*) FROM t GROUP BY ROLLUP (a)", "ROLLUP"),
5035            ("SELECT a, count(*) FROM t GROUP BY CUBE (a)", "CUBE"),
5036        ] {
5037            let e = parse(sql).unwrap_err().to_string();
5038            assert!(e.contains(needle), "{} -> {}", sql, e);
5039        }
5040        // Trailing garbage is an error, not something to ignore.
5041        assert!(parse("SELECT a FROM t JUNK JUNK2").is_err());
5042    }
5043
5044    #[test]
5045    fn THE_dn_QUERY_parses_completely() {
5046        let s = parse(
5047            r#"SELECT n.nspname AS "Name",
5048                 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5049               FROM pg_catalog.pg_namespace n
5050               WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5051               ORDER BY 1;"#,
5052        )
5053        .expect("psql's \\dn must parse");
5054
5055        assert_eq!(s.items.len(), 2);
5056        assert_eq!(s.items[0].alias, Some("Name".into()));
5057        assert_eq!(s.items[1].alias, Some("Owner".into()));
5058        let from = s.from.unwrap();
5059        assert_eq!(from.name, "pg_catalog.pg_namespace");
5060        assert_eq!(from.binding(), "n");
5061        assert!(s.where_.is_some());
5062        assert_eq!(s.order_by[0].ordinal, Some(1));
5063    }
5064
5065    #[test]
5066    fn THE_dt_QUERY_parses_completely() {
5067        let s = parse(
5068            r#"SELECT n.nspname as "Schema",
5069                 c.relname as "Name",
5070                 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5071                   WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5072                   WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5073                   WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5074                   WHEN 'I' THEN 'partitioned index' END as "Type",
5075                 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5076               FROM pg_catalog.pg_class c
5077                    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5078                    LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5079               WHERE c.relkind IN ('r','p','')
5080                     AND n.nspname <> 'pg_catalog'
5081                     AND n.nspname !~ '^pg_toast'
5082                     AND n.nspname <> 'information_schema'
5083                 AND pg_catalog.pg_table_is_visible(c.oid)
5084               ORDER BY 1,2;"#,
5085        )
5086        .expect("psql's \\dt must parse");
5087
5088        assert_eq!(s.items.len(), 4);
5089        assert_eq!(s.items[2].alias, Some("Type".into()));
5090        match &s.items[2].expr {
5091            Expr::Case { whens, .. } => assert_eq!(whens.len(), 9, "all nine branches"),
5092            other => panic!("{:?}", other),
5093        }
5094        assert_eq!(s.joins.len(), 2);
5095        assert!(s.joins.iter().all(|j| j.kind == JoinKind::Left && j.on.is_some()));
5096        assert_eq!(s.from.unwrap().binding(), "c");
5097        assert_eq!(s.order_by.len(), 2);
5098        assert_eq!(
5099            (s.order_by[0].ordinal, s.order_by[1].ordinal),
5100            (Some(1), Some(2))
5101        );
5102    }
5103}
5104
5105#[cfg(test)]
5106mod eval_tests {
5107    use super::*;
5108    use serde_json::json;
5109
5110    /// One binding named `t` holding `row`.
5111    fn one(row: &Value) -> Bound<'_> {
5112        Bound::new(vec![("t".to_string(), Some(row))])
5113    }
5114
5115    fn ev(sql_expr: &str, row: &Value) -> Result<Value> {
5116        let s = parse(&format!("SELECT {} FROM t", sql_expr))?;
5117        eval(&s.items[0].expr, &one(row))
5118    }
5119
5120    fn v(sql_expr: &str, row: &Value) -> Value {
5121        ev(sql_expr, row).unwrap_or_else(|e| panic!("{}: {}", sql_expr, e))
5122    }
5123
5124    #[test]
5125    fn literals_and_columns_resolve() {
5126        let r = json!({"a": 1, "s": "x", "b": true, "n": null});
5127        assert_eq!(v("42", &r), json!(42));
5128        assert_eq!(v("'hi'", &r), json!("hi"));
5129        assert_eq!(v("NULL", &r), Value::Null);
5130        assert_eq!(v("TRUE", &r), json!(true));
5131        assert_eq!(v("a", &r), json!(1));
5132        assert_eq!(v("t.a", &r), json!(1));
5133        assert_eq!(v("s", &r), json!("x"));
5134        // An absent column is NULL, because a schemaless document may omit
5135        // any field — that is data, not an error.
5136        assert_eq!(v("nosuch", &r), Value::Null);
5137    }
5138
5139    #[test]
5140    fn an_unknown_table_ALIAS_is_an_error_while_an_unknown_column_is_null() {
5141        // The distinction matters: a typo'd alias is a query bug worth
5142        // reporting, while a missing field is ordinary schemaless behaviour.
5143        let r = json!({"a": 1});
5144        assert_eq!(v("t.nosuch", &r), Value::Null);
5145        let e = ev("zz.a", &r).unwrap_err().to_string();
5146        assert!(e.contains("zz"), "{}", e);
5147    }
5148
5149    // ── SQL's three-valued logic. The subtle, dangerous part. ───────────────
5150
5151    #[test]
5152    fn every_comparison_over_NULL_is_UNKNOWN_including_null_equals_null() {
5153        let r = json!({"n": null, "a": 1});
5154        assert_eq!(v("n = 1", &r), Value::Null);
5155        assert_eq!(v("n != 1", &r), Value::Null);
5156        assert_eq!(v("n < 1", &r), Value::Null);
5157        // The one everybody gets wrong: NULL = NULL is UNKNOWN, not true.
5158        assert_eq!(v("n = n", &r), Value::Null);
5159        assert_eq!(v("n = NULL", &r), Value::Null);
5160    }
5161
5162    #[test]
5163    fn NOT_UNKNOWN_is_UNKNOWN_not_true() {
5164        // Collapsing UNKNOWN to false here would make
5165        // `WHERE NOT (n.nspname = 'x')` include the unmatched rows of a LEFT
5166        // JOIN that Postgres excludes — the counts would silently disagree.
5167        let r = json!({"n": null});
5168        assert_eq!(v("NOT (n = 1)", &r), Value::Null);
5169        assert_eq!(v("NOT TRUE", &r), json!(false));
5170        assert_eq!(v("NOT FALSE", &r), json!(true));
5171    }
5172
5173    #[test]
5174    fn AND_and_OR_follow_the_three_valued_truth_tables() {
5175        let r = json!({"n": null});
5176        // false AND unknown = FALSE (the false decides it)
5177        assert_eq!(v("FALSE AND n = 1", &r), json!(false));
5178        // true AND unknown = unknown
5179        assert_eq!(v("TRUE AND n = 1", &r), Value::Null);
5180        // true OR unknown = TRUE (the true decides it)
5181        assert_eq!(v("TRUE OR n = 1", &r), json!(true));
5182        // false OR unknown = unknown
5183        assert_eq!(v("FALSE OR n = 1", &r), Value::Null);
5184        // and the ordinary cases
5185        assert_eq!(v("TRUE AND TRUE", &r), json!(true));
5186        assert_eq!(v("TRUE AND FALSE", &r), json!(false));
5187        assert_eq!(v("FALSE OR FALSE", &r), json!(false));
5188    }
5189
5190    #[test]
5191    fn IS_NULL_is_the_one_predicate_that_is_never_unknown() {
5192        let r = json!({"n": null, "a": 1});
5193        assert_eq!(v("n IS NULL", &r), json!(true));
5194        assert_eq!(v("n IS NOT NULL", &r), json!(false));
5195        assert_eq!(v("a IS NULL", &r), json!(false));
5196        assert_eq!(v("a IS NOT NULL", &r), json!(true));
5197        // An absent column is indistinguishable from an explicit null, which
5198        // is the honest answer for a schemaless store.
5199        assert_eq!(v("nosuch IS NULL", &r), json!(true));
5200    }
5201
5202    #[test]
5203    fn NOT_IN_with_a_NULL_in_the_list_is_UNKNOWN_the_classic_trap() {
5204        let r = json!({"a": 2});
5205        assert_eq!(v("a IN (1, 2)", &r), json!(true));
5206        assert_eq!(v("a IN (1, 3)", &r), json!(false));
5207        assert_eq!(v("a NOT IN (1, 3)", &r), json!(true));
5208        // `2 NOT IN (1, NULL)` is UNKNOWN, not true — 2 MIGHT equal the null.
5209        // Postgres agrees, and getting this wrong silently includes rows.
5210        assert_eq!(v("a NOT IN (1, NULL)", &r), Value::Null);
5211        // A match still decides it even with a null present.
5212        assert_eq!(v("a IN (2, NULL)", &r), json!(true));
5213        // NULL on the left is unknown regardless.
5214        assert_eq!(v("nosuch IN (1)", &r), Value::Null);
5215    }
5216
5217    // ── operators ───────────────────────────────────────────────────────────
5218
5219    #[test]
5220    fn comparisons_work_across_numbers_strings_and_booleans() {
5221        let r = json!({"n": 5, "s": "b", "t": true});
5222        assert_eq!(v("n > 3", &r), json!(true));
5223        assert_eq!(v("n <= 5", &r), json!(true));
5224        assert_eq!(v("s < 'c'", &r), json!(true));
5225        assert_eq!(v("s > 'c'", &r), json!(false));
5226        // A number and a numeric-looking string compare NUMERICALLY, because
5227        // a catalogue oid is a number while a client may quote it.
5228        assert_eq!(v("n = '5'", &r), json!(true));
5229        assert_eq!(v("n = '5.0'", &r), json!(true));
5230        // And a non-numeric string falls back to text comparison rather than
5231        // erroring.
5232        assert_eq!(v("n = 'five'", &r), json!(false));
5233    }
5234
5235    #[test]
5236    fn the_regex_operators_use_the_SAME_matcher_as_NQL() {
5237        // Two implementations would be two chances for the SQL surface and
5238        // the NQL surface to disagree about the same operator.
5239        let r = json!({"s": "pg_catalog"});
5240        assert_eq!(v("s ~ '^pg_'", &r), json!(true));
5241        assert_eq!(v("s !~ '^pg_'", &r), json!(false));
5242        assert_eq!(v("s ~ '^PG_'", &r), json!(false));
5243        assert_eq!(v("s ~* '^PG_'", &r), json!(true));
5244        assert_eq!(v("s !~ '^zz'", &r), json!(true));
5245        // NULL propagates.
5246        assert_eq!(v("nosuch ~ '^x'", &r), Value::Null);
5247        // The ERE subset: groups, alternation, quantifiers — what `\d orders`
5248        // sends (`^(orders)$`) and `\d pg_*` would (`^(pg_.*)$`).
5249        assert_eq!(v("s ~ '^(pg_catalog)$'", &r), json!(true));
5250        assert_eq!(v("s ~ '^(pg_.*)$'", &r), json!(true));
5251        assert_eq!(v("s ~ '^(public|pg_catalog)$'", &r), json!(true));
5252        assert_eq!(v("s ~ '^pg_[a-z]+$'", &r), json!(true));
5253        assert_eq!(v("s ~ '^pg_[0-9]+$'", &r), json!(false));
5254        // And an unsupported construct is refused BY NAME, not approximated.
5255        let e = ev("s ~ 'a{2}'", &r).unwrap_err().to_string();
5256        assert!(e.contains("interval"), "{}", e);
5257    }
5258
5259    #[test]
5260    fn like_works_in_all_four_spellings() {
5261        let r = json!({"s": "Acme Pool"});
5262        assert_eq!(v("s LIKE 'Acme%'", &r), json!(true));
5263        assert_eq!(v("s LIKE 'acme%'", &r), json!(false));
5264        assert_eq!(v("s ILIKE 'acme%'", &r), json!(true));
5265        assert_eq!(v("s NOT LIKE 'zz%'", &r), json!(true));
5266        assert_eq!(v("nosuch LIKE 'x'", &r), Value::Null);
5267    }
5268
5269    #[test]
5270    fn arithmetic_and_concatenation_propagate_null_and_refuse_div_by_zero() {
5271        let r = json!({"a": 7, "b": 2});
5272        assert_eq!(v("a + b", &r), json!(9));
5273        assert_eq!(v("a - b", &r), json!(5));
5274        assert_eq!(v("a * b", &r), json!(14));
5275        assert_eq!(v("a / b", &r), json!(3.5));
5276        assert_eq!(v("a % b", &r), json!(1));
5277        assert_eq!(v("-a", &r), json!(-7));
5278        // A non-integral result stays a float — the rule is about how INTEGRAL
5279        // values render, not about collapsing every number to an integer.
5280        assert_eq!(v("a / b", &r), json!(3.5));
5281        assert_eq!(v("b / a", &r), json!(2.0 / 7.0));
5282        assert_eq!(v("'x' || 'y'", &r), json!("xy"));
5283        assert_eq!(v("'x' || nosuch", &r), Value::Null);
5284        assert_eq!(v("a + nosuch", &r), Value::Null);
5285        // Division by zero is an ERROR in Postgres, not infinity. Returning
5286        // inf would be a confidently wrong number.
5287        assert!(ev("a / 0", &r).is_err());
5288        assert!(ev("a % 0", &r).is_err());
5289    }
5290
5291    #[test]
5292    fn a_cast_is_transparent_rather_than_rejected() {
5293        // `pr.prattrs::pg_catalog.int2[]` appears in real catalogue SQL, and
5294        // the cast cannot change the answer for the shapes it is used on.
5295        let r = json!({"a": 7});
5296        assert_eq!(v("a::int2", &r), json!(7));
5297        assert_eq!(v("a::pg_catalog.int2[]", &r), json!(7));
5298    }
5299
5300    // ── CASE ────────────────────────────────────────────────────────────────
5301
5302    #[test]
5303    fn a_simple_CASE_picks_the_matching_branch() {
5304        // This is psql's \dt shape, with the real relkind values.
5305        let expr = "CASE k WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' \
5306                    WHEN 'i' THEN 'index' END";
5307        assert_eq!(v(expr, &json!({"k": "r"})), json!("table"));
5308        assert_eq!(v(expr, &json!({"k": "v"})), json!("view"));
5309        assert_eq!(v(expr, &json!({"k": "i"})), json!("index"));
5310        // No branch and no ELSE is NULL — which is exactly what \dt relies on
5311        // for a relkind it does not name.
5312        assert_eq!(v(expr, &json!({"k": "z"})), Value::Null);
5313    }
5314
5315    #[test]
5316    fn a_searched_CASE_evaluates_predicates_and_UNKNOWN_does_not_match() {
5317        let expr = "CASE WHEN n > 5 THEN 'big' WHEN n > 0 THEN 'small' ELSE 'none' END";
5318        assert_eq!(v(expr, &json!({"n": 9})), json!("big"));
5319        assert_eq!(v(expr, &json!({"n": 2})), json!("small"));
5320        assert_eq!(v(expr, &json!({"n": -1})), json!("none"));
5321        // An UNKNOWN condition must not match — it falls through to ELSE.
5322        assert_eq!(v(expr, &json!({"other": 1})), json!("none"));
5323    }
5324
5325    #[test]
5326    fn an_ELSE_branch_is_used_when_nothing_matches() {
5327        assert_eq!(
5328            v("CASE k WHEN 'r' THEN 'table' ELSE 'other' END", &json!({"k": "z"})),
5329            json!("other")
5330        );
5331    }
5332
5333    // ── functions ───────────────────────────────────────────────────────────
5334
5335    #[test]
5336    fn the_catalogue_functions_psql_calls_all_answer() {
5337        let r = json!({"o": 10, "enc": 6});
5338        // \dn and \dt both call this for the "Owner" column.
5339        assert_eq!(v("pg_get_userbyid(o)", &r), json!("nedb"));
5340        assert_eq!(v("pg_catalog.pg_get_userbyid(o)", &r), json!("nedb"));
5341        // \dt filters on this. Returning false would hide EVERY table.
5342        assert_eq!(v("pg_table_is_visible(o)", &r), json!(true));
5343        assert_eq!(v("pg_encoding_to_char(enc)", &r), json!("UTF8"));
5344        assert_eq!(v("current_schema", &r), json!("public"));
5345        assert_eq!(v("current_database()", &r), json!("nedb"));
5346        assert_eq!(v("current_user", &r), json!("nedb"));
5347        // The definition-printing functions return NULL rather than invented
5348        // DDL — NEDB has no DDL to print.
5349        assert_eq!(v("pg_get_expr(o, o)", &r), Value::Null);
5350        assert_eq!(v("obj_description(o)", &r), Value::Null);
5351    }
5352
5353    #[test]
5354    fn text_and_null_handling_functions_work() {
5355        let r = json!({"s": "AbC", "n": null});
5356        assert_eq!(v("lower(s)", &r), json!("abc"));
5357        assert_eq!(v("upper(s)", &r), json!("ABC"));
5358        assert_eq!(v("length(s)", &r), json!(3));
5359        assert_eq!(v("lower(n)", &r), Value::Null);
5360        assert_eq!(v("coalesce(n, 'fallback')", &r), json!("fallback"));
5361        assert_eq!(v("coalesce(s, 'fallback')", &r), json!("AbC"));
5362        assert_eq!(v("coalesce(n, n)", &r), Value::Null);
5363        assert_eq!(v("nullif(s, 'AbC')", &r), Value::Null);
5364        assert_eq!(v("nullif(s, 'zz')", &r), json!("AbC"));
5365        // format_type names the type the same way information_schema does.
5366        assert_eq!(v("format_type(20, NULL)", &r), json!("bigint"));
5367    }
5368
5369    #[test]
5370    fn coalesce_does_not_evaluate_past_its_first_non_null() {
5371        // `a / 0` would error; coalesce must never reach it.
5372        let r = json!({"a": 1});
5373        assert_eq!(v("coalesce(a, a / 0)", &r), json!(1));
5374    }
5375
5376    #[test]
5377    fn an_unknown_function_is_REFUSED_rather_than_answered_with_NULL() {
5378        // A NULL column reads as missing DATA rather than a missing feature,
5379        // and somebody would file a data-loss bug against it.
5380        let e = ev("pg_stat_get_numscans(1)", &json!({})).unwrap_err().to_string();
5381        assert!(e.contains("pg_stat_get_numscans"), "{}", e);
5382        assert!(e.contains("refused"), "{}", e);
5383    }
5384
5385    // ── join bindings ───────────────────────────────────────────────────────
5386
5387    #[test]
5388    fn a_qualified_column_reads_only_its_OWN_binding() {
5389        // Both rows have `name`. Without qualifier isolation a join would
5390        // silently read the wrong table's column.
5391        let a = json!({"name": "left", "x": 1});
5392        let b = json!({"name": "right", "y": 2});
5393        let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), Some(&b))]);
5394        let get = |e: &str| {
5395            let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5396            eval(&s.items[0].expr, &row).unwrap()
5397        };
5398        assert_eq!(get("a.name"), json!("left"));
5399        assert_eq!(get("b.name"), json!("right"));
5400        // A bare name takes the first binding that HAS the key.
5401        assert_eq!(get("name"), json!("left"));
5402        assert_eq!(get("y"), json!(2), "a bare name still finds a later binding");
5403    }
5404
5405    #[test]
5406    fn an_unmatched_LEFT_JOIN_side_reads_as_NULL_not_as_a_missing_column() {
5407        // The distinction is what makes `n.nspname IS NULL` answer correctly
5408        // for a row that found no match.
5409        let a = json!({"x": 1});
5410        let row = Bound::new(vec![("a".into(), Some(&a)), ("b".into(), None)]);
5411        let get = |e: &str| {
5412            let s = parse(&format!("SELECT {} FROM x", e)).unwrap();
5413            eval(&s.items[0].expr, &row).unwrap()
5414        };
5415        assert_eq!(get("b.anything"), Value::Null);
5416        assert_eq!(get("b.anything IS NULL"), json!(true));
5417        assert_eq!(get("a.x"), json!(1));
5418    }
5419}
5420
5421#[cfg(test)]
5422mod exec_tests {
5423    use super::*;
5424    use serde_json::json;
5425
5426    /// A resolver over a fixed set of named tables.
5427    fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5428        let owned: Vec<(String, Vec<Value>)> =
5429            defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5430        move |name: &str| {
5431            // Match on the bare name so `pg_catalog.pg_class` finds `pg_class`.
5432            let bare = name.rsplit('.').next().unwrap_or(name);
5433            Ok(owned
5434                .iter()
5435                .find(|(n, _)| n == name || n == bare)
5436                .map(|(_, r)| from_vec(r.clone())))
5437        }
5438    }
5439
5440    fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5441        let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n  -> {}", sql, e));
5442        (cols.into_iter().map(|c| c.name).collect(), rows)
5443    }
5444
5445    fn col(rows: &[Value], name: &str) -> Vec<Value> {
5446        rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5447    }
5448
5449    // ── the basics, over one table ───────────────────────────────────────────
5450
5451    #[test]
5452    fn select_columns_where_order_limit_offset() {
5453        let t = tables(vec![(
5454            "t",
5455            vec![json!({"a": 3, "s": "c"}), json!({"a": 1, "s": "a"}), json!({"a": 2, "s": "b"})],
5456        )]);
5457        let (names, rows) = go("SELECT a, s FROM t ORDER BY a", &t);
5458        assert_eq!(names, vec!["a", "s"]);
5459        assert_eq!(col(&rows, "a"), vec![json!(1), json!(2), json!(3)]);
5460
5461        let (_, rows) = go("SELECT a FROM t ORDER BY a DESC", &t);
5462        assert_eq!(col(&rows, "a"), vec![json!(3), json!(2), json!(1)]);
5463
5464        let (_, rows) = go("SELECT a FROM t WHERE a > 1 ORDER BY a", &t);
5465        assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5466
5467        let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 2", &t);
5468        assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5469
5470        let (_, rows) = go("SELECT a FROM t ORDER BY a OFFSET 1", &t);
5471        assert_eq!(col(&rows, "a"), vec![json!(2), json!(3)]);
5472
5473        let (_, rows) = go("SELECT a FROM t ORDER BY a LIMIT 1 OFFSET 1", &t);
5474        assert_eq!(col(&rows, "a"), vec![json!(2)]);
5475
5476        // Past the end is an empty page, not an error.
5477        let (_, rows) = go("SELECT a FROM t OFFSET 99", &t);
5478        assert!(rows.is_empty());
5479    }
5480
5481    #[test]
5482    fn an_output_column_takes_its_alias_or_a_derived_name() {
5483        // Clients index result columns BY NAME, so inventing a different name
5484        // breaks code that works against Postgres.
5485        let t = tables(vec![("t", vec![json!({"a": 1})])]);
5486        assert_eq!(go(r#"SELECT a AS "Name" FROM t"#, &t).0, vec!["Name"]);
5487        assert_eq!(go("SELECT a FROM t", &t).0, vec!["a"]);
5488        assert_eq!(go("SELECT lower('X') FROM t", &t).0, vec!["lower"]);
5489        assert_eq!(go("SELECT 1 + 1 FROM t", &t).0, vec!["?column?"]);
5490        assert_eq!(go("SELECT CASE a WHEN 1 THEN 'x' END FROM t", &t).0, vec!["case"]);
5491    }
5492
5493    #[test]
5494    fn star_expands_from_the_rows_and_a_qualified_star_from_one_binding() {
5495        let t = tables(vec![
5496            ("a", vec![json!({"x": 1, "y": 2})]),
5497            ("b", vec![json!({"z": 3})]),
5498        ]);
5499        let (names, rows) = go("SELECT * FROM a", &t);
5500        assert_eq!(names, vec!["x", "y"]);
5501        assert_eq!(rows.len(), 1);
5502
5503        let (names, _) = go("SELECT a.* FROM a CROSS JOIN b", &t);
5504        assert_eq!(names, vec!["x", "y"], "a qualified star takes ONE binding");
5505
5506        // With no rows a `*` yields no columns, which is the honest answer for
5507        // a schemaless source: only a row knows what columns exist.
5508        let empty = tables(vec![("e", vec![])]);
5509        assert_eq!(go("SELECT * FROM e", &empty).0, Vec::<String>::new());
5510    }
5511
5512    #[test]
5513    fn distinct_dedupes_on_the_projected_values() {
5514        let t = tables(vec![(
5515            "t",
5516            vec![json!({"g": "x"}), json!({"g": "x"}), json!({"g": "y"})],
5517        )]);
5518        let (_, rows) = go("SELECT DISTINCT g FROM t ORDER BY 1", &t);
5519        assert_eq!(col(&rows, "g"), vec![json!("x"), json!("y")]);
5520        let (_, rows) = go("SELECT g FROM t", &t);
5521        assert_eq!(rows.len(), 3, "without DISTINCT every row survives");
5522    }
5523
5524    #[test]
5525    fn order_by_an_ORDINAL_sorts_the_projected_column() {
5526        let t = tables(vec![(
5527            "t",
5528            vec![json!({"a": 2, "b": "z"}), json!({"a": 1, "b": "y"})],
5529        )]);
5530        let (_, rows) = go("SELECT a, b FROM t ORDER BY 1", &t);
5531        assert_eq!(col(&rows, "a"), vec![json!(1), json!(2)]);
5532        let (_, rows) = go("SELECT a, b FROM t ORDER BY 2 DESC", &t);
5533        assert_eq!(col(&rows, "b"), vec![json!("z"), json!("y")]);
5534        // Out of range is an error naming the range, not a silent no-sort.
5535        let e = run("SELECT a FROM t ORDER BY 3", &t).unwrap_err().to_string();
5536        assert!(e.contains("out of range"), "{}", e);
5537    }
5538
5539    #[test]
5540    fn order_by_an_expression_may_use_a_column_NOT_in_the_select_list() {
5541        let t = tables(vec![(
5542            "t",
5543            vec![json!({"a": 1, "hidden": 9}), json!({"a": 2, "hidden": 1})],
5544        )]);
5545        let (_, rows) = go("SELECT a FROM t ORDER BY hidden", &t);
5546        assert_eq!(col(&rows, "a"), vec![json!(2), json!(1)]);
5547    }
5548
5549    #[test]
5550    fn null_ordering_follows_the_direction_defaults() {
5551        let t = tables(vec![(
5552            "t",
5553            vec![json!({"a": 2}), json!({"a": null}), json!({"a": 1})],
5554        )]);
5555        // ASC defaults to NULLS LAST.
5556        assert_eq!(col(&go("SELECT a FROM t ORDER BY a", &t).1, "a"),
5557                   vec![json!(1), json!(2), Value::Null]);
5558        // DESC defaults to NULLS FIRST.
5559        assert_eq!(col(&go("SELECT a FROM t ORDER BY a DESC", &t).1, "a"),
5560                   vec![Value::Null, json!(2), json!(1)]);
5561        // An explicit clause overrides the default.
5562        assert_eq!(col(&go("SELECT a FROM t ORDER BY a NULLS FIRST", &t).1, "a"),
5563                   vec![Value::Null, json!(1), json!(2)]);
5564    }
5565
5566    #[test]
5567    fn a_where_clause_that_is_UNKNOWN_excludes_the_row() {
5568        let t = tables(vec![(
5569            "t",
5570            vec![json!({"a": 1}), json!({"a": null}), json!({"other": 1})],
5571        )]);
5572        // Only the row where the comparison is TRUE survives; UNKNOWN drops.
5573        let (_, rows) = go("SELECT a FROM t WHERE a = 1", &t);
5574        assert_eq!(rows.len(), 1);
5575        // And NOT over UNKNOWN is still UNKNOWN, so it drops too.
5576        let (_, rows) = go("SELECT a FROM t WHERE NOT (a = 1)", &t);
5577        assert_eq!(rows.len(), 0, "NOT UNKNOWN must not resurrect a null row");
5578    }
5579
5580    #[test]
5581    fn select_with_no_FROM_returns_exactly_one_row() {
5582        // A client's liveness probe is written this way.
5583        let t = tables(vec![]);
5584        let (names, rows) = go("SELECT 1", &t);
5585        assert_eq!(rows.len(), 1);
5586        assert_eq!(names, vec!["?column?"]);
5587        assert_eq!(go("SELECT current_schema", &t).1.len(), 1);
5588    }
5589
5590    #[test]
5591    fn an_unknown_relation_is_NAMED_rather_than_answered_with_no_rows() {
5592        // An unknown table that returned zero rows would look exactly like an
5593        // empty one, which is how "where did my data go" starts.
5594        let t = tables(vec![("t", vec![])]);
5595        let e = run("SELECT a FROM nosuchtable", &t).unwrap_err().to_string();
5596        assert!(e.contains("nosuchtable"), "{}", e);
5597        assert!(e.contains("does not exist"), "{}", e);
5598    }
5599
5600    // ── joins ────────────────────────────────────────────────────────────────
5601
5602    #[test]
5603    fn an_inner_join_keeps_only_matching_pairs() {
5604        let t = tables(vec![
5605            ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5606            ("r", vec![json!({"lid": 1, "v": "x"})]),
5607        ]);
5608        let (_, rows) = go("SELECT l.n, r.v FROM l JOIN r ON r.lid = l.id", &t);
5609        assert_eq!(rows.len(), 1);
5610        assert_eq!(col(&rows, "n"), vec![json!("a")]);
5611    }
5612
5613    #[test]
5614    fn a_LEFT_join_keeps_unmatched_left_rows_with_NULLs() {
5615        // This is the shape psql's \dt uses twice.
5616        let t = tables(vec![
5617            ("l", vec![json!({"id": 1, "n": "a"}), json!({"id": 2, "n": "b"})]),
5618            ("r", vec![json!({"lid": 1, "v": "x"})]),
5619        ]);
5620        let (_, rows) = go("SELECT l.n, r.v FROM l LEFT JOIN r ON r.lid = l.id ORDER BY 1", &t);
5621        assert_eq!(rows.len(), 2);
5622        assert_eq!(col(&rows, "n"), vec![json!("a"), json!("b")]);
5623        assert_eq!(col(&rows, "v"), vec![json!("x"), Value::Null]);
5624    }
5625
5626    #[test]
5627    fn a_RIGHT_join_keeps_unmatched_right_rows_and_FULL_keeps_both() {
5628        let t = tables(vec![
5629            ("l", vec![json!({"id": 1})]),
5630            ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5631        ]);
5632        let (_, rows) = go("SELECT l.id, r.lid FROM l RIGHT JOIN r ON r.lid = l.id", &t);
5633        assert_eq!(rows.len(), 2);
5634        assert!(col(&rows, "id").contains(&Value::Null), "the unmatched right row keeps NULLs on the left");
5635
5636        let t2 = tables(vec![
5637            ("l", vec![json!({"id": 1}), json!({"id": 5})]),
5638            ("r", vec![json!({"lid": 1}), json!({"lid": 9})]),
5639        ]);
5640        let (_, rows) = go("SELECT l.id, r.lid FROM l FULL OUTER JOIN r ON r.lid = l.id", &t2);
5641        assert_eq!(rows.len(), 3, "one match plus one orphan on each side");
5642    }
5643
5644    #[test]
5645    fn a_cross_join_is_the_cartesian_product() {
5646        let t = tables(vec![
5647            ("a", vec![json!({"x": 1}), json!({"x": 2})]),
5648            ("b", vec![json!({"y": 1}), json!({"y": 2}), json!({"y": 3})]),
5649        ]);
5650        assert_eq!(go("SELECT a.x, b.y FROM a CROSS JOIN b", &t).1.len(), 6);
5651        // A comma FROM list means the same thing.
5652        assert_eq!(go("SELECT a.x, b.y FROM a, b", &t).1.len(), 6);
5653    }
5654
5655    #[test]
5656    fn an_ON_clause_that_is_UNKNOWN_does_not_join() {
5657        // Treating UNKNOWN as a match would invent pairings out of missing
5658        // data — rows that exist in neither table.
5659        let t = tables(vec![
5660            ("l", vec![json!({"id": null})]),
5661            ("r", vec![json!({"lid": null})]),
5662        ]);
5663        let (_, rows) = go("SELECT l.id FROM l JOIN r ON r.lid = l.id", &t);
5664        assert!(rows.is_empty(), "NULL = NULL is UNKNOWN, so nothing joins");
5665        // …and on a LEFT JOIN the left row survives with NULLs.
5666        let (_, rows) = go("SELECT l.id FROM l LEFT JOIN r ON r.lid = l.id", &t);
5667        assert_eq!(rows.len(), 1);
5668    }
5669
5670    #[test]
5671    fn two_joins_chain() {
5672        let t = tables(vec![
5673            ("a", vec![json!({"id": 1, "bid": 10, "cid": 100})]),
5674            ("b", vec![json!({"id": 10, "bn": "B"})]),
5675            ("c", vec![json!({"id": 100, "cn": "C"})]),
5676        ]);
5677        let (_, rows) = go(
5678            "SELECT a.id, b.bn, c.cn FROM a \
5679             LEFT JOIN b ON b.id = a.bid \
5680             LEFT JOIN c ON c.id = a.cid",
5681            &t,
5682        );
5683        assert_eq!(rows.len(), 1);
5684        assert_eq!(col(&rows, "bn"), vec![json!("B")]);
5685        assert_eq!(col(&rows, "cn"), vec![json!("C")]);
5686    }
5687
5688    // ── THE acceptance tests ─────────────────────────────────────────────────
5689
5690    /// The catalogue rows psql's `\dn` and `\dt` actually read.
5691    fn catalog() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5692        tables(vec![
5693            (
5694                "pg_namespace",
5695                vec![
5696                    json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5697                    json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5698                    json!({"oid": 13000, "nspname": "information_schema", "nspowner": 10}),
5699                ],
5700            ),
5701            (
5702                "pg_class",
5703                vec![
5704                    json!({"oid": 16401, "relname": "orders", "relnamespace": 2200,
5705                           "relkind": "r", "relowner": 10, "relam": 2}),
5706                    json!({"oid": 16402, "relname": "drivers", "relnamespace": 2200,
5707                           "relkind": "r", "relowner": 10, "relam": 2}),
5708                ],
5709            ),
5710            ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5711        ])
5712    }
5713
5714    #[test]
5715    fn THE_dn_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5716        let (names, rows) = go(
5717            r#"SELECT n.nspname AS "Name",
5718                 pg_catalog.pg_get_userbyid(n.nspowner) AS "Owner"
5719               FROM pg_catalog.pg_namespace n
5720               WHERE n.nspname !~ '^pg_' AND n.nspname <> 'information_schema'
5721               ORDER BY 1;"#,
5722            &catalog(),
5723        );
5724
5725        assert_eq!(names, vec!["Name", "Owner"], "psql reads these BY NAME");
5726        // `pg_catalog` is excluded by the regex, `information_schema` by the
5727        // `<>` — leaving exactly the one schema a user cares about.
5728        assert_eq!(col(&rows, "Name"), vec![json!("public")]);
5729        assert_eq!(col(&rows, "Owner"), vec![json!("nedb")]);
5730    }
5731
5732    #[test]
5733    fn THE_dt_QUERY_RUNS_AND_RETURNS_THE_RIGHT_ROWS() {
5734        let (names, rows) = go(
5735            r#"SELECT n.nspname as "Schema",
5736                 c.relname as "Name",
5737                 CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view'
5738                   WHEN 'm' THEN 'materialized view' WHEN 'i' THEN 'index'
5739                   WHEN 'S' THEN 'sequence' WHEN 't' THEN 'TOAST table'
5740                   WHEN 'f' THEN 'foreign table' WHEN 'p' THEN 'partitioned table'
5741                   WHEN 'I' THEN 'partitioned index' END as "Type",
5742                 pg_catalog.pg_get_userbyid(c.relowner) as "Owner"
5743               FROM pg_catalog.pg_class c
5744                    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5745                    LEFT JOIN pg_catalog.pg_am am ON am.oid = c.relam
5746               WHERE c.relkind IN ('r','p','')
5747                     AND n.nspname <> 'pg_catalog'
5748                     AND n.nspname !~ '^pg_toast'
5749                     AND n.nspname <> 'information_schema'
5750                 AND pg_catalog.pg_table_is_visible(c.oid)
5751               ORDER BY 1,2;"#,
5752            &catalog(),
5753        );
5754
5755        assert_eq!(names, vec!["Schema", "Name", "Type", "Owner"]);
5756        // ORDER BY 1,2 — schema then name, so `drivers` precedes `orders`.
5757        assert_eq!(col(&rows, "Name"), vec![json!("drivers"), json!("orders")]);
5758        assert_eq!(col(&rows, "Schema"), vec![json!("public"), json!("public")]);
5759        // The nine-branch CASE resolves relkind 'r'.
5760        assert_eq!(col(&rows, "Type"), vec![json!("table"), json!("table")]);
5761        assert_eq!(col(&rows, "Owner"), vec![json!("nedb"), json!("nedb")]);
5762    }
5763
5764    #[test]
5765    fn the_dt_query_still_filters_correctly_with_a_system_relation_present() {
5766        // A relation in pg_catalog must be excluded by the `<>`, and one with
5767        // an unlisted relkind by the IN list. If either filter were dropped —
5768        // the bug the parser restructure fixed — \dt would list internals.
5769        let t = tables(vec![
5770            (
5771                "pg_namespace",
5772                vec![
5773                    json!({"oid": 2200, "nspname": "public", "nspowner": 10}),
5774                    json!({"oid": 11, "nspname": "pg_catalog", "nspowner": 10}),
5775                ],
5776            ),
5777            (
5778                "pg_class",
5779                vec![
5780                    json!({"oid": 1, "relname": "mine", "relnamespace": 2200,
5781                           "relkind": "r", "relowner": 10, "relam": 2}),
5782                    json!({"oid": 2, "relname": "pg_internal", "relnamespace": 11,
5783                           "relkind": "r", "relowner": 10, "relam": 2}),
5784                    json!({"oid": 3, "relname": "an_index", "relnamespace": 2200,
5785                           "relkind": "i", "relowner": 10, "relam": 2}),
5786                ],
5787            ),
5788            ("pg_am", vec![json!({"oid": 2, "amname": "heap"})]),
5789        ]);
5790        let (_, rows) = go(
5791            r#"SELECT c.relname as "Name" FROM pg_catalog.pg_class c
5792                 LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
5793               WHERE c.relkind IN ('r','p','') AND n.nspname <> 'pg_catalog'
5794               ORDER BY 1"#,
5795            &t,
5796        );
5797        assert_eq!(col(&rows, "Name"), vec![json!("mine")],
5798                   "a system relation and an index must both be filtered out");
5799    }
5800}
5801
5802#[cfg(test)]
5803mod operator_syntax_tests {
5804    use super::*;
5805    use serde_json::json;
5806
5807    #[test]
5808    fn the_OPERATOR_qualification_psql_generates_is_understood() {
5809        // `\d` writes every operator this way:
5810        //   c.relname OPERATOR(pg_catalog.~) '^(orders)$'
5811        // It names exactly the operator it wraps, so the schema is dropped.
5812        let s = parse(
5813            "SELECT a FROM t WHERE n OPERATOR(pg_catalog.~) '^x' \
5814             AND m OPERATOR(pg_catalog.=) 1",
5815        )
5816        .expect("psql's OPERATOR() form must parse");
5817        match s.where_.unwrap() {
5818            Expr::Binary { op, left, .. } => {
5819                assert_eq!(op, "AND");
5820                assert!(matches!(*left, Expr::Binary { ref op, .. } if op == "~"));
5821            }
5822            other => panic!("{:?}", other),
5823        }
5824    }
5825
5826    #[test]
5827    fn an_OPERATOR_qualified_comparison_EVALUATES() {
5828        let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
5829            Ok(Some(from_vec(vec![json!({"n": "orders"}), json!({"n": "pg_toast_1"})])))
5830        };
5831        let (_, rows) = run(
5832            "SELECT n FROM pg_class WHERE n OPERATOR(pg_catalog.~) '^ord'",
5833            &t,
5834        )
5835        .unwrap();
5836        assert_eq!(rows.len(), 1);
5837        assert_eq!(rows[0]["n"], json!("orders"));
5838    }
5839
5840    #[test]
5841    fn a_subquery_an_ARRAY_constructor_and_EXISTS_all_PARSE() {
5842        // These used to be refused by name. They are the constructs `\d`,
5843        // `\dp` and `\dT` hinge on, and now each has a variant of its own.
5844        let s = parse("SELECT a FROM t WHERE x = (SELECT 1)").unwrap();
5845        assert!(matches!(s.where_, Some(Expr::Binary { ref right, .. }) if matches!(**right, Expr::Subquery(_))));
5846        let s = parse("SELECT array_to_string(ARRAY(SELECT a FROM b), ',') FROM t").unwrap();
5847        assert!(matches!(&s.items[0].expr, Expr::Func { args, .. } if matches!(args[0], Expr::ArrayQuery(_))));
5848        let s = parse("SELECT a FROM t WHERE EXISTS (SELECT 1)").unwrap();
5849        assert!(matches!(s.where_, Some(Expr::Exists { negated: false, .. })));
5850        let s = parse("SELECT a FROM t WHERE NOT EXISTS (SELECT 1)").unwrap();
5851        assert!(matches!(s.where_, Some(Expr::Unary { ref expr, .. }) if matches!(**expr, Expr::Exists { .. })));
5852        // Quantified comparisons, subscripts, CAST(), IS DISTINCT FROM.
5853        let s = parse("SELECT a FROM t WHERE oid = ANY (polroles) AND 'd' = any(kinds) AND x <> ALL (SELECT y FROM u)").unwrap();
5854        assert!(s.where_.is_some());
5855        let s = parse("SELECT prattrs[s] FROM t").unwrap();
5856        assert!(matches!(s.items[0].expr, Expr::Index { .. }));
5857        let s = parse("SELECT CAST('tuple' AS pg_catalog.text), CAST(n AS int2[]) FROM t").unwrap();
5858        assert!(matches!(&s.items[0].expr, Expr::Cast { ty, .. } if ty == "text"));
5859        assert!(matches!(&s.items[1].expr, Expr::Cast { ty, .. } if ty == "int2[]"));
5860        let s = parse("SELECT a FROM t WHERE a IS DISTINCT FROM b").unwrap();
5861        assert!(matches!(s.where_, Some(Expr::Binary { ref op, .. }) if op == "IS DISTINCT FROM"));
5862        // A comma FROM list interleaved with joins, as `\dF+` writes it.
5863        let s = parse("SELECT 1 FROM c LEFT JOIN n ON n.oid = c.ns, p LEFT JOIN np ON np.oid = p.ns").unwrap();
5864        assert_eq!(s.joins.len(), 3);
5865        assert!(matches!(s.joins[1].kind, JoinKind::Cross));
5866        // LATERAL and a derived table.
5867        let s = parse("SELECT 1 FROM c, LATERAL (SELECT 2 AS two) s").unwrap();
5868        assert!(s.joins[0].table.lateral && s.joins[0].table.sub.is_some());
5869        let s = parse("SELECT tt.a FROM (SELECT 1 AS a UNION ALL SELECT 2) AS tt ORDER BY 1").unwrap();
5870        assert_eq!(s.from.as_ref().unwrap().sub.as_ref().unwrap().set_ops.len(), 1);
5871    }
5872
5873    #[test]
5874    fn a_compound_query_keeps_ORDER_BY_for_the_whole() {
5875        let s = parse("SELECT a FROM t UNION SELECT b FROM u UNION ALL SELECT c FROM v ORDER BY 1 LIMIT 5").unwrap();
5876        assert_eq!(s.set_ops.len(), 2);
5877        assert_eq!(s.set_ops[0].op, SetOp::Union);
5878        assert!(!s.set_ops[0].all);
5879        assert!(s.set_ops[1].all);
5880        assert_eq!(s.order_by.len(), 1);
5881        assert_eq!(s.limit, Some(5));
5882        assert!(s.set_ops[1].query.order_by.is_empty(), "the tail belongs to the whole, not the last arm");
5883    }
5884}
5885
5886#[cfg(test)]
5887mod subquery_exec_tests {
5888    use super::*;
5889    use serde_json::json;
5890
5891    fn tables(defs: Vec<(&str, Vec<Value>)>) -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5892        let owned: Vec<(String, Vec<Value>)> =
5893            defs.into_iter().map(|(n, r)| (n.to_string(), r)).collect();
5894        move |name: &str| {
5895            let bare = name.rsplit('.').next().unwrap_or(name);
5896            Ok(owned.iter().find(|(n, _)| n == name || n == bare).map(|(_, r)| from_vec(r.clone())))
5897        }
5898    }
5899
5900    fn go(sql: &str, r: &Resolver) -> (Vec<String>, Vec<Value>) {
5901        let (cols, rows) = run(sql, r).unwrap_or_else(|e| panic!("{}\n  -> {}", sql, e));
5902        (cols.into_iter().map(|c| c.name).collect(), rows)
5903    }
5904
5905    fn col(rows: &[Value], name: &str) -> Vec<Value> {
5906        rows.iter().map(|r| r.get(name).cloned().unwrap_or(Value::Null)).collect()
5907    }
5908
5909    fn shop() -> impl Fn(&str) -> Result<Option<Box<dyn Relation>>> {
5910        tables(vec![
5911            ("c", vec![
5912                json!({"id": 1, "name": "ann", "tags": ["a", "b"]}),
5913                json!({"id": 2, "name": "bob", "tags": []}),
5914                json!({"id": 3, "name": "cyd", "tags": null}),
5915            ]),
5916            ("o", vec![
5917                json!({"oid": 10, "cid": 1, "total": 5}),
5918                json!({"oid": 11, "cid": 1, "total": 7}),
5919                json!({"oid": 12, "cid": 2, "total": 9}),
5920            ]),
5921        ])
5922    }
5923
5924    #[test]
5925    fn a_correlated_scalar_subquery_sees_the_outer_row() {
5926        let t = shop();
5927        let (_, rows) = go(
5928            "SELECT c.name, (SELECT sum(o.total) FROM o WHERE o.cid = c.id) AS spent FROM c ORDER BY c.id",
5929            &t,
5930        );
5931        assert_eq!(col(&rows, "spent"), vec![json!(12), json!(9), Value::Null]);
5932        // A scalar subquery returning two rows is an error, as in Postgres.
5933        let e = run("SELECT (SELECT o.total FROM o WHERE o.cid = c.id) FROM c", &t).unwrap_err().to_string();
5934        assert!(e.contains("more than one row"), "{}", e);
5935        // And two columns is an error too.
5936        let e = run("SELECT (SELECT oid, total FROM o) FROM c", &t).unwrap_err().to_string();
5937        assert!(e.contains("exactly one column"), "{}", e);
5938    }
5939
5940    #[test]
5941    fn EXISTS_and_NOT_EXISTS_are_never_unknown() {
5942        let t = shop();
5943        let (_, rows) = go("SELECT c.name FROM c WHERE EXISTS (SELECT 1 FROM o WHERE o.cid = c.id) ORDER BY 1", &t);
5944        assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5945        let (_, rows) = go("SELECT c.name FROM c WHERE NOT EXISTS (SELECT 1 FROM o WHERE o.cid = c.id)", &t);
5946        assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5947    }
5948
5949    #[test]
5950    fn ARRAY_of_a_subquery_and_array_to_string_compose_like_psql_dp() {
5951        let t = shop();
5952        let (_, rows) = go(
5953            "SELECT c.name, array_to_string(ARRAY(SELECT o.total FROM o WHERE o.cid = c.id ORDER BY o.total), ',') AS totals FROM c ORDER BY c.id",
5954            &t,
5955        );
5956        // An empty ARRAY joins to the empty string, not NULL — as Postgres.
5957        assert_eq!(col(&rows, "totals"), vec![json!("5,7"), json!("9"), json!("")]);
5958        let (_, rows) = go("SELECT array_length(ARRAY(SELECT oid FROM o), 1) AS n FROM c WHERE c.id = 1", &t);
5959        assert_eq!(col(&rows, "n"), vec![json!(3)]);
5960    }
5961
5962    #[test]
5963    fn ANY_ALL_and_IN_over_arrays_and_subqueries() {
5964        let t = shop();
5965        let (_, rows) = go("SELECT c.name FROM c WHERE 'a' = ANY (c.tags) ORDER BY 1", &t);
5966        assert_eq!(col(&rows, "name"), vec![json!("ann")]);
5967        // ANY over an empty array is false; over NULL is NULL — neither row.
5968        let (_, rows) = go("SELECT c.name FROM c WHERE 'zz' = ANY (c.tags)", &t);
5969        assert!(rows.is_empty());
5970        let (_, rows) = go("SELECT c.name FROM c WHERE c.id = ANY (SELECT o.cid FROM o) ORDER BY 1", &t);
5971        assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5972        let (_, rows) = go("SELECT c.name FROM c WHERE c.id <> ALL (SELECT o.cid FROM o)", &t);
5973        assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5974        let (_, rows) = go("SELECT c.name FROM c WHERE c.id IN (SELECT o.cid FROM o WHERE o.total > 6) ORDER BY 1", &t);
5975        assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
5976        let (_, rows) = go("SELECT c.name FROM c WHERE c.id NOT IN (SELECT o.cid FROM o)", &t);
5977        assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
5978        // Subscripts are one-based.
5979        let (_, rows) = go("SELECT c.tags[2] AS second FROM c WHERE c.id = 1", &t);
5980        assert_eq!(col(&rows, "second"), vec![json!("b")]);
5981    }
5982
5983    #[test]
5984    fn set_operations_combine_arms_and_sort_the_whole() {
5985        let t = shop();
5986        let (names, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1", &t);
5987        assert_eq!(names, vec!["k"], "column names come from the first arm");
5988        assert_eq!(col(&rows, "k"), vec![json!(1), json!(1), json!(1), json!(2), json!(2), json!(3)]);
5989        let (_, rows) = go("SELECT c.id AS k FROM c UNION SELECT o.cid FROM o ORDER BY 1", &t);
5990        assert_eq!(col(&rows, "k"), vec![json!(1), json!(2), json!(3)]);
5991        let (_, rows) = go("SELECT c.id AS k FROM c INTERSECT SELECT o.cid FROM o ORDER BY 1", &t);
5992        assert_eq!(col(&rows, "k"), vec![json!(1), json!(2)]);
5993        let (_, rows) = go("SELECT c.id AS k FROM c EXCEPT SELECT o.cid FROM o", &t);
5994        assert_eq!(col(&rows, "k"), vec![json!(3)]);
5995        let (_, rows) = go("SELECT c.id AS k FROM c UNION ALL SELECT o.cid FROM o ORDER BY 1 DESC LIMIT 2", &t);
5996        assert_eq!(col(&rows, "k"), vec![json!(3), json!(2)]);
5997        let e = run("SELECT c.id FROM c UNION SELECT o.oid, o.cid FROM o", &t).unwrap_err().to_string();
5998        assert!(e.contains("same number of columns"), "{}", e);
5999    }
6000
6001    #[test]
6002    fn a_derived_table_is_a_relation_and_LATERAL_sees_its_left() {
6003        let t = shop();
6004        let (_, rows) = go(
6005            "SELECT tt.who FROM (SELECT c.name AS who FROM c WHERE c.id < 3) AS tt ORDER BY 1",
6006            &t,
6007        );
6008        assert_eq!(col(&rows, "who"), vec![json!("ann"), json!("bob")]);
6009        // Column aliases rename positionally.
6010        let (_, rows) = go("SELECT tt.x FROM (SELECT c.name FROM c WHERE c.id = 1) AS tt(x)", &t);
6011        assert_eq!(col(&rows, "x"), vec![json!("ann")]);
6012        // LATERAL: one aggregate per left row, then ORDER BY an output alias.
6013        let (_, rows) = go(
6014            "SELECT c.name AS \"Name\", s.n AS \"Orders\" FROM c, LATERAL (SELECT count(*) AS n FROM o WHERE o.cid = c.id) s ORDER BY \"Orders\" DESC, \"Name\"",
6015            &t,
6016        );
6017        assert_eq!(col(&rows, "Name"), vec![json!("ann"), json!("bob"), json!("cyd")]);
6018        assert_eq!(col(&rows, "Orders"), vec![json!(2), json!(1), json!(0)]);
6019    }
6020
6021    #[test]
6022    fn table_functions_generate_series_and_unnest() {
6023        let t = shop();
6024        let (_, rows) = go("SELECT s.generate_series AS n FROM generate_series(1, 3) s", &t);
6025        assert_eq!(col(&rows, "n"), vec![json!(1), json!(2), json!(3)]);
6026        let (_, rows) = go("SELECT x FROM pg_catalog.unnest(ARRAY['p', 'q']) AS t(x)", &t);
6027        assert_eq!(col(&rows, "x"), vec![json!("p"), json!("q")]);
6028        // unnest over the OUTER row's column, as `\dy` writes it.
6029        let (_, rows) = go(
6030            "SELECT c.name, array_to_string(array(select x from pg_catalog.unnest(c.tags) as t(x)), ', ') AS tags FROM c ORDER BY c.id",
6031            &t,
6032        );
6033        assert_eq!(col(&rows, "tags"), vec![json!("a, b"), json!(""), json!("")]);
6034        let e = run("SELECT 1 FROM nosuchfn(1) f", &t).unwrap_err().to_string();
6035        assert!(e.contains("table function nosuchfn()"), "{}", e);
6036    }
6037
6038    #[test]
6039    fn aggregates_without_GROUP_BY_collapse_to_one_row() {
6040        let t = shop();
6041        let (names, rows) = go(
6042            "SELECT count(*), count(c.tags) AS tagged, min(c.name), max(c.name) AS hi, string_agg(c.name, '|') AS all FROM c",
6043            &t,
6044        );
6045        assert_eq!(names, vec!["count", "tagged", "min", "hi", "all"]);
6046        assert_eq!(rows.len(), 1);
6047        assert_eq!(rows[0]["count"], json!(3));
6048        assert_eq!(rows[0]["tagged"], json!(2), "count(x) skips NULL");
6049        assert_eq!(rows[0]["min"], json!("ann"));
6050        assert_eq!(rows[0]["hi"], json!("cyd"));
6051        assert_eq!(rows[0]["all"], json!("ann|bob|cyd"));
6052        // Over no rows: count is 0, everything else NULL.
6053        let (_, rows) = go("SELECT count(*) AS n, sum(o.total) AS s FROM o WHERE o.total > 100", &t);
6054        assert_eq!(rows[0]["n"], json!(0));
6055        assert_eq!(rows[0]["s"], Value::Null);
6056        // Arithmetic around an aggregate works; a bare column beside one is
6057        // refused with Postgres's own message.
6058        let (_, rows) = go("SELECT sum(o.total) / count(*) AS avg_total, avg(o.total) AS a FROM o", &t);
6059        assert_eq!(rows[0]["avg_total"], json!(7));
6060        assert_eq!(rows[0]["a"], json!(7));
6061        let e = run("SELECT c.name, count(*) FROM c", &t).unwrap_err().to_string();
6062        assert!(e.contains("must appear in the GROUP BY clause"), "{}", e);
6063    }
6064
6065    #[test]
6066    fn IS_DISTINCT_FROM_is_null_safe() {
6067        let t = shop();
6068        let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS DISTINCT FROM NULL ORDER BY 1", &t);
6069        assert_eq!(col(&rows, "name"), vec![json!("ann"), json!("bob")]);
6070        let (_, rows) = go("SELECT c.name FROM c WHERE c.tags IS NOT DISTINCT FROM NULL", &t);
6071        assert_eq!(col(&rows, "name"), vec![json!("cyd")]);
6072    }
6073
6074    #[test]
6075    fn THE_dT_QUERY_RUNS_over_a_catalogue_fixture() {
6076        // psql 17's \dT, verbatim: two correlated subqueries and NOT EXISTS.
6077        let t = tables(vec![
6078            ("pg_namespace", vec![
6079                json!({"oid": 11, "nspname": "pg_catalog"}),
6080                json!({"oid": 2200, "nspname": "public"}),
6081            ]),
6082            ("pg_type", vec![
6083                json!({"oid": 25, "typname": "text", "typnamespace": 11, "typrelid": 0, "typelem": 0, "typarray": 1009}),
6084                json!({"oid": 1009, "typname": "_text", "typnamespace": 11, "typrelid": 0, "typelem": 25, "typarray": 0}),
6085                json!({"oid": 70000, "typname": "mood", "typnamespace": 2200, "typrelid": 0, "typelem": 0, "typarray": 70001}),
6086                json!({"oid": 70001, "typname": "_mood", "typnamespace": 2200, "typrelid": 0, "typelem": 70000, "typarray": 0}),
6087            ]),
6088            ("pg_class", vec![]),
6089        ]);
6090        let (_, rows) = go(
6091            r#"SELECT n.nspname as "Schema",
6092                 pg_catalog.format_type(t.oid, NULL) AS "Name",
6093                 pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
6094               FROM pg_catalog.pg_type t
6095                    LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
6096               WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
6097                 AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
6098                 AND n.nspname <> 'pg_catalog'
6099                 AND n.nspname <> 'information_schema'
6100                 AND pg_catalog.pg_type_is_visible(t.oid)
6101               ORDER BY 1, 2;"#,
6102            &t,
6103        );
6104        // `_mood` is hidden by NOT EXISTS (its element type's typarray is
6105        // it), `text` and `_text` by the schema filter — `mood` remains.
6106        assert_eq!(rows.len(), 1, "{:?}", rows);
6107        assert_eq!(col(&rows, "Schema"), vec![json!("public")]);
6108    }
6109}
6110
6111#[cfg(test)]
6112mod collate_tests {
6113    use super::*;
6114    use serde_json::json;
6115
6116    #[test]
6117    fn COLLATE_is_consumed_because_it_cannot_change_the_answer() {
6118        // psql writes `COLLATE pg_catalog."C"` throughout `\d`. NEDB has one
6119        // collation, so refusing a clause that provably has no effect would
6120        // reject a query whose result is already correct.
6121        for sql in [
6122            r#"SELECT a FROM t ORDER BY a COLLATE "C""#,
6123            r#"SELECT a COLLATE "C" FROM t"#,
6124            r#"SELECT a FROM t WHERE a COLLATE pg_catalog."C" = 'x'"#,
6125        ] {
6126            parse(sql).unwrap_or_else(|e| panic!("{} -> {}", sql, e));
6127        }
6128        // A malformed COLLATE is still an error rather than silently skipped.
6129        assert!(parse("SELECT a FROM t ORDER BY a COLLATE").is_err());
6130    }
6131
6132    #[test]
6133    fn a_COLLATE_annotated_comparison_still_evaluates() {
6134        let t = |_: &str| -> Result<Option<Box<dyn Relation>>> {
6135            Ok(Some(from_vec(vec![json!({"n": "b"}), json!({"n": "a"})])))
6136        };
6137        let (_, rows) = run(r#"SELECT n FROM pg_class ORDER BY n COLLATE "C""#, &t).unwrap();
6138        assert_eq!(rows[0]["n"], json!("a"), "the ORDER BY still sorts");
6139    }
6140}