One JOIN clause from the FROM list. Multi-join queries
(A JOIN B ... JOIN C ...) become a Vec<JoinClause> evaluated
left-to-right against the accumulator. The match condition is one
of ON / USING / NATURAL (see JoinConstraintKind);
CROSS JOIN arrives here already rewritten to ON true.
A parsed ORDER BY clause: a single sort key (expression), ascending
by default. Phase 7b widened this from “bare column name” to
“arbitrary expression” so KNN queries of the form
ORDER BY vec_distance_l2(col, [...]) LIMIT k work end-to-end. The
expression is evaluated per-row at execution time via eval_expr;
the simple ORDER BY col form still works because that’s just an
Expr::Identifier taking the same path.
How a JOIN matches rows. SQLR-5 originally shipped ON only; the
USING / NATURAL increment adds the two name-based constraints.
ON carries its predicate straight from the parser. USING and
NATURAL defer their equality synthesis to the executor because
they need table schemas (which column names exist, and — for
NATURAL — which are shared) that the parser doesn’t have. The
executor turns both into the same left.col = right.col [AND …]
predicate the ON path already evaluates. CROSS JOIN is rewritten
to ON true at parse time (no schema needed) and so reuses the
On variant directly.
SQLR-5 — flavor of join. SQLite ships INNER and LEFT OUTER; we
implement the full quartet on top of a single nested-loop driver
because the per-flavor differences are small (NULL-padding policy
for unmatched left/right rows). RIGHT OUTER and FULL OUTER aren’t
in SQLite — see docs/design-decisions.md for the rationale.