beck_core/
query.rs

1//! The read model's relational half: a `select` compiled into the plan.
2//!
3//! [`docs/99-the-data-tier-means-of-combination.md`](../../../../../docs/99-the-data-tier-means-of-combination.md)
4//! §99.9 item 9:
5//!
6//! > The read-model SQL grows joins and `group by` **by compiling into the plan**, not by growing
7//! > its own interpreter — which closes §23.19 and §12.5 together and keeps one code path.
8//!
9//! # What this module is, in one sentence
10//!
11//! It writes the Beck expression a person would have written, and hands it to
12//! [`crate::plan::Plan::of_query`]. Nothing here joins anything, groups anything or deduplicates
13//! anything: `select … join … on b.k = a.k` becomes the loop `for a in as: for b in bs where
14//! b.k == a.k`, which [`crate::relate`] already reads as an equi-join over an index, and `group by
15//! g` becomes the loop over `list_unique` that `corpus/35-workload.beck` writes by hand. The
16//! operators are [`crate::plan::Op::Join`], [`crate::plan::Op::ArrangeBy`],
17//! [`crate::plan::Op::GroupBy`] and [`crate::plan::Op::Distinct`] — the same ones, with the same
18//! delta rules, that a program's view compiles to.
19//!
20//! That is the whole of item 9, and it is worth naming what the alternative would have cost: a
21//! second join, a second set of aggregates and a second `distinct` living beside the first,
22//! agreeing by inspection rather than by construction, and a differential harness that covers one
23//! of the two.
24//!
25//! # The shape each query compiles to
26//!
27//! | SQL | The expression |
28//! |---|---|
29//! | `from a join b on b.k = a.k` | `concat_lists(map_list(a, λx. map_list(filter_list(b, λy. y.k == x.k), λy. row)))` — one stage per join, left-deep, so each stage is its own `map_list` in the plan and therefore gets its own join and its own index |
30//! | `group by g` | `map_list(list_unique(map_list(R, λr. g(r))), λk. …)`, each aggregate a question about `filter_list(R, λr. g(r) == k)` |
31//! | `count(*)` per group | `list_len` of that filter — the join's own tally, so no group is built |
32//! | `min/max/sum(c)` per group | `list_min(map_list(that filter, λr. r.c))` — [`crate::plan::Op::GroupBy`] |
33//! | `distinct` | `list_unique` of the projected row |
34//! | an aggregate with no `group by` | the aggregate of the whole collection, as one row |
35//!
36//! # What a column is, and why the rows are normalised first
37//!
38//! A table's columns are a schema fact; a table's element is a run-time value — a record for a
39//! collection of models, the element itself for a collection of scalars. Rather than teach the
40//! compiled expression that difference, every table's rows go through [`Table::row_values`] first,
41//! which is the function [`Table::row`] builds a scan's cells with. So a column is `c{n}` inside
42//! the plan, for every table, and the scan and the join cannot disagree about what a column is.
43//!
44//! # What is deliberately not here
45//!
46//! * **No cost-based ordering.** The `from` list fixes the left-deep order, exactly as a `for` loop
47//!   fixes it ([`docs/99`](../../../../../docs/99-the-data-tier-means-of-combination.md) §99.8's
48//!   "an inferred surface postpones the solver", arrived at from the other side: a *written*
49//!   surface fixes the order outright, so there is still nothing for a solver to choose).
50//! * **No outer join**, because an unmatched row would need columns invented for it.
51//! * **No `having`**: a `where` narrows the rows before they are grouped, and a filter over the
52//!   groups themselves would be a second predicate language.
53
54use std::collections::BTreeSet;
55use std::sync::Arc;
56
57use beck_diag::Span;
58
59use crate::core::{Const, Core, CoreKind, Fields, Prim, Value, VarId};
60use crate::engine::{Engine, Prepared};
61use crate::plan::{Agg, Plan, Relate};
62use crate::read::{
63    self, Cell, Column, Datum, Eval, Field, Item, Name, Schema, Select, SqlError, SqlTy, Table,
64};
65use crate::ty::{Tier, Ty};
66
67/// A conjunction — the shape [`crate::read`] parses a `where` into, because `and` is the operator
68/// that lets one term be pushed into one table's scan while another is not.
69type Filter = Vec<read::Expr>;
70
71/// One record per row, as the fields of the [`CoreKind::Make`] that builds it.
72type Row = Vec<(Arc<str>, Core)>;
73
74/// A `select`'s relational half, compiled and ready to run.
75pub struct Compiled {
76    /// What the rows this produces are called, in order.
77    pub fields: Vec<Field>,
78    /// Whether those rows are already the select list's answer.
79    ///
80    /// True for a `group by` and for a `distinct`, because the operator that produced them was
81    /// compiled *from* that list — the projection happened inside the plan. False for a plain join,
82    /// whose rows carry every column of every table so that a `where` or an `order by` can name one
83    /// the select list does not.
84    pub projected: bool,
85    /// The `where` terms this did **not** apply, for the caller to apply to the rows.
86    ///
87    /// A term that names one table is pushed into that table's scan. One that spans two tables
88    /// cannot be, and is left here — except an equality between two columns, which is a join
89    /// condition written in the `where` and is moved into the `on` it means.
90    pub residual: Filter,
91    /// The tables the plan reads, in the order its state record holds them.
92    inputs: Vec<Input>,
93    plan: Plan,
94    /// Whether the plan's root is one row rather than a collection of them — an aggregate with no
95    /// `group by` is a question about the whole table, and its answer is one row.
96    single: bool,
97}
98
99/// One table the compiled plan reads.
100struct Input {
101    table: Table,
102    /// What the query called it, because a prefilter's conditions are written with that name.
103    alias: Arc<str>,
104    /// Where this table's columns start in the row the plan reads, so `c{n}` names the same column
105    /// everywhere in it.
106    base: usize,
107    prefilter: Filter,
108}
109
110impl Compiled {
111    /// The plan this query compiles to — what `beck explain` prints, and what a gate reads.
112    pub fn plan(&self) -> &Plan {
113        &self.plan
114    }
115
116    /// Read the tables, run the plan, and answer with the rows.
117    pub fn run(&self, schema: &Schema, rows: &dyn read::Rows) -> Result<Vec<Vec<Cell>>, SqlError> {
118        Ok(self.run_measured(schema, rows)?.0)
119    }
120
121    /// The same, and what the engine did to produce it.
122    ///
123    /// [`crate::engine::Work`] counts applications, entries touched and operators recomputed, so a
124    /// gate can say "answering this join did not reconsider every pair" without a clock in it —
125    /// which is what `scaling.rs` asserts about a program's loops and now asserts about a query's.
126    pub fn run_measured(
127        &self,
128        schema: &Schema,
129        rows: &dyn read::Rows,
130    ) -> Result<(Vec<Vec<Cell>>, crate::engine::Work), SqlError> {
131        let backend = rows.backend().ok_or_else(|| SqlError {
132            message: "this reader has no executor behind it, so a join, a `group by` and a \
133                      `distinct` cannot be answered: they are compiled into the view plan, and a \
134                      plan is prepared by a backend"
135                .to_string(),
136            code: "0A000",
137        })?;
138
139        let mut state = Fields::with_capacity(self.inputs.len());
140        for (i, input) in self.inputs.iter().enumerate() {
141            let table = &input.table;
142            let values = scan(schema, table, rows)?;
143            // The query's own name for the table, not the table's: a prefilter says `p.name` when
144            // the `from` said `people p`, and one rule for what a name means is the whole point of
145            // resolving it here the way the joined rows resolve it.
146            let fields: Vec<Field> = table
147                .columns
148                .iter()
149                .map(|c| Field {
150                    column: c.clone(),
151                    of: Some(input.alias.clone()),
152                })
153                .collect();
154            let ev = Eval::new(schema, &fields, rows);
155            let mut out = Vec::with_capacity(values.len());
156            for v in &values {
157                if !input.prefilter.is_empty() && !ev.holds(&input.prefilter, &table.row(v))? {
158                    continue;
159                }
160                out.push(normalise(table, v, input.base));
161            }
162            state.insert(Arc::from(table_field(i)), Value::list(out));
163        }
164        let state = Value::data("Query", None, state);
165
166        let prepared = Prepared::new(Arc::new(self.plan.clone()), backend).map_err(exec)?;
167        let mut engine = Engine::new(Arc::new(prepared));
168        let out = engine
169            .render(&state, &Value::Unit, &Value::Unit)
170            .map_err(exec)?;
171
172        let values: Vec<Value> = match (&out, self.single) {
173            (_, true) => vec![out.clone()],
174            (Value::List(xs), false) => xs.to_vec(),
175            (Value::Map(m), false) => m.iter().map(|(_, v)| v.clone()).collect(),
176            (other, false) => vec![(*other).clone()],
177        };
178        let out = values
179            .iter()
180            .map(|v| {
181                self.fields
182                    .iter()
183                    .enumerate()
184                    .map(|(i, f)| match v.field(&field_name(i)) {
185                        Some(x) => read::cell_of(x, &f.column),
186                        None => None,
187                    })
188                    .collect()
189            })
190            .collect();
191        Ok((out, engine.work()))
192    }
193}
194
195fn exec(e: crate::backend::ExecError) -> SqlError {
196    SqlError {
197        message: format!("the query's operators could not be run: {e}"),
198        code: "58000",
199    }
200}
201
202/// A table's rows, from wherever that table's rows come from.
203///
204/// The catalogue and `pg_catalog` are built rather than scanned, and both are tables a join may
205/// name: "which tables have a column called `id`" is a self-join over one, and `psql`'s `\d` is a
206/// join over three of the other.
207fn scan(schema: &Schema, table: &Table, rows: &dyn read::Rows) -> Result<Vec<Value>, SqlError> {
208    match schema.builtin_rows(table) {
209        Some(values) => Ok(values),
210        None => rows.scan(table),
211    }
212}
213
214/// One element as the record the plan reads columns out of: `c{base+i}` per column.
215fn normalise(table: &Table, v: &Value, base: usize) -> Value {
216    Value::data(
217        "Row",
218        None,
219        table
220            .row_values(v)
221            .into_iter()
222            .enumerate()
223            .map(|(i, v)| (Arc::from(field_name(base + i)), v))
224            .collect(),
225    )
226}
227
228/// What a column is called inside the plan, and inside the record it produces.
229fn field_name(i: usize) -> String {
230    format!("c{i}")
231}
232
233/// What a table is called in the record the plan is handed as its state.
234fn table_field(i: usize) -> String {
235    format!("t{i}")
236}
237
238// -------------------------------------------------------------------------------------------
239// Deciding whether the plan is needed at all
240// -------------------------------------------------------------------------------------------
241
242/// Whether a `select` needs the plan.
243///
244/// The boundary is which *operators* the query wants, not how big it is. A scan with a `where` and
245/// an `order by` is what [`crate::read`] has always answered directly and it keeps answering it —
246/// `select count(*)` included, whose whole point is that a maintained arrangement already knows its
247/// own size ([`docs/23`](../../../../../docs/23-incremental-views-report.md) §23.19) and that
248/// compiling a plan to rediscover it would be work for nothing. What comes here is a query that
249/// **relates, groups or deduplicates**, because those are the three things the plan has operators
250/// for and this module has none.
251pub fn relational(s: &Select) -> bool {
252    s.from.len() > 1
253        || !s.group.is_empty()
254        || s.distinct
255        || s.items.iter().any(|i| matches!(i, Item::Aggregate(..)))
256}
257
258// -------------------------------------------------------------------------------------------
259// The compiler
260// -------------------------------------------------------------------------------------------
261
262/// Compile a `select` into a plan over the tables it names.
263pub fn compile(schema: &Schema, s: &Select) -> Result<Compiled, SqlError> {
264    compile_with(schema, s, Relate::default())
265}
266
267/// The same, with [`Relate`] said out loud.
268///
269/// [`docs/08`](../../../../../docs/08-roadmap.md) §8.3 item 8's off switch reaches this surface for
270/// the reason it reaches a program's: the recognition is what turns a nested loop into an indexed
271/// join, and a default nobody has run is a claim. [`Relate::Refuse`] compiles the same expression
272/// to the nested loop it literally is, which is what a gate measures the operator against.
273pub fn compile_with(schema: &Schema, s: &Select, relate: Relate) -> Result<Compiled, SqlError> {
274    let mut q = Query {
275        schema,
276        entries: Vec::new(),
277        fields: Vec::new(),
278        fresh: 0,
279    };
280    q.resolve_from(s)?;
281    if q.entries.is_empty() {
282        return Err(SqlError::syntax(
283            "a join, a `group by` and a `distinct` are all questions about a table, and this \
284             query has no `from`",
285        ));
286    }
287    q.fresh = q.entries.len() as VarId;
288
289    // A comma join's equality lives in the `where`, and it is the same query as a `join … on`:
290    // lifting it gives the indexed operator rather than a cross product the filter then throws
291    // most of away. One that cannot be lifted stays in the `where` and is answered over the pairs,
292    // which is correct and `O(rows × rows)` — the cost of writing it that way.
293    let filter = q.lift_join_conditions(s.filter.clone());
294    let (residual, prefilters) = q.push_down(&filter)?;
295    let grouping = !s.group.is_empty() || s.items.iter().any(Item::aggregates);
296    if !residual.is_empty() && (grouping || s.distinct) {
297        return Err(SqlError::unsupported(format!(
298            "a `where` that spans two tables cannot be applied before a `{}`: every condition here \
299             narrows one table, and an `or` across two of them would have to be a filter over the \
300             joined rows",
301            match grouping {
302                true => "group by",
303                false => "distinct",
304            }
305        )));
306    }
307
308    // The state's fields are positional rather than named after the tables: a query may join a
309    // table to itself, and two fields with one name is not a record.
310    let tables: Vec<Arc<str>> = (0..q.entries.len())
311        .map(|i| Arc::from(table_field(i)))
312        .collect();
313    let rows = q.rows_expression();
314
315    // The stages below read the joined rows more than once — a `group by` reads them once for its
316    // keys and once per aggregate — so the rows are **bound** rather than rebuilt. A `let` is what
317    // makes that one node with several consumers rather than several nodes computing the same
318    // thing, which is §5.3's sharing at the granularity the plan shares at.
319    let bound = q.fresh();
320    let rows_var = var(bound);
321
322    let (body, fields, projected, single) = if grouping {
323        let (body, fields) = q.grouped(s, bound)?;
324        (body, fields, true, s.group.is_empty())
325    } else if s.distinct {
326        let param = q.fresh();
327        let (row, fields) = q.project(s, param)?;
328        let body = prim(
329            Prim::ListUnique,
330            vec![prim(
331                Prim::MapList,
332                vec![rows_var.clone(), lam(vec![param], make(row))],
333            )],
334        );
335        (body, fields, true, false)
336    } else {
337        (rows_var.clone(), q.fields.clone(), false, false)
338    };
339    // `distinct` over a grouped query is `list_unique` over the groups, which is what the two words
340    // mean together; the operator is the same one either way.
341    let body = match s.distinct && grouping && !single {
342        true => prim(Prim::ListUnique, vec![body]),
343        false => body,
344    };
345
346    let body = bind(bound, rows, body);
347    let plan = Plan::of_query_with(&tables, &body, relate);
348    let inputs = q
349        .entries
350        .iter()
351        .zip(prefilters)
352        .map(|(e, prefilter)| Input {
353            table: e.table.clone(),
354            alias: e.alias.clone(),
355            base: e.base,
356            prefilter,
357        })
358        .collect();
359    Ok(Compiled {
360        fields,
361        projected,
362        residual,
363        inputs,
364        plan,
365        single,
366    })
367}
368
369/// One `from` entry, resolved.
370struct Entry<'a> {
371    alias: Arc<str>,
372    table: &'a Table,
373    /// Where this table's columns start in the wide row.
374    base: usize,
375    /// The `on` equalities as (this table's column, an earlier table's column).
376    on: Vec<(usize, usize)>,
377    /// `left join`: a row before this one survives with nulls when this table has no match.
378    left: bool,
379}
380
381struct Query<'a> {
382    schema: &'a Schema,
383    entries: Vec<Entry<'a>>,
384    /// Every column of every table, in `from` order — the row a join produces.
385    fields: Vec<Field>,
386    fresh: VarId,
387}
388
389impl<'a> Query<'a> {
390    fn fresh(&mut self) -> VarId {
391        self.fresh += 1;
392        self.fresh - 1
393    }
394
395    /// Resolve the `from` list: the tables, their names in this query, and where each one's columns
396    /// sit in the row a join produces.
397    fn resolve_from(&mut self, s: &'a Select) -> Result<(), SqlError> {
398        for (i, f) in s.from.iter().enumerate() {
399            if let Some(call) = &f.function {
400                return Err(SqlError::unsupported(format!(
401                    "`{call}(…)` in a `from` is a set-returning function, and this read model has \
402                     none: what a `from` names here is a relation"
403                )));
404            }
405            let table = self.schema.relation(f.namespace.as_deref(), &f.table)?;
406            if self.entries.iter().any(|e| e.alias.as_ref() == f.alias) {
407                return Err(SqlError::syntax(format!(
408                    "\"{}\" is in this `from` twice; give one of them a name (`{} as x`)",
409                    f.alias, f.table
410                )));
411            }
412            let base = self.fields.len();
413            self.fields.extend(table.columns.iter().map(|c| Field {
414                column: c.clone(),
415                of: Some(Arc::from(f.alias.as_str())),
416            }));
417            self.entries.push(Entry {
418                alias: Arc::from(f.alias.as_str()),
419                table,
420                base,
421                on: Vec::new(),
422                left: f.left,
423            });
424            // Resolved after the entry exists, so a join may name its own columns.
425            let mut on = Vec::new();
426            for (l, r) in &f.on {
427                let (li, ri) = (self.resolve(l)?, self.resolve(r)?);
428                let pair = match (li >= base, ri >= base) {
429                    (true, false) => (li, ri),
430                    (false, true) => (ri, li),
431                    _ => {
432                        return Err(SqlError::unsupported(format!(
433                            "`on {l} = {r}` does not join \"{}\" to a table before it: one side \
434                             has to be a column of \"{}\" and the other a column of a table \
435                             already in the `from`",
436                            f.alias, f.alias
437                        )))
438                    }
439                };
440                // Two conditions on a join key, and both are the SQL semantics this operator does
441                // not have rather than an implementation gap. `==` here is [`Value`]'s own equality
442                // — the order the index is a `BTreeMap` in — so a `NULL` would equal a `NULL`,
443                // where SQL says it equals nothing; and `Int` and `Float` are different values,
444                // where SQL would coerce. Both are refused rather than answered differently from
445                // every other database.
446                let (a, b) = (&self.fields[pair.0], &self.fields[pair.1]);
447                if a.column.nullable || b.column.nullable {
448                    return Err(SqlError::unsupported(format!(
449                        "`on {l} = {r}` joins on a column that can be null, and this join's \
450                         equality is the index's own: a null would match a null, where SQL says a \
451                         null matches nothing"
452                    )));
453                }
454                if a.column.ty != b.column.ty {
455                    return Err(SqlError::unsupported(format!(
456                        "`on {l} = {r}` compares {} with {}, and this join's equality does not \
457                         coerce between them: give the two columns the same type",
458                        a.column.ty.name(),
459                        b.column.ty.name()
460                    )));
461                }
462                on.push(pair);
463            }
464            if i > 0 && on.is_empty() && !f.on.is_empty() {
465                return Err(SqlError::syntax(format!(
466                    "`join {}` wants `on <column> = <column>`",
467                    f.table
468                )));
469            }
470            self.entries[i].on = on;
471        }
472        Ok(())
473    }
474
475    /// A column reference, as an index into the wide row.
476    fn resolve(&self, n: &Name) -> Result<usize, SqlError> {
477        let matching: Vec<usize> = (0..self.fields.len())
478            .filter(|&i| {
479                let f = &self.fields[i];
480                f.column.name.as_ref() == n.column
481                    && match &n.table {
482                        Some(t) => f.of.as_deref() == Some(t.as_str()),
483                        None => true,
484                    }
485            })
486            .collect();
487        match matching.as_slice() {
488            [one] => Ok(*one),
489            [] => Err(SqlError::no_column(match &n.table {
490                Some(t) if !self.entries.iter().any(|e| e.alias.as_ref() == t.as_str()) => {
491                    format!(
492                        "\"{t}\" is not a table in this query; it has {}",
493                        self.entries
494                            .iter()
495                            .map(|e| format!("\"{}\"", e.alias))
496                            .collect::<Vec<_>>()
497                            .join(", ")
498                    )
499                }
500                _ => format!(
501                    "there is no column \"{n}\" here; there is {}",
502                    read::names_of(&self.fields)
503                ),
504            })),
505            _ => Err(SqlError::no_column(format!(
506                "\"{n}\" is ambiguous: more than one table in this query has a column called \
507                 \"{}\", so qualify it — `t.{}`",
508                n.column, n.column
509            ))),
510        }
511    }
512
513    /// Split the `where` into what each table can be scanned with, and what is left over.
514    ///
515    /// A term that names one table narrows that table's scan; one that names none is a constant
516    /// and narrows the first (so a `where false` reads nothing rather than everything); one that
517    /// spans two is left for the joined rows.
518    ///
519    /// A prefilter is what makes a `where` cost `O(rows)` on the table it names rather than
520    /// `O(rows × rows)` on the join — the rows it removes are rows no stage ever pairs.
521    ///
522    /// **A term over a `left join`'s own table is never pushed into it.** Removing a row from the
523    /// null-supplying side does not remove the joined row: it turns it into one with nulls, which
524    /// the term would then have rejected. `psql`'s `\d` is exactly this — `left join pg_namespace
525    /// n … where n.nspname <> 'pg_catalog'` — and pushing it down answers with every relation the
526    /// query asked to hide.
527    fn push_down(&self, filter: &Filter) -> Result<(Filter, Vec<Filter>), SqlError> {
528        let mut prefilters: Vec<Filter> = vec![Vec::new(); self.entries.len()];
529        let mut residual = Vec::new();
530        for term in filter {
531            let mut names = Vec::new();
532            term.names(&mut names);
533            // A name this query cannot resolve — one belonging to a subquery the term carries, or
534            // one that is simply wrong — makes the term unpushable rather than an error here: it
535            // is applied to the joined rows, where resolving it is what a person is shown.
536            let mut owners = BTreeSet::new();
537            let mut resolved = true;
538            for name in &names {
539                match self.resolve(name) {
540                    Ok(i) => {
541                        owners.insert(self.owner(i));
542                    }
543                    Err(_) => resolved = false,
544                }
545            }
546            if !resolved {
547                residual.push(term.clone());
548                continue;
549            }
550            match owners.iter().copied().collect::<Vec<_>>().as_slice() {
551                [] => prefilters[0].push(term.clone()),
552                [one] if !self.entries[*one].left => prefilters[*one].push(term.clone()),
553                _ => residual.push(term.clone()),
554            }
555        }
556        Ok((residual, prefilters))
557    }
558
559    /// Move `where a.k = b.k` into the `on` of whichever entry it joins, and answer what is left.
560    ///
561    /// It applies only to an entry that has no `on` of its own and is not a `left join`: those are
562    /// the entries a comma join and a `cross join` produce, and they are the ones whose equality
563    /// was written in the `where` because the syntax has nowhere else to put it. Everything the
564    /// lift does not recognise stays where it was, so this can make a query faster and cannot make
565    /// one answer differently.
566    fn lift_join_conditions(&mut self, terms: Filter) -> Filter {
567        let mut kept = Vec::with_capacity(terms.len());
568        for term in terms {
569            let read::Expr::Cmp(l, read::CmpOp::Eq, r) = &term else {
570                kept.push(term);
571                continue;
572            };
573            let (read::Expr::Column(l), read::Expr::Column(r)) = (&**l, &**r) else {
574                kept.push(term);
575                continue;
576            };
577            let (Ok(li), Ok(ri)) = (self.resolve(l), self.resolve(r)) else {
578                kept.push(term);
579                continue;
580            };
581            let (lo, hi) = (self.owner(li), self.owner(ri));
582            // The later entry is the one being joined; the earlier one is what it joins to.
583            let (entry, pair) = match lo.cmp(&hi) {
584                std::cmp::Ordering::Less => (hi, (ri, li)),
585                std::cmp::Ordering::Greater => (lo, (li, ri)),
586                std::cmp::Ordering::Equal => {
587                    kept.push(term);
588                    continue;
589                }
590            };
591            let e = &self.entries[entry];
592            let usable = entry > 0
593                && e.on.is_empty()
594                && !e.left
595                && !self.fields[pair.0].column.nullable
596                && !self.fields[pair.1].column.nullable
597                && self.fields[pair.0].column.ty == self.fields[pair.1].column.ty;
598            match usable {
599                true => self.entries[entry].on.push(pair),
600                false => kept.push(term),
601            }
602        }
603        kept
604    }
605
606    /// Which `from` entry a wide column belongs to.
607    fn owner(&self, column: usize) -> usize {
608        self.entries
609            .iter()
610            .rposition(|e| e.base <= column)
611            .unwrap_or(0)
612    }
613
614    /// The expression whose value is the rows the `from` list produces.
615    ///
616    /// One stage per join, left-deep — `concat_lists(map_list(prev, λr. map_list(filter_list(t, λy.
617    /// y.k == r.k), λy. row)))`. Each stage is a `map_list` of its own in the plan and therefore
618    /// gets its own [`crate::plan::Op::Join`] over its own index; nesting them inside one
619    /// per-element function would index the first join and leave the rest as nested loops, which is
620    /// the cost this exists to remove.
621    fn rows_expression(&mut self) -> Core {
622        let mut rows = var(0);
623        for i in 1..self.entries.len() {
624            let left = self.fresh();
625            let right = self.fresh();
626            let base = self.entries[i].base;
627            let width = self.entries[i].table.columns.len();
628            let pairs = self.entries[i].on.clone();
629            let (no_key, outer) = (pairs.is_empty(), self.entries[i].left);
630            let key_of = |v: VarId, mine: bool| -> Core {
631                let at = |p: &(usize, usize)| if mine { p.0 } else { p.1 };
632                match pairs.as_slice() {
633                    [one] => field(var(v), &field_name(at(one))),
634                    many => make(
635                        many.iter()
636                            .enumerate()
637                            .map(|(k, p)| {
638                                (
639                                    Arc::from(format!("k{k}")),
640                                    field(var(v), &field_name(at(p))),
641                                )
642                            })
643                            .collect(),
644                    ),
645                }
646            };
647            let combined = make(
648                (0..base)
649                    .map(|k| (Arc::from(field_name(k)), field(var(left), &field_name(k))))
650                    .chain(
651                        (base..base + width)
652                            .map(|k| (Arc::from(field_name(k)), field(var(right), &field_name(k)))),
653                    )
654                    .collect(),
655            );
656            // A `from` entry with no `on` is a cross product — a comma join whose equality stayed
657            // in the `where`, or a `cross join`. It is the loop without the filter, and the `where`
658            // then narrows the pairs it produced.
659            let source = match no_key {
660                true => var(i as VarId),
661                false => prim(
662                    Prim::FilterList,
663                    vec![
664                        var(i as VarId),
665                        lam(
666                            vec![right],
667                            prim(Prim::Eq, vec![key_of(right, true), key_of(left, false)]),
668                        ),
669                    ],
670                ),
671            };
672            let matched = prim(Prim::MapList, vec![source, lam(vec![right], combined)]);
673            // A `left join` keeps the left row when the group is empty, with this table's columns
674            // as units — which `read::cell_of` reads as the NULL SQL says they are. The group is
675            // **bound** rather than built twice: it is what the emptiness is a question about and
676            // what the answer is when it is not empty.
677            let inner = match outer {
678                false => matched,
679                true => {
680                    let group = self.fresh();
681                    let empty = make(
682                        (0..base)
683                            .map(|k| (Arc::from(field_name(k)), field(var(left), &field_name(k))))
684                            .chain((base..base + width).map(|k| {
685                                (Arc::from(field_name(k)), node(CoreKind::Const(Const::Unit)))
686                            }))
687                            .collect(),
688                    );
689                    bind(
690                        group,
691                        matched,
692                        node(CoreKind::If {
693                            cond: Box::new(prim(Prim::ListIsEmpty, vec![var(group)])),
694                            then: Box::new(node(CoreKind::ListLit(vec![empty]))),
695                            alt: Box::new(var(group)),
696                        }),
697                    )
698                }
699            };
700            rows = prim(
701                Prim::ConcatLists,
702                vec![prim(Prim::MapList, vec![rows, lam(vec![left], inner)])],
703            );
704        }
705        rows
706    }
707
708    /// The select list as the fields of one record per row, and what its columns are called.
709    fn project(&mut self, s: &Select, param: VarId) -> Result<(Row, Vec<Field>), SqlError> {
710        let mut out = Vec::new();
711        let mut fields = Vec::new();
712        for item in &s.items {
713            match item {
714                Item::All(qualifier) => {
715                    let mut any = false;
716                    for i in 0..self.fields.len() {
717                        if let Some(t) = qualifier {
718                            if self.fields[i].of.as_deref() != Some(t.as_str()) {
719                                continue;
720                            }
721                        }
722                        any = true;
723                        let f = self.fields[i].clone();
724                        push(&mut out, &mut fields, f, field(var(param), &field_name(i)));
725                    }
726                    if !any {
727                        return Err(SqlError::no_table(format!(
728                            "\"{}\" is not a table in this query",
729                            qualifier.as_deref().unwrap_or("")
730                        )));
731                    }
732                }
733                Item::Column(name, alias) => {
734                    let i = self.resolve(name)?;
735                    let mut f = self.fields[i].clone();
736                    if let Some(a) = alias {
737                        f.column.name = Arc::from(a.as_str());
738                        f.of = None;
739                    }
740                    push(&mut out, &mut fields, f, field(var(param), &field_name(i)));
741                }
742                Item::Literal(d, alias) => {
743                    let f = literal_field(d, alias.as_deref());
744                    push(&mut out, &mut fields, f, constant(d));
745                }
746                Item::Count(_) | Item::Aggregate(..) => {
747                    return Err(SqlError::unsupported(
748                        "`select distinct` over an aggregate has nothing to be distinct about; a \
749                         `group by` is what asks an aggregate per group",
750                    ))
751                }
752                // An expression in a `select distinct` or a `group by` would have to be compiled
753                // into the operator rather than evaluated over what it produced: the operator's
754                // key *is* the select list, so a `case` there is a `case` inside a
755                // `list_unique`. Nothing asks for one, and answering it wrongly would be worse
756                // than saying so.
757                Item::Expr(_, alias) => {
758                    return Err(SqlError::unsupported(format!(
759                        "`{}` is an expression, and a `{}` is over the columns it groups by: an \
760                         expression here would have to be part of the key the operator builds",
761                        alias.as_deref().unwrap_or("an expression"),
762                        match s.group.is_empty() {
763                            true => "select distinct",
764                            false => "group by",
765                        }
766                    )))
767                }
768            }
769        }
770        Ok((out, fields))
771    }
772
773    /// A `group by`, or an aggregate with no `group by` — which is the same question asked of the
774    /// whole collection rather than of a group.
775    ///
776    /// The grouped form is the loop `corpus/35-workload.beck` writes by hand: the distinct keys are
777    /// the rows, and each aggregate is a question about the filter that would have built the group.
778    /// [`crate::relate`] reads exactly that shape, so what the plan ends up with is a
779    /// [`crate::plan::Op::ArrangeBy`] for the counts, a [`crate::plan::Op::GroupBy`] per other
780    /// aggregate, and a join per question — and no group is ever built.
781    fn grouped(&mut self, s: &Select, rows: VarId) -> Result<(Core, Vec<Field>), SqlError> {
782        let keys: Vec<usize> = s
783            .group
784            .iter()
785            .map(|n| self.resolve(n))
786            .collect::<Result<_, _>>()?;
787        let ungrouped = keys.is_empty();
788        let element = self.fresh();
789
790        let mut out = Vec::new();
791        let mut fields = Vec::new();
792        for item in &s.items {
793            match item {
794                Item::All(_) => {
795                    return Err(SqlError::unsupported(
796                        "`select *` with a `group by` would name every column of every row, and a \
797                         group is not a row: name the grouped columns and the aggregates",
798                    ))
799                }
800                // See `project`: an expression's place in a grouped query is inside the key the
801                // operator builds, and this evaluates expressions over the rows one produced.
802                Item::Expr(_, alias) => {
803                    return Err(SqlError::unsupported(format!(
804                        "`{}` is an expression, and a `group by` groups by columns: an expression \
805                         here would have to be part of the key the operator builds",
806                        alias.as_deref().unwrap_or("an expression")
807                    )))
808                }
809                Item::Column(name, alias) => {
810                    let i = self.resolve(name)?;
811                    let at = keys.iter().position(|&k| k == i).ok_or_else(|| SqlError {
812                        message: format!(
813                            "\"{name}\" is not in the `group by`, so a group has more than one of \
814                             it: put it in the `group by`, or ask an aggregate for it"
815                        ),
816                        code: "42803",
817                    })?;
818                    let mut f = self.fields[i].clone();
819                    if let Some(a) = alias {
820                        f.column.name = Arc::from(a.as_str());
821                        f.of = None;
822                    }
823                    let value = match keys.len() {
824                        1 => var(element),
825                        _ => field(var(element), &format!("k{at}")),
826                    };
827                    push(&mut out, &mut fields, f, value);
828                }
829                Item::Literal(d, alias) => {
830                    let f = literal_field(d, alias.as_deref());
831                    push(&mut out, &mut fields, f, constant(d));
832                }
833                Item::Count(alias) => {
834                    let f = Field {
835                        column: Column {
836                            name: Arc::from(alias.as_deref().unwrap_or("count")),
837                            ty: SqlTy::Bigint,
838                            nullable: false,
839                        },
840                        of: None,
841                    };
842                    let over = match ungrouped {
843                        true => var(rows),
844                        false => self.group_filter(rows, element, &keys),
845                    };
846                    push(&mut out, &mut fields, f, prim(Prim::ListLen, vec![over]));
847                }
848                Item::Aggregate(agg, name, alias) => {
849                    let i = self.resolve(name)?;
850                    let source = self.fields[i].clone();
851                    if source.column.nullable {
852                        return Err(SqlError::unsupported(format!(
853                            "`{}({name})` is not answered here because \"{name}\" is an `Option` \
854                             and SQL's aggregates skip nulls: this one is a function of what every \
855                             row contributes, so a row contributing nothing has no answer",
856                            agg.name()
857                        )));
858                    }
859                    if *agg == Agg::Sum && source.column.ty != SqlTy::Bigint {
860                        return Err(SqlError::unsupported(format!(
861                            "`sum({name})` is not answered here because \"{name}\" is {} and a \
862                             total over it would depend on the order it was added in — Beck's \
863                             `list_sum` is exact over `Int` and has no `Float` form at all \
864                             (docs/46 §46.16)",
865                            source.column.ty.name()
866                        )));
867                    }
868                    let f = Field {
869                        column: Column {
870                            name: Arc::from(
871                                alias.clone().unwrap_or_else(|| agg.name().to_string()),
872                            ),
873                            ty: source.column.ty,
874                            nullable: *agg != Agg::Sum,
875                        },
876                        of: None,
877                    };
878                    let over = match ungrouped {
879                        true => var(rows),
880                        false => self.group_filter(rows, element, &keys),
881                    };
882                    let row = self.fresh();
883                    let projected = prim(
884                        Prim::MapList,
885                        vec![over, lam(vec![row], field(var(row), &field_name(i)))],
886                    );
887                    let op = match agg {
888                        Agg::Min => Prim::ListMin,
889                        Agg::Max => Prim::ListMax,
890                        Agg::Sum => Prim::ListSum,
891                    };
892                    push(&mut out, &mut fields, f, prim(op, vec![projected]));
893                }
894            }
895        }
896
897        if ungrouped {
898            return Ok((make(out), fields));
899        }
900        let key_param = self.fresh();
901        let distinct = prim(
902            Prim::ListUnique,
903            vec![prim(
904                Prim::MapList,
905                vec![var(rows), lam(vec![key_param], group_key(&keys, key_param))],
906            )],
907        );
908        Ok((
909            prim(Prim::MapList, vec![distinct, lam(vec![element], make(out))]),
910            fields,
911        ))
912    }
913
914    /// `filter_list(R, λr. g(r) == k)` — the group, as the expression an aggregate asks about.
915    ///
916    /// Never evaluated as written: [`crate::relate`] reads it as the probe of an index keyed by
917    /// `g`, and what the operator above it asks for decides whether a group is built at all. A
918    /// count is answered from the join's tally and an extreme from
919    /// [`crate::plan::Op::GroupBy`]'s multiset, so this expression is the *question* rather than
920    /// the work.
921    fn group_filter(&mut self, rows: VarId, element: VarId, keys: &[usize]) -> Core {
922        let row = self.fresh();
923        let predicate = prim(Prim::Eq, vec![group_key(keys, row), var(element)]);
924        prim(Prim::FilterList, vec![var(rows), lam(vec![row], predicate)])
925    }
926}
927
928/// The value a group's key has, as a function of one row: the column itself when a query groups by
929/// one, and a record of them when it groups by several — which is a key a `BTreeMap` orders exactly
930/// as `==` compares it, so the index and the equality agree by construction.
931fn group_key(keys: &[usize], v: VarId) -> Core {
932    match keys {
933        [one] => field(var(v), &field_name(*one)),
934        many => make(
935            many.iter()
936                .enumerate()
937                .map(|(j, &i)| (Arc::from(format!("k{j}")), field(var(v), &field_name(i))))
938                .collect(),
939        ),
940    }
941}
942
943fn push(out: &mut Row, fields: &mut Vec<Field>, f: Field, value: Core) {
944    out.push((Arc::from(field_name(fields.len())), value));
945    fields.push(f);
946}
947
948fn literal_field(d: &Datum, alias: Option<&str>) -> Field {
949    Field {
950        column: Column {
951            name: Arc::from(alias.unwrap_or("?column?")),
952            ty: d.ty(),
953            nullable: false,
954        },
955        of: None,
956    }
957}
958
959// -------------------------------------------------------------------------------------------
960// Small `Core` constructors
961// -------------------------------------------------------------------------------------------
962//
963// Types are `unit` throughout and that is not laziness: a plan runs after the checker, nothing
964// downstream reads a type off these nodes, and giving them invented ones would be a second, wrong
965// answer to a question this expression was never asked.
966
967fn node(kind: CoreKind) -> Core {
968    Core {
969        kind,
970        ty: Ty::unit(),
971        tier: Tier::Any,
972        span: Span::NONE,
973        last_use: false,
974        order: crate::fields::UNORDERED,
975        locals: 0,
976    }
977}
978
979fn var(v: VarId) -> Core {
980    node(CoreKind::Var(v))
981}
982
983fn field(base: Core, name: &str) -> Core {
984    node(CoreKind::Field {
985        base: Box::new(base),
986        name: Arc::from(name),
987    })
988}
989
990fn prim(op: Prim, args: Vec<Core>) -> Core {
991    node(CoreKind::Prim { op, args })
992}
993
994fn make(fields: Row) -> Core {
995    node(CoreKind::Make {
996        ty: Arc::from("Row"),
997        variant: None,
998        fields,
999    })
1000}
1001
1002fn lam(params: Vec<VarId>, body: Core) -> Core {
1003    node(CoreKind::Lam {
1004        params: params.into(),
1005        body: Arc::new(body),
1006    })
1007}
1008
1009fn bind(v: VarId, value: Core, body: Core) -> Core {
1010    node(CoreKind::Let {
1011        var: v,
1012        value: Box::new(value),
1013        body: Box::new(body),
1014    })
1015}
1016
1017fn constant(d: &Datum) -> Core {
1018    node(CoreKind::Const(match d {
1019        Datum::Boolean(b) => Const::Bool(*b),
1020        Datum::Bigint(i) => Const::Int(*i),
1021        Datum::Double(f) => Const::Float(*f),
1022        Datum::Text(s) => Const::Str(Arc::from(s.as_str())),
1023    }))
1024}