sqllens 0.1.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +409 -255
- package/dist/api.d.ts +9 -7
- package/dist/api.js +15 -6
- package/dist/bigquery/parse.d.ts +5 -18
- package/dist/bigquery/parse.js +1 -1
- package/dist/completion/complete.d.ts +3 -1
- package/dist/completion/complete.js +4 -2
- package/dist/completion/config.d.ts +1 -1
- package/dist/completion/parser-factory.d.ts +1 -1
- package/dist/databricks/parse.d.ts +4 -17
- package/dist/derived-dialects.d.ts +8 -0
- package/dist/derived-dialects.js +50 -0
- package/dist/dialect-symbols.d.ts +1 -1
- package/dist/dialect.d.ts +3 -0
- package/dist/dialect.js +1 -0
- package/dist/document/document.d.ts +159 -5
- package/dist/document/document.js +473 -18
- package/dist/document/node-at.js +2 -132
- package/dist/document/shift.d.ts +6 -0
- package/dist/document/shift.js +11 -0
- package/dist/document/split.d.ts +1 -1
- package/dist/duckdb/parse.d.ts +4 -16
- package/dist/index.d.ts +12 -7
- package/dist/index.js +15 -10
- package/dist/ir/ir.d.ts +19 -1
- package/dist/ir/part-span.d.ts +11 -0
- package/dist/ir/part-span.js +35 -1
- package/dist/ir/walk.d.ts +9 -0
- package/dist/ir/walk.js +135 -0
- package/dist/lineage/lineage.d.ts +4 -1
- package/dist/lineage/lineage.js +5 -1
- package/dist/minijinja/apply-tags.d.ts +12 -3
- package/dist/minijinja/apply-tags.js +30 -16
- package/dist/minijinja/engine.d.ts +4 -0
- package/dist/minijinja/engine.js +11 -0
- package/dist/minijinja/index.d.ts +8 -0
- package/dist/minijinja/index.js +8 -0
- package/dist/minijinja/parse.d.ts +3 -44
- package/dist/minijinja/parse.js +71 -11
- package/dist/minijinja/tag-ast.js +16 -2
- package/dist/minijinja/variants.d.ts +10 -2
- package/dist/minijinja/variants.js +43 -6
- package/dist/parse-result.d.ts +11 -0
- package/dist/parse-result.js +1 -0
- package/dist/postgres/parse.d.ts +4 -16
- package/dist/qualify/qualify.d.ts +8 -1
- package/dist/qualify/qualify.js +21 -5
- package/dist/qualify/template-provider.d.ts +2 -19
- package/dist/redshift/parse.d.ts +4 -16
- package/dist/references/references.js +2 -0
- package/dist/scope/walk.d.ts +15 -0
- package/dist/scope/walk.js +73 -0
- package/dist/session.d.ts +83 -0
- package/dist/session.js +159 -0
- package/dist/signature/signatures.d.ts +1 -1
- package/dist/snowflake/parse.d.ts +4 -16
- package/dist/symbols/symbols.d.ts +14 -0
- package/dist/symbols/symbols.js +115 -3
- package/dist/template/engine.d.ts +65 -0
- package/dist/template/engine.js +1 -0
- package/dist/token/classify.d.ts +1 -1
- package/dist/token/map.d.ts +1 -1
- package/dist/token/tokenize.d.ts +1 -1
- package/dist/trino/parse.d.ts +4 -15
- package/dist/tsql/parse.d.ts +4 -16
- package/package.json +95 -91
- package/dist/adapters.d.ts +0 -8
- package/dist/adapters.js +0 -43
package/dist/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ParserRuleContext } from "antlr4ng";
|
|
2
|
-
import type { Expr, QueryExpr } from "./ir/ir.js";
|
|
2
|
+
import type { Expr, Projection, QueryExpr } from "./ir/ir.js";
|
|
3
3
|
import type { SyntaxDiagnostic } from "./parse-diagnostics.js";
|
|
4
4
|
import { type Scope, type ScopeTree } from "./scope/scope.js";
|
|
5
5
|
import { type Qualification } from "./qualify/qualify.js";
|
|
@@ -10,9 +10,8 @@ export type { Nullability } from "./infer/nullability.js";
|
|
|
10
10
|
import { originsOf as exprOriginsOf, type ColumnLineage, type Origin } from "./lineage/lineage.js";
|
|
11
11
|
import { type StarExpansion, type Sym } from "./symbols/symbols.js";
|
|
12
12
|
import type { Token } from "./token/token.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export type Dialect = "databricks" | "tsql" | "snowflake" | "bigquery" | "redshift" | "postgres" | "duckdb" | "trino";
|
|
13
|
+
import type { Dialect } from "./dialect.js";
|
|
14
|
+
export type { Dialect } from "./dialect.js";
|
|
16
15
|
/** Options carrying the dialect, needed only when a lift helper enters from a raw string. */
|
|
17
16
|
export interface DialectOpts {
|
|
18
17
|
dialect?: Dialect;
|
|
@@ -103,22 +102,25 @@ export declare class Lineage {
|
|
|
103
102
|
/** Per output column: the base-table columns it derives from. */
|
|
104
103
|
readonly all: readonly ColumnLineage[];
|
|
105
104
|
private readonly byOutput;
|
|
105
|
+
private readonly byNode;
|
|
106
106
|
constructor(columns: ColumnLineage[]);
|
|
107
107
|
/** The base-table origins of a named output column, or [] if there is no such output. */
|
|
108
108
|
originsOf(column: string): Origin[];
|
|
109
|
+
/** Origins keyed by the producing Projection node — unambiguous under duplicate output names. */
|
|
110
|
+
originsOfNode(projection: Projection): Origin[];
|
|
109
111
|
}
|
|
110
112
|
export { exprOriginsOf as originsOfExpr };
|
|
111
113
|
export { lineageAt, lineageOf, type LineageHop, type ViaStep } from "./lineage/hops.js";
|
|
112
114
|
export { tokenize } from "./token/tokenize.js";
|
|
113
115
|
export type { Token, TokenRole } from "./token/token.js";
|
|
114
|
-
export { SqlDocument, type DocumentAnalysis, type StatementCell } from "./document/document.js";
|
|
116
|
+
export { SqlDocument, type DocumentAnalysis, type StatementCell, type DocumentVariant, type UnionCte, } from "./document/document.js";
|
|
115
117
|
export { LineIndex } from "./document/line-index.js";
|
|
116
118
|
export type { StatementCellSpan } from "./document/split.js";
|
|
117
|
-
export { complete, type Completion } from "./completion/complete.js";
|
|
119
|
+
export { complete, completeAt, type Completion } from "./completion/complete.js";
|
|
118
120
|
export { signatureAt, type SignatureInfo } from "./signature/signature.js";
|
|
119
121
|
export { FUNCTION_SIGNATURES, HARVESTED_SIGNATURES, lookupSignature, hasSignature, type FnSignature, type ParamSig, } from "./signature/signatures.js";
|
|
120
122
|
export { referencesAt, type Occurrence, type Occurrences } from "./references/references.js";
|
|
121
123
|
export { dialectSymbols, type DialectSymbols } from "./dialect-symbols.js";
|
|
122
124
|
export { CallbackSchema, type SchemaProvider, type TableResolver } from "./qualify/schema-provider.js";
|
|
123
|
-
export {
|
|
125
|
+
export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
|
|
124
126
|
export { foldIdentifier, displayName, type IdentKind } from "./ident/fold.js";
|
package/dist/api.js
CHANGED
|
@@ -157,16 +157,25 @@ export class Lineage {
|
|
|
157
157
|
/** Per output column: the base-table columns it derives from. */
|
|
158
158
|
all;
|
|
159
159
|
byOutput;
|
|
160
|
+
byNode;
|
|
160
161
|
constructor(columns) {
|
|
161
162
|
this.all = columns;
|
|
162
163
|
this.byOutput = new Map();
|
|
163
|
-
|
|
164
|
+
this.byNode = new WeakMap();
|
|
165
|
+
for (const c of columns) {
|
|
164
166
|
this.byOutput.set(c.output, c.origins);
|
|
167
|
+
if (c.projection)
|
|
168
|
+
this.byNode.set(c.projection, c.origins);
|
|
169
|
+
}
|
|
165
170
|
}
|
|
166
171
|
/** The base-table origins of a named output column, or [] if there is no such output. */
|
|
167
172
|
originsOf(column) {
|
|
168
173
|
return this.byOutput.get(column) ?? [];
|
|
169
174
|
}
|
|
175
|
+
/** Origins keyed by the producing Projection node — unambiguous under duplicate output names. */
|
|
176
|
+
originsOfNode(projection) {
|
|
177
|
+
return this.byNode.get(projection) ?? [];
|
|
178
|
+
}
|
|
170
179
|
}
|
|
171
180
|
// Re-export the single-expression origin walk under its building-block name (distinct from the
|
|
172
181
|
// Lineage wrapper) so consumers can trace one expression without a full query lineage.
|
|
@@ -185,11 +194,11 @@ export { tokenize } from "./token/tokenize.js";
|
|
|
185
194
|
// It composes the surface above (parse/toScopes/qualify/deriveSymbols/TypeInfo); the import cycle
|
|
186
195
|
// (api re-exports SqlDocument, document imports from api) is safe because document.ts only calls
|
|
187
196
|
// these at call time, never at module-eval time.
|
|
188
|
-
export { SqlDocument } from "./document/document.js";
|
|
197
|
+
export { SqlDocument, } from "./document/document.js";
|
|
189
198
|
export { LineIndex } from "./document/line-index.js";
|
|
190
199
|
// Scope-aware completion over a SqlDocument — the broken-input editor feature (keywords + schema
|
|
191
200
|
// tables/columns + function names at the caret). Total: never throws.
|
|
192
|
-
export { complete } from "./completion/complete.js";
|
|
201
|
+
export { complete, completeAt } from "./completion/complete.js";
|
|
193
202
|
// Signature help over a SqlDocument — the broken-input editor feature that shows parameter hints
|
|
194
203
|
// while typing inside a call's parens. Lookup order: curated (hand-verified) → harvested (doc-derived
|
|
195
204
|
// long tail, from tools/harvest-signatures.mjs) → name-only fallback. A pure token scan; never throws.
|
|
@@ -207,9 +216,9 @@ export { dialectSymbols } from "./dialect-symbols.js";
|
|
|
207
216
|
// lazy resolver whose prime() bumps a version to invalidate SqlDocument.analyze's memo) both
|
|
208
217
|
// satisfy `SchemaProvider`; every analysis entry point accepts the interface.
|
|
209
218
|
export { CallbackSchema } from "./qualify/schema-provider.js";
|
|
210
|
-
// The
|
|
211
|
-
//
|
|
212
|
-
export {
|
|
219
|
+
// The derived-dialect → dialect map: resolve an engine name (athena, glue, fabric, spark, …) to the
|
|
220
|
+
// dialect that parses its SQL, so consumers don't re-derive the family knowledge.
|
|
221
|
+
export { DERIVED_DIALECTS, resolveDialect } from "./derived-dialects.js";
|
|
213
222
|
// The dialect-true identifier fold — the identity key for name comparison (unquote + case-fold per
|
|
214
223
|
// the dialect's documented rules) and its display twin (unquote only). Exported so an embedding
|
|
215
224
|
// consumer comparing names against IR/scope output folds the same way the pipeline does.
|
package/dist/bigquery/parse.d.ts
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
tree: ParserRuleContext;
|
|
7
|
-
/** Count of lexer + parser + escape + post-parse syntax errors; equals `diagnostics.length`. */
|
|
8
|
-
errors: number;
|
|
9
|
-
/** Positioned syntax diagnostics — lexer/parser (listener-captured) plus escape and post-parse
|
|
10
|
-
* errors, all carrying a source span. `errors === diagnostics.length`. */
|
|
11
|
-
diagnostics: SyntaxDiagnostic[];
|
|
12
|
-
/** Every token the parser consumed (trivia included, EOF excluded), as neutral `Token`s. Built
|
|
13
|
-
* from the dot-path-rewritten stream, so spans match the consumed source. */
|
|
14
|
-
tokens: Token[];
|
|
15
|
-
/** True when the fast SLL prediction pass bailed and the parse re-ran under full LL. Same result
|
|
16
|
-
* either way — this just says which path produced it, for perf profiling (tools/profile-sll.ts). */
|
|
17
|
-
sllFallback: boolean;
|
|
18
|
-
}
|
|
1
|
+
import type { ParseResult } from "../parse-result.js";
|
|
2
|
+
/** The CST rooted at `root` (`stmts EOF`); errors count lexer + parser + escape + post-parse
|
|
3
|
+
* diagnostics (`errors === diagnostics.length`); tokens are built from the dot-path-rewritten
|
|
4
|
+
* stream. See `ParseResult` for the full result shape. */
|
|
5
|
+
export type { ParseResult } from "../parse-result.js";
|
|
19
6
|
/**
|
|
20
7
|
* Lex + parse BigQuery / GoogleSQL (one statement or a `;`-separated batch). Two-stage parsing:
|
|
21
8
|
* try the fast SLL prediction mode first (bail on the first conflict), fall back to full LL
|
package/dist/bigquery/parse.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BailErrorStrategy, CharStream, CommonTokenStream, PredictionMode
|
|
1
|
+
import { BailErrorStrategy, CharStream, CommonTokenStream, PredictionMode } from "antlr4ng";
|
|
2
2
|
import { GoogleSQLLexer } from "../generated/bigquery/GoogleSQLLexer.js";
|
|
3
3
|
import { GoogleSQLParser } from "../generated/bigquery/GoogleSQLParser.js";
|
|
4
4
|
import { dotPathTokenSource } from "./dot-path.js";
|
|
@@ -13,4 +13,6 @@ export interface Completion {
|
|
|
13
13
|
* (table names + column types). NEVER throws: on broken / mid-edit input it still returns the
|
|
14
14
|
* keyword candidates the walk can reach.
|
|
15
15
|
*/
|
|
16
|
-
export declare function
|
|
16
|
+
export declare function completeAt(doc: SqlDocument, offset: number, schema?: SchemaProvider): Completion[];
|
|
17
|
+
/** @deprecated Use completeAt — same function, uniform cursor-verb naming. */
|
|
18
|
+
export declare const complete: typeof completeAt;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
//
|
|
2
|
+
// completeAt() — scope-aware completion over a SqlDocument.
|
|
3
3
|
//
|
|
4
4
|
// The interactive editor feature that lives in the BROKEN-input world: the user
|
|
5
5
|
// is mid-keystroke, so this runs its OWN error-tolerant lex+parse of the current
|
|
@@ -25,7 +25,7 @@ import { makeParser } from "./parser-factory.js";
|
|
|
25
25
|
* (table names + column types). NEVER throws: on broken / mid-edit input it still returns the
|
|
26
26
|
* keyword candidates the walk can reach.
|
|
27
27
|
*/
|
|
28
|
-
export function
|
|
28
|
+
export function completeAt(doc, offset, schema) {
|
|
29
29
|
try {
|
|
30
30
|
return collect(doc, offset, schema);
|
|
31
31
|
}
|
|
@@ -34,6 +34,8 @@ export function complete(doc, offset, schema) {
|
|
|
34
34
|
return [];
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/** @deprecated Use completeAt — same function, uniform cursor-verb naming. */
|
|
38
|
+
export const complete = completeAt;
|
|
37
39
|
function collect(doc, offset, schema) {
|
|
38
40
|
const dialect = doc.dialect;
|
|
39
41
|
const cfg = COMPLETION_CONFIG[dialect];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CommonTokenStream, type Lexer, type Parser, type ParserRuleContext } from "antlr4ng";
|
|
2
|
-
import type { Dialect } from "../
|
|
2
|
+
import type { Dialect } from "../dialect.js";
|
|
3
3
|
/**
|
|
4
4
|
* A ready-to-walk parser for the completion engine: the lexer, the token stream, the entry
|
|
5
5
|
* rule's index (for `atn.ruleToStartState[...]`), and a `runEntry()` that drives the parse far
|
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
/** The CST rooted at `multiStatement` (a `;`-separated batch of statements and/or
|
|
6
|
-
* BEGIN…END SQL-scripting compounds, + EOF). */
|
|
7
|
-
tree: ParserRuleContext;
|
|
8
|
-
/** Count of lexer + parser syntax errors. */
|
|
9
|
-
errors: number;
|
|
10
|
-
/** Positioned syntax diagnostics (message + line/column/offset/length), in report order. */
|
|
11
|
-
diagnostics: SyntaxDiagnostic[];
|
|
12
|
-
/** Every lexer token (trivia included, EOF excluded), as neutral `Token`s with exact spans. */
|
|
13
|
-
tokens: Token[];
|
|
14
|
-
/** True when the fast SLL prediction pass bailed and the parse re-ran under full LL. Same result
|
|
15
|
-
* either way — this just says which path produced it, for perf profiling (tools/profile-sll.ts). */
|
|
16
|
-
sllFallback: boolean;
|
|
17
|
-
}
|
|
1
|
+
import type { ParseResult } from "../parse-result.js";
|
|
2
|
+
/** The CST rooted at `multiStatement` (a `;`-separated batch of statements and/or
|
|
3
|
+
* BEGIN…END SQL-scripting compounds, + EOF); see `ParseResult` for the full result shape. */
|
|
4
|
+
export type { ParseResult } from "../parse-result.js";
|
|
18
5
|
/**
|
|
19
6
|
* Lex + parse one Databricks SQL statement. Two-stage parsing: try the fast SLL
|
|
20
7
|
* prediction mode first (bail on the first conflict), and fall back to full LL only
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Dialect } from "./dialect.js";
|
|
2
|
+
export declare const DERIVED_DIALECTS: Readonly<Record<string, Dialect>>;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve an engine / product name (or a dialect name) to the dialect that
|
|
5
|
+
* parses its SQL. Case-insensitive. Returns undefined for anything not
|
|
6
|
+
* genuinely served by a gated grammar — never guesses.
|
|
7
|
+
*/
|
|
8
|
+
export declare function resolveDialect(engineOrDialect: string): Dialect | undefined;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Derived-dialect → dialect map. The eight grammars parse more than eight
|
|
3
|
+
// engines: a *derived dialect* is an engine with no grammar of its own whose
|
|
4
|
+
// SQL surface is a subset of — or identical to — one we already parse (Amazon
|
|
5
|
+
// Athena's engine is Trino, AWS Glue runs Spark, Microsoft Fabric / Azure
|
|
6
|
+
// Synapse / SQL Server speak T-SQL, …). A consumer that only knows an engine
|
|
7
|
+
// name should not have to own that knowledge — this table does.
|
|
8
|
+
//
|
|
9
|
+
// Keys are engine / product names (plus our own dialect names, so both
|
|
10
|
+
// vocabularies resolve). Only engines whose SQL surface our corpus gates
|
|
11
|
+
// genuinely represent are mapped — an unlisted name resolves to undefined,
|
|
12
|
+
// never to a guess.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
export const DERIVED_DIALECTS = {
|
|
15
|
+
// identity — the engine name and the dialect name coincide
|
|
16
|
+
databricks: "databricks",
|
|
17
|
+
snowflake: "snowflake",
|
|
18
|
+
bigquery: "bigquery",
|
|
19
|
+
redshift: "redshift",
|
|
20
|
+
postgres: "postgres",
|
|
21
|
+
duckdb: "duckdb",
|
|
22
|
+
trino: "trino",
|
|
23
|
+
// our dialect name (not an engine name) — accepted so both vocabularies work
|
|
24
|
+
tsql: "tsql",
|
|
25
|
+
// Spark SQL family — Databricks SQL = Spark SQL; AWS Glue runs Spark
|
|
26
|
+
spark: "databricks",
|
|
27
|
+
glue: "databricks",
|
|
28
|
+
// T-SQL family — Microsoft Fabric and Azure Synapse (restricted T-SQL subsets)
|
|
29
|
+
// and SQL Server (the reference T-SQL)
|
|
30
|
+
fabric: "tsql",
|
|
31
|
+
synapse: "tsql",
|
|
32
|
+
sqlserver: "tsql",
|
|
33
|
+
// Trino family — Amazon Athena engine v3 routes queries/DML to Trino; Presto
|
|
34
|
+
// is Trino's predecessor
|
|
35
|
+
athena: "trino",
|
|
36
|
+
presto: "trino",
|
|
37
|
+
// Alternate spelling of the engine name (alias class, same as our own dialect
|
|
38
|
+
// names above). Admitted 2026-07-10 on the anvil channel's request; caveat noted
|
|
39
|
+
// there: no dbt adapter is attested to emit `postgresql` as its adapter_type —
|
|
40
|
+
// it is inherited consumer vocabulary, admitted as a cheap, honest alias.
|
|
41
|
+
postgresql: "postgres",
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Resolve an engine / product name (or a dialect name) to the dialect that
|
|
45
|
+
* parses its SQL. Case-insensitive. Returns undefined for anything not
|
|
46
|
+
* genuinely served by a gated grammar — never guesses.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveDialect(engineOrDialect) {
|
|
49
|
+
return DERIVED_DIALECTS[engineOrDialect.trim().toLowerCase()];
|
|
50
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** The dialects reachable through the unified surface. Each has its own grammar/CST and a
|
|
2
|
+
* parse+lower pair; everything after lower() runs unchanged on all eight. */
|
|
3
|
+
export type Dialect = "databricks" | "tsql" | "snowflake" | "bigquery" | "redshift" | "postgres" | "duckdb" | "trino";
|
package/dist/dialect.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import type { ParserRuleContext } from "antlr4ng";
|
|
2
|
-
import { TypeInfo
|
|
3
|
-
import type
|
|
2
|
+
import { TypeInfo } from "../api.js";
|
|
3
|
+
import { type LineageHop } from "../lineage/hops.js";
|
|
4
|
+
import { type Occurrences } from "../references/references.js";
|
|
5
|
+
import type { Dialect } from "../dialect.js";
|
|
6
|
+
import type { QueryExpr, PartSpan } from "../ir/ir.js";
|
|
4
7
|
import type { StatementCategory } from "../ir/statement.js";
|
|
5
8
|
import type { SyntaxDiagnostic } from "../parse-diagnostics.js";
|
|
6
9
|
import type { ScopeTree } from "../scope/scope.js";
|
|
7
10
|
import type { Qualification, Diagnostic } from "../qualify/qualify.js";
|
|
8
11
|
import type { SchemaProvider } from "../qualify/schema-provider.js";
|
|
9
|
-
import type
|
|
12
|
+
import { type TemplateProvider } from "../qualify/template-provider.js";
|
|
13
|
+
import type { Span, Sym } from "../symbols/symbols.js";
|
|
10
14
|
import type { Token } from "../token/token.js";
|
|
15
|
+
import type { TemplateEngine, TemplatedParseResult, TemplateVariant } from "../template/engine.js";
|
|
11
16
|
import { LineIndex } from "./line-index.js";
|
|
12
17
|
import { type NodeHit } from "./node-at.js";
|
|
13
18
|
import { type StatementCellSpan } from "./split.js";
|
|
@@ -45,6 +50,39 @@ export interface DocumentAnalysis {
|
|
|
45
50
|
/** Qualification's semantic diagnostics (unknown table/column/field). */
|
|
46
51
|
diagnostics: Diagnostic[];
|
|
47
52
|
}
|
|
53
|
+
/** One coherent arm realization of a templated document. Lazy: nothing parses until doc() or
|
|
54
|
+
* text() is first touched. A variant IS a document — everything a plain SqlDocument exposes
|
|
55
|
+
* (ast/tokens/scopes/templated/analyze/cursor members) is available per arm through doc(). */
|
|
56
|
+
export interface DocumentVariant {
|
|
57
|
+
/** The arm this variant activates (undefined = variant 0, all defaults). Mirrors
|
|
58
|
+
* TemplateVariant.active, including the synthetic-empty marker from Task 1. */
|
|
59
|
+
readonly active?: TemplateVariant["active"];
|
|
60
|
+
/** The realized text: original text with inactive arms whitespace-blanked —
|
|
61
|
+
* length- and newline-preserving (spans stay document-true). Memoized. */
|
|
62
|
+
text(): string;
|
|
63
|
+
/** The arm's own SqlDocument: same dialect, SAME engine, SAME provider, SHARING the parent's
|
|
64
|
+
* content-addressed cell-cache family (an arm whose text an edit didn't change is a cache hit).
|
|
65
|
+
* Memoized per variant. */
|
|
66
|
+
doc(): SqlDocument;
|
|
67
|
+
}
|
|
68
|
+
/** One CTE's identity + its output columns unioned across arms (`SqlDocument.unionCtes`). */
|
|
69
|
+
export interface UnionCte {
|
|
70
|
+
/** Fold-normalized, quote-preserving identity form — `foldIdentifier(raw, dialect)`, the same
|
|
71
|
+
* vocabulary the column entries below (and the rest of the resolved surface) speak. The exact
|
|
72
|
+
* written form is recoverable by slicing the source at `declarationSpan`. */
|
|
73
|
+
name: string;
|
|
74
|
+
/** Declaration identity: the CTE name's own span (`CteDef.nameCst`, falling back to the whole
|
|
75
|
+
* `CteDef.cst` when the name itself has no real token). Part of the key: two same-named CTEs
|
|
76
|
+
* declared at different positions stay distinct entries. */
|
|
77
|
+
declarationSpan: PartSpan;
|
|
78
|
+
/** Output columns unioned by NAME across arms; each column's span is the FIRST LIVE ARM's (arm
|
|
79
|
+
* iteration order = `SqlDocument.variants` order) — the representative-span rule pinned by the
|
|
80
|
+
* variant-acceptance brief's A8a-c. Names are fold-normalized like `name` above. */
|
|
81
|
+
columns: {
|
|
82
|
+
name: string;
|
|
83
|
+
span: Span;
|
|
84
|
+
}[];
|
|
85
|
+
}
|
|
48
86
|
export declare class SqlDocument {
|
|
49
87
|
readonly uri?: string;
|
|
50
88
|
readonly version: number;
|
|
@@ -73,6 +111,10 @@ export declare class SqlDocument {
|
|
|
73
111
|
* facade's scopes (`statement: "compound"`); use `statements`/`cellAt` for per-statement scopes. */
|
|
74
112
|
readonly scopes: ScopeTree;
|
|
75
113
|
readonly lines: LineIndex;
|
|
114
|
+
/** The template artifacts when this document was built with a `templating` engine:
|
|
115
|
+
* tags/regions/symbols/placeholder/degraded plus tagOf/nodeOf/diagnosticsOf.
|
|
116
|
+
* Undefined on plain documents. */
|
|
117
|
+
readonly templated?: TemplatedParseResult;
|
|
76
118
|
/** Schema-keyed memo of the MERGED analyze() result (concat + coordinate shift). Rebuilt per doc
|
|
77
119
|
* version — the merge must redo when an earlier statement's line count changes and shifts later
|
|
78
120
|
* cells' doc coordinates — while the per-CELL analysis (the expensive qualify/deriveSymbols) is
|
|
@@ -86,6 +128,19 @@ export declare class SqlDocument {
|
|
|
86
128
|
/** The content-addressed cross-edit cell cache, carried to withText() children. Its contents stay
|
|
87
129
|
* mutable (a memo) even though the reference is frozen with the instance. */
|
|
88
130
|
private readonly _cellCache;
|
|
131
|
+
/** The injected template engine + provider, carried to withText() children so an edit keeps
|
|
132
|
+
* building through the same door its parent used. Undefined on a plain document. */
|
|
133
|
+
private readonly _templating?;
|
|
134
|
+
private readonly _provider?;
|
|
135
|
+
/** Memo box for the `variants` getter (frozen instance, mutable memo — the `_analysisCache`
|
|
136
|
+
* precedent: the box reference is frozen with the instance, but its `.value` stays settable). */
|
|
137
|
+
private readonly _variantsMemo;
|
|
138
|
+
/** Schema-keyed memos for the four union views (`unionSymbols`/`unionDiagnostics`/`unionCtes`/
|
|
139
|
+
* `unionOutputColumns`) — one WeakMap per view, same identity+version pattern as `_analysisCache`. */
|
|
140
|
+
private readonly _unionSymbolsCache;
|
|
141
|
+
private readonly _unionDiagnosticsCache;
|
|
142
|
+
private readonly _unionCtesCache;
|
|
143
|
+
private readonly _unionOutputColumnsCache;
|
|
89
144
|
private constructor();
|
|
90
145
|
/** Build one statement cell for `span`: reuse the cached cell-relative parse if its text is
|
|
91
146
|
* already known (content addressing), else parse+scope it and cache it; then shift tokens /
|
|
@@ -100,14 +155,29 @@ export declare class SqlDocument {
|
|
|
100
155
|
* NOT replace the cache entry: the first occurrence keeps its stable cross-edit identity (the
|
|
101
156
|
* common case), and only intra-doc duplicates — rare — pay a re-parse per build. */
|
|
102
157
|
private buildCell;
|
|
158
|
+
/** Build the ONE cell for a TEMPLATED document: span [0, text.length) — the templated build path
|
|
159
|
+
* bypasses `splitStatements` entirely (one cell, whole text), and its products come from a single
|
|
160
|
+
* `engine.parse(text, dialect, { provider })` call rather than the plain per-dialect `parse()`.
|
|
161
|
+
* `r.tokens`/`r.diagnostics` are ALREADY document coordinates (the cell always starts at 0), so —
|
|
162
|
+
* unlike `buildCell` — nothing is shifted. Mirrors `buildCell`'s CachedCell/StatementCell shapes
|
|
163
|
+
* so every downstream consumer (analyze(), cellAt(), nodeAt()…) sees the same structure whether
|
|
164
|
+
* the document is plain or templated. Cached in the SAME cross-edit `_cellCache` as plain cells,
|
|
165
|
+
* under a prefixed key so a templated cell can never collide with a plain one for the same text. */
|
|
166
|
+
private buildTemplatedCell;
|
|
103
167
|
/** Build a document for `text` in `dialect`. Total: never throws, even on broken / mid-edit input.
|
|
104
|
-
* Starts a FRESH cell cache — cross-edit reuse comes from withText(), not create().
|
|
168
|
+
* Starts a FRESH cell cache — cross-edit reuse comes from withText(), not create(). Pass
|
|
169
|
+
* `templating` to parse through an injected TemplateEngine (jinja-SQL etc.) instead of the plain
|
|
170
|
+
* per-dialect parser — absent, this is the exact untouched plain-SQL path (never auto-detected).
|
|
171
|
+
* `provider` feeds the engine's fills/markers and is ignored without `templating`. */
|
|
105
172
|
static create(text: string, dialect: Dialect, opts?: {
|
|
106
173
|
uri?: string;
|
|
107
174
|
version?: number;
|
|
175
|
+
templating?: TemplateEngine;
|
|
176
|
+
provider?: TemplateProvider;
|
|
108
177
|
}): SqlDocument;
|
|
109
178
|
/** An edit: a NEW SqlDocument for the new text. This instance is untouched (immutable). The cell
|
|
110
|
-
* cache is CARRIED forward, so statements whose text didn't change reuse their parsed cells.
|
|
179
|
+
* cache is CARRIED forward, so statements whose text didn't change reuse their parsed cells. The
|
|
180
|
+
* injected templating engine + provider (if any) ride the instance to this child too. */
|
|
111
181
|
withText(text: string, version: number): SqlDocument;
|
|
112
182
|
/** The statement cell owning `offset` (binary search over the tiling cell spans), or undefined if
|
|
113
183
|
* there are no cells. An offset at end-of-document resolves to the last cell. */
|
|
@@ -122,6 +192,42 @@ export declare class SqlDocument {
|
|
|
122
192
|
* `expr.cst` carries CELL-relative spans; a caller turning it into a document Range shifts it by the
|
|
123
193
|
* owning cell's start (see `cellBaseAt` in src/lsp/ranges.ts). Single-cell: identical to today. */
|
|
124
194
|
nodeAt(offset: number): NodeHit | undefined;
|
|
195
|
+
/** The declaration + every occurrence of the symbol at `offset` (the references engine,
|
|
196
|
+
* cell-aware): resolved over the CELL owning the offset — its own scopes/ast, with a
|
|
197
|
+
* cell-relative offset — then every returned span (occurrences + declaration) shifted from
|
|
198
|
+
* cell-relative to DOCUMENT coordinates by the cell base. Absorbs the dance the LSP
|
|
199
|
+
* references/documentHighlight/codeLens features hand-rolled. Single-cell documents: base
|
|
200
|
+
* 0/0/0, byte-identical to the free referencesAt over doc.scopes. Total: null off-symbol or
|
|
201
|
+
* with no cells; never throws. */
|
|
202
|
+
referencesAt(offset: number, schema?: SchemaProvider): Occurrences | null;
|
|
203
|
+
/** The per-hop lineage spine anchored at `offset` (cell-aware): resolved over the CELL owning
|
|
204
|
+
* the offset, with a cell-relative offset. NOTE: on a MULTI-statement document the returned
|
|
205
|
+
* hop nodes' cst spans are CELL-relative — the spine references the frozen per-cell IR (hops
|
|
206
|
+
* are references, not copies), so nothing is shifted here; use `cellAt(offset).span.start` as
|
|
207
|
+
* the base to map them to document coordinates. Single-cell documents (every dbt model) are
|
|
208
|
+
* identical either way. Total: undefined off-symbol or with no cells; never throws. */
|
|
209
|
+
lineageAt(offset: number, schema?: SchemaProvider): LineageHop | undefined;
|
|
210
|
+
/** The coherent per-arm variants of a templated document (engine.variants() consumed). `[]` on
|
|
211
|
+
* plain documents and on templated documents with no control-flow regions. Lazy at every level:
|
|
212
|
+
* enumeration on first read, realization+parse per variant on first doc()/text(). Memoized on
|
|
213
|
+
* the instance (frozen instance, mutable memo box — see `_variantsMemo`). */
|
|
214
|
+
get variants(): readonly DocumentVariant[];
|
|
215
|
+
/** Compute (no memo) the variant list: absent `variants` hook, a plain document, or a templated
|
|
216
|
+
* document with no control-flow regions all answer `[]`. Otherwise consults the engine, wrapped
|
|
217
|
+
* defensively (degrade to `[]`, never throw — engine.variants may throw only on engine bugs, the
|
|
218
|
+
* same posture parseTemplated's own catch uses). */
|
|
219
|
+
private buildVariants;
|
|
220
|
+
/** Wrap one engine-produced TemplateVariant as a DocumentVariant: `text()` delegates straight to
|
|
221
|
+
* the engine variant's own memoized realization (never re-realized here); `doc()` builds — once,
|
|
222
|
+
* memoized — the arm's own SqlDocument through the PRIVATE constructor, carrying this document's
|
|
223
|
+
* `_cellCache` (the exact carry `withText` uses, NOT the public `create`'s fresh cache) plus the
|
|
224
|
+
* same templating engine + provider, so an unchanged arm across an edit is a cache hit. */
|
|
225
|
+
private wrapVariant;
|
|
226
|
+
/** The variant whose realization has `offset` LIVE (its arm active): an offset inside a
|
|
227
|
+
* non-default arm routes to that arm's variant; a default-arm or outside-all-regions offset
|
|
228
|
+
* routes to variant 0. undefined on plain documents and no-region templated documents.
|
|
229
|
+
* Never throws; out-of-range offsets answer variant 0 (the honest default). */
|
|
230
|
+
variantAt(offset: number): DocumentVariant | undefined;
|
|
125
231
|
/** The schema-dependent tiers, over the cached per-cell scopes/ast (no re-parse). Memoized by
|
|
126
232
|
* schema IDENTITY + VERSION — a plain Schema (version 0) memoizes exactly as before; a
|
|
127
233
|
* CallbackSchema that has been prime()d bumps its version and so re-computes with the newly
|
|
@@ -139,4 +245,52 @@ export declare class SqlDocument {
|
|
|
139
245
|
* identity + version) so it survives edits that don't touch this cell, yet re-runs when a primed
|
|
140
246
|
* CallbackSchema bumps its version. */
|
|
141
247
|
private cellAnalysis;
|
|
248
|
+
/** Symbols across ALL variants, deduped by span+identity+NAME (`start:end:kind:frame:name` —
|
|
249
|
+
* NAME is load-bearing: schema-expanded star Syms share one zero-width span by design, see
|
|
250
|
+
* symbols.ts's emitColumns). Computed over the VARIANT documents only — never the primary
|
|
251
|
+
* parse, whose all-arms-live SQL can mis-read conflicting arms (e.g. `col_a col_b` reading as
|
|
252
|
+
* an alias) into junk that must never leak into the union. Equals the plain `analyze(schema)`
|
|
253
|
+
* symbols when there are no variants (a plain document, or a templated one with no
|
|
254
|
+
* control-flow regions). Memoized per schema identity+version, like `analyze()`. */
|
|
255
|
+
unionSymbols(schema?: SchemaProvider): Sym[];
|
|
256
|
+
private buildUnionSymbols;
|
|
257
|
+
/** Diagnostics across all variants — syntax + semantic, like `session.diagnostics()` — deduped
|
|
258
|
+
* by position+identity, NOT message text: two arms producing the SAME diagnostic at the SAME
|
|
259
|
+
* position collapse to one entry, but the same message at TWO DIFFERENT positions stays two
|
|
260
|
+
* entries (the exact case a message-keyed merge gets wrong). Same variant-only, memoized
|
|
261
|
+
* semantics as `unionSymbols`. */
|
|
262
|
+
unionDiagnostics(schema?: SchemaProvider): (SyntaxDiagnostic | Diagnostic)[];
|
|
263
|
+
private buildUnionDiagnostics;
|
|
264
|
+
/** Per-CTE column unions across all variants, keyed by NAME + declaration span (two same-named
|
|
265
|
+
* CTEs declared at different positions stay distinct entries; a CTE existing in only one arm
|
|
266
|
+
* still appears). Columns union by NAME; a column's representative span is the FIRST LIVE ARM's
|
|
267
|
+
* (arm iteration order = `this.variants` order) — the rule the variant-acceptance brief's A8a-c
|
|
268
|
+
* pin. A CTE whose body is a set operation answers through the qualification (names per SQL
|
|
269
|
+
* setop semantics, spans from the declaring branch); a PIPE-syntax body answers no columns — a
|
|
270
|
+
* visible, ledgered gap, see `scopeOutputColumns`. Falls through to this document's own
|
|
271
|
+
* (single-arm) answer when there are no variants — there is no pre-existing single-doc
|
|
272
|
+
* equivalent to delegate to, unlike unionSymbols/unionDiagnostics, so the no-variant case is
|
|
273
|
+
* just the one-arm instance of the same algorithm. Variant-only, memoized like `unionSymbols`.
|
|
274
|
+
* KNOWN GAP (visible, ledgered — not silently narrowed): on a multi-statement document these
|
|
275
|
+
* answer `[]` — the compound facade carries no CTEs/outputs; the per-cell merge is a tracked
|
|
276
|
+
* follow-up; single-statement documents (every dbt model) are fully covered. */
|
|
277
|
+
unionCtes(schema?: SchemaProvider): UnionCte[];
|
|
278
|
+
private buildUnionCtes;
|
|
279
|
+
/** The document's final-SELECT (root scope) output columns unioned across arms — same NAME
|
|
280
|
+
* keying and representative-span rule as `unionCtes`. A setop root (the dbt-incremental
|
|
281
|
+
* `… UNION ALL …` arm shape) answers through the qualification — names per SQL setop semantics
|
|
282
|
+
* (left branch positionally, BY NAME appends right-only), spans from the declaring branch. A
|
|
283
|
+
* PIPE-syntax root answers `[]` — a visible, ledgered gap, see `scopeOutputColumns`. Falls
|
|
284
|
+
* through to this document's own root outputs when there are no variants.
|
|
285
|
+
* KNOWN GAP (visible, ledgered — not silently narrowed): on a multi-statement document these
|
|
286
|
+
* answer `[]` — the compound facade carries no CTEs/outputs; the per-cell merge is a tracked
|
|
287
|
+
* follow-up; single-statement documents (every dbt model) are fully covered. */
|
|
288
|
+
unionOutputColumns(schema?: SchemaProvider): {
|
|
289
|
+
name: string;
|
|
290
|
+
span: Span;
|
|
291
|
+
}[];
|
|
292
|
+
private buildUnionOutputColumns;
|
|
293
|
+
/** The per-arm documents `unionCtes`/`unionOutputColumns` aggregate over: the real variants when
|
|
294
|
+
* they exist, else this document itself as the sole arm (see those methods' fall-through note). */
|
|
295
|
+
private arms;
|
|
142
296
|
}
|