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