Expand description
Pure, conservative scanner over SQL text: the :name placeholders a
query uses and the tables its FROM/JOIN clauses read, each located by
line, column, and Unicode-character offset/length (#841, #842).
This module never executes or fully parses the SQL — it walks the token
stream sqlparser::tokenizer::Tokenizer::tokenize_with_location
produces and applies a handful of local, position-based rules. That
makes it deliberately less capable than a real parser, in exchange for
never mis-locating (or mis-naming) anything it does report: every rule
below is written to favor a false negative (missing a table or
placeholder) over a false positive.
§What scan_sql finds
- Placeholders: a
Token::Colonimmediately followed — no whitespace, per token-span adjacency — by an unquotedToken::Word.::tokenizes as its ownToken::DoubleColon, so a cast never matches. Text inside a string literal or a comment is already one token to the tokenizer, so nothing inside either is ever inspected. Duplicate names (compared with the case SQLite keeps: exact) keep only their first occurrence. - Tables: an identifier (bare or double-quoted) is reported when the
token immediately before it is the keyword
FROM, the keywordJOIN(LEFT/INNER/CROSS/… all end in the literalJOINtoken this checks), or a comma inside aFROMlist that is still open at the current parenthesis depth. An identifier immediately followed by((a function call, e.g.json_each(x)) or by.(a qualifiedschema.tablereference — neither part is reported) is excluded. Names declared by aWITH [RECURSIVE] name AS (...)clause are collected first and never reported as a table reference, wherever they are used — but any table the CTE’s own body reads is still reported, since the body is scanned like any other subquery. Duplicate names (compared case-insensitively, like SQLite compares table names) keep only their first occurrence’s spelling and position.
§What it deliberately does not detect
- An alias is never reported and never influences whether the table it
names is reported — this module has no notion of “alias”, only of
“the token after a table reference wasn’t
(or.”. - A table named only inside a derived table’s
(...)—FROM (SELECT * FROM t) subreportst(found by the ordinaryFROMrule while scanning inside the parentheses) but neversub. VALUES, table-valued functions, and any otherFROMitem that is not a bare or quoted identifier are silently skipped, not reported as errors.- A three-or-more-part qualified name (
catalog.schema.table) is excluded the same way a two-part one is; neither segment is reported. - Anything the tokenizer itself cannot lex (see
ScanError) — the caller is expected to let SQL parsing report that error instead.
Structs§
- Placeholder
- A
:nameplaceholder the SQL uses, located at the:. - Scan
Result - The result of scanning one SQL statement: every placeholder and table
reference
scan_sqlfound, each in first-occurrence order. - Source
Position - A location inside the scanned SQL text, in two coordinate systems at
once: 1-based
line/column(matching theLine: N, Column: Mmarkersqlparser’s own parse errors already use, so a caller that extracts that marker from one keeps working for the other) and a 0-based Unicode-charoffset/lengthpair from the start of the text — what a browser text editor (CodeMirror) indexes with, since it counts code points, not UTF-8 bytes. - Table
Ref - A table the SQL reads, located at its identifier (quotes included, when quoted).
Enums§
- Scan
Error - A failure to tokenize the SQL text at all. Per this module’s contract (#841), a caller receiving this should skip linting and let the SQL engine’s own parser report the error instead — this scanner never tries to diagnose why the SQL is malformed.
Functions§
- scan_
sql - Scans
sqlfor:nameplaceholders and the tables itsFROM/JOINclauses read, without executing or fully parsing it. See the module docs for the exact rules and their limits. - undeclared_
tables - Returns the tables
resultfound that are not present indeclared_labels(compared case-insensitively, like SQLite compares table names), in the order they first appeared in the SQL.