Skip to content

fix(sql): accept the OUTER keyword in LEFT/RIGHT/FULL OUTER JOIN#96

Merged
fupelaqu merged 3 commits into
mainfrom
feature/licensingInfrastructure
Jun 1, 2026
Merged

fix(sql): accept the OUTER keyword in LEFT/RIGHT/FULL OUTER JOIN#96
fupelaqu merged 3 commits into
mainfrom
feature/licensingInfrastructure

Conversation

@fupelaqu
Copy link
Copy Markdown
Contributor

@fupelaqu fupelaqu commented Jun 1, 2026

ANSI SQL allows the optional OUTER keyword after LEFT / RIGHT / FULL
LEFT OUTER JOIN and LEFT JOIN are the same. The previous regexes
on each JoinType (Expr("LEFT") etc.) matched only the bare keyword,
so the standard FULL OUTER JOIN / LEFT OUTER JOIN /
RIGHT OUTER JOIN forms failed to parse, breaking any BI tool or ORM
that emits the explicit OUTER syntax.

Fix at the model level: each of LeftJoin, RightJoin, FullJoin
now overrides words to list the longer <keyword>\s+OUTER form
first and the bare keyword second. TokenRegex.regex builds
(?i)(LEFT\s+OUTER|LEFT)\b, and regex alternation prefers the
longest-first match — so both surface forms parse into the same
AST node.

This is intentionally a model-side fix rather than a parser-side fix
so the regex is reused everywhere LeftJoin.regex / RightJoin.regex
/ FullJoin.regex is referenced. The sql value used for AST
round-trip rendering stays on the canonical short form, so
Update.sql / Join.sql keep emitting LEFT JOIN rather than
LEFT OUTER JOIN.

Closes #94.

fupelaqu added 3 commits May 31, 2026 08:04
Closes #94.

ANSI SQL allows the optional `OUTER` keyword after LEFT / RIGHT / FULL
— `LEFT OUTER JOIN` and `LEFT JOIN` are the same. The previous regexes
on each JoinType (`Expr("LEFT")` etc.) matched only the bare keyword,
so the standard `FULL OUTER JOIN` / `LEFT OUTER JOIN` /
`RIGHT OUTER JOIN` forms failed to parse, breaking any BI tool or ORM
that emits the explicit OUTER syntax.

Fix at the model level: each of `LeftJoin`, `RightJoin`, `FullJoin`
now overrides `words` to list the longer `<keyword>\s+OUTER` form
first and the bare keyword second. `TokenRegex.regex` builds
`(?i)(LEFT\s+OUTER|LEFT)\b`, and regex alternation prefers the
longest-first match — so both surface forms parse into the same
AST node.

This is intentionally a model-side fix rather than a parser-side fix
so the regex is reused everywhere `LeftJoin.regex` / `RightJoin.regex`
/ `FullJoin.regex` is referenced. The `sql` value used for AST
round-trip rendering stays on the canonical short form, so
`Update.sql` / `Join.sql` keep emitting `LEFT JOIN` rather than
`LEFT OUTER JOIN`.

Three new ParserSpec tests assert that the short and OUTER forms
produce identical AST nodes (`LeftJoin`, `RightJoin`, `FullJoin`).
Full ParserSpec — 192 tests — still green. `+ sql/compile` clean
under 2.12 and 2.13.
…ialized views

Closes softclient4es-extensions#19's user-facing documentation gap.

The MV engine now rejects RIGHT JOIN and FULL OUTER JOIN with an
actionable error message; this commit documents the constraint so
users hit it once, in the docs, instead of at CREATE MATERIALIZED
VIEW time.

Both copies are updated to stay in sync:
  - documentation/sql/materialized_views.md (this repo)
  - softclient4es-web/src/content/docs/sql/materialized-views.mdx
    (published site)

Each gets:
  - A new row in the Limitations table flagging RIGHT JOIN and
    FULL OUTER JOIN as unsupported.
  - A new "Supported JOIN types" subsection explaining the underlying
    architectural reason — the ingest pipeline is driven by writes to
    the main (left-hand) FROM table and can only enrich into the
    main-table document via EnrichProcessor — and the workaround
    (rewrite RIGHT JOIN with swapped table order; FULL OUTER JOIN is
    simply not expressible in this model).
@fupelaqu fupelaqu marked this pull request as ready for review June 1, 2026 07:04
@fupelaqu fupelaqu merged commit 194dd19 into main Jun 1, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parser: accept the OUTER keyword in LEFT/RIGHT/FULL OUTER JOIN

1 participant