Skip to content

Commit aa846cf

Browse files
authored
feat(vnext): add dialect-aware statement index (#174)
## Summary - add an internal synchronous parser-free statement partition and binary-search cursor lookup - preserve explicit half-open ownership for trivia, terminators, empty slots, EOF, and left/right affinity - add bounded PostgreSQL, DuckDB, BigQuery, and Dremio lexical profiles with opaque fail-closed suffixes - keep incremental reuse, session attachment, parser semantics, and public APIs out of this slice ## Safety and performance - cap materialized slots at 10,000 and collapse the remainder into one opaque slot - bound dollar delimiters and retain no statement substrings - conservatively handle Unicode dollar tags and prefix boundaries - fail closed for BigQuery procedural bodies, custom delimiters, and PostgreSQL BEGIN ATOMIC bodies - 16 MiB scans under a 128 MiB heap: about 44 ms ASCII, 61 ms Latin, 47 ms astral ## Evidence - exact head: 56b57ea - focused statement-index tests: 82 passed - full suite: 795 passed, 1 governed expected failure - changed coverage: 98.76% statements, 96.97% branches, 100% functions, 98.75% lines - strict typecheck, non-mutating lint, test integrity, demo build, packed-package smoke, and Chromium passed - lexical/correctness reviewer: APPROVE exact head, no blocker/high/medium findings - performance/API reviewer: APPROVE exact head, no blocker/high/medium findings ## Public API No vNext public exports or generated root declarations are added. The full scanner remains the correctness oracle for a later independently reviewed incremental slice. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds an internal, dialect-aware SQL statement index for vNext. It partitions `analysisText` into exact half-open slots with binary-search cursor lookup and bounded resources, with no public API changes. - **New Features** - Exact slot partitioning and ownership: terminators associate left; trailing trivia moves to the next slot; explicit empty slots are preserved. - Dialect profiles for PostgreSQL, DuckDB, BigQuery, and Dremio; protects strings/comments (e.g., dollar-quoting, E-strings, triple/raw/backtick, nested comments) and fails closed for custom delimiters, BigQuery procedural blocks, and PostgreSQL BEGIN ATOMIC. - Left/right-affinity lookup via binary search with explicit EOF behavior for terminated vs open statements. - Bounded resources: up to 10,000 materialized slots; bounded dollar-quote tag length; no statement substring copies; frozen partition data. - Reports unterminated strings/comments without exposing internal semicolons; operates on `analysisText` so UTF-16 offsets match the original text under the current masking transform; dialect behavior is keyed by internal profile identity, not a caller-controlled ID. <sup>Written for commit 2ca3c25. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/marimo-team/codemirror-sql/pull/174?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. -->
1 parent 359cc18 commit aa846cf

5 files changed

Lines changed: 1767 additions & 0 deletions

File tree

docs/adr/0001-language-service-and-session.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,20 @@ document offsets and statement-relative offsets use distinct branded types.
202202
Statement lookup uses explicit cursor affinity at boundaries and EOF. It must
203203
not reintroduce the v0.x inclusive-end behavior.
204204

205+
The first statement-index implementation is an internal synchronous full-scan
206+
oracle. Exact slot extents partition the analysis text, terminators associate
207+
left, and trivia after a terminator associates with the following slot. Empty
208+
documents and terminal delimiters retain explicit empty slots. Lookup requires
209+
left or right affinity and uses binary search.
210+
211+
The index does not contain statement text, line numbers, inferred kinds, parser
212+
validity, or AST data. It materializes at most 10,000 slots and collapses an
213+
unscanned remainder into an opaque slot on resource limits or unsupported
214+
delimiter/procedural constructs. Opaque slots cannot be passed to later parsing
215+
as exact source. Dialect lexical behavior uses internal configuration identity,
216+
never a caller-controlled dialect ID. Incremental reuse and session attachment
217+
remain a separate change tested against the full-scan oracle.
218+
205219
## Request outcomes
206220

207221
Every session feature request settles with the same top-level union:

docs/vnext/source-coordinates.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,7 @@ functions remain internal. This slice intentionally does not publish a source
4444
transformer or source-map SPI. Generated or reordered source requires a
4545
versioned segment-map design and evidence from real consumers before becoming
4646
public.
47+
48+
The internal [statement index](./statement-index.md) scans `analysisText` and
49+
uses its length-preserving offsets without publishing analysis-coordinate
50+
ranges.

docs/vnext/statement-index.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# vNext Statement Index
2+
3+
Status: internal full-scan correctness oracle
4+
5+
The statement index is a synchronous, parser-free partition of
6+
`analysisText`. It does not classify, parse, validate, or copy statements, and
7+
it has no public `/vnext` export yet.
8+
9+
Each exact slot contains:
10+
11+
- An `extent` that participates in a contiguous partition of the complete text.
12+
- A `source` range that includes leading and trailing trivia but excludes the
13+
terminator.
14+
- An optional one-code-unit `terminator`, owned by the slot on its left.
15+
- `hasCode`, which distinguishes code from whitespace/comment-only slots.
16+
- A lexical end state for incomplete quoted strings or block comments.
17+
18+
An empty document has one empty slot. A document ending exactly in a semicolon
19+
has an explicit trailing empty slot; trailing trivia instead forms the final
20+
trivia-only slot. Consecutive semicolons retain their empty slots.
21+
22+
The scanner returns an opaque suffix instead of guessed boundaries when it
23+
encounters an unsupported custom delimiter, an unsupported BigQuery procedural
24+
body, or a resource limit. Opaque slots omit `source`, `terminator`, and
25+
`hasCode`, so later layers cannot accidentally parse them as exact statements.
26+
At most 10,000 slots are materialized; a semicolon-dense remainder collapses
27+
into one opaque slot.
28+
29+
## Cursor affinity
30+
31+
Point lookup always requires `left` or `right` affinity. At a shared extent
32+
boundary, left selects the preceding slot and right selects the following slot.
33+
At position zero both select the first slot. At EOF after a terminator, left
34+
selects the terminated slot and right selects the explicit trailing empty slot.
35+
At EOF in an unterminated final statement, both select that statement.
36+
37+
The lookup uses binary search. It deliberately does not implement a hidden
38+
"nearest code statement" fallback; completion, hover, gutter, and future
39+
run-current-statement commands need different policies.
40+
41+
## Dialect profiles
42+
43+
Lexical behavior is carried by immutable internal profile identity. It is never
44+
inferred from a caller-controlled dialect ID. This first oracle owns profiles
45+
for:
46+
47+
- PostgreSQL: doubled quotes, `E'...'`, dollar-quoted strings, and nested block
48+
comments, following the
49+
[PostgreSQL lexical contract](https://www.postgresql.org/docs/current/sql-syntax-lexical.html).
50+
Dollar-tag and literal-prefix boundaries conservatively treat every
51+
non-ASCII code point as identifier-like, covering the engines' permissive
52+
Unicode behavior without exposing internal semicolons. SQL routine bodies
53+
introduced by `BEGIN ATOMIC` are opaque in this slice.
54+
- DuckDB: doubled quotes, escape strings, tagged dollar-quoted strings, and
55+
nested comments. Dollar quoting follows
56+
[DuckDB literal types](https://duckdb.org/docs/current/sql/data_types/literal_types).
57+
- BigQuery: single, double, triple, raw, bytes, and raw-bytes strings; backtick
58+
identifiers; `#` comments; and non-nesting block comments, following the
59+
[GoogleSQL lexical contract](https://docs.cloud.google.com/bigquery/docs/reference/standard-sql/lexical).
60+
Procedural bodies, including labeled loops, are opaque in this slice.
61+
- Dremio: a compatibility profile limited to verified single-quoted strings,
62+
double-quoted identifiers, and standard comments. It does not silently
63+
inherit PostgreSQL extensions.
64+
65+
Unterminated lexical constructs consume the remainder and report their opening
66+
offset. Regular BigQuery strings also fail closed at a line break, because only
67+
triple-quoted strings may span lines.
68+
69+
## Complexity and sequencing
70+
71+
A full build is linear in UTF-16 code units, retains only slot records and a
72+
bounded dollar delimiter, and creates no statement substrings. Point lookup is
73+
logarithmic in slot count. The scanner operates on `analysisText`; the current
74+
length-preserving source transform makes its analysis ranges valid at the same
75+
offsets in `originalText`.
76+
77+
This implementation remains the correctness oracle. Incremental rescanning,
78+
change mapping, cache reuse, and session attachment belong to a later slice and
79+
must be tested against a fresh full build after arbitrary edits.

0 commit comments

Comments
 (0)