Skip to main content

Module scan

Module scan 

Source
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::Colon immediately followed — no whitespace, per token-span adjacency — by an unquoted Token::Word. :: tokenizes as its own Token::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 keyword JOIN (LEFT/INNER/CROSS/… all end in the literal JOIN token this checks), or a comma inside a FROM list 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 qualified schema.table reference — neither part is reported) is excluded. Names declared by a WITH [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) sub reports t (found by the ordinary FROM rule while scanning inside the parentheses) but never sub.
  • VALUES, table-valued functions, and any other FROM item 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 :name placeholder the SQL uses, located at the :.
ScanResult
The result of scanning one SQL statement: every placeholder and table reference scan_sql found, each in first-occurrence order.
SourcePosition
A location inside the scanned SQL text, in two coordinate systems at once: 1-based line/column (matching the Line: N, Column: M marker sqlparser’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-char offset/length pair from the start of the text — what a browser text editor (CodeMirror) indexes with, since it counts code points, not UTF-8 bytes.
TableRef
A table the SQL reads, located at its identifier (quotes included, when quoted).

Enums§

ScanError
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 sql for :name placeholders and the tables its FROM/JOIN clauses read, without executing or fully parsing it. See the module docs for the exact rules and their limits.
undeclared_tables
Returns the tables result found that are not present in declared_labels (compared case-insensitively, like SQLite compares table names), in the order they first appeared in the SQL.