sqllens 1.1.0 → 1.2.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 +2 -74
- package/THIRD-PARTY-NOTICES.md +50 -5
- package/dist/api.d.ts +5 -3
- package/dist/api.js +11 -4
- package/dist/bigquery/behavior.d.ts +2 -0
- package/dist/bigquery/behavior.js +20 -0
- package/dist/bigquery/dot-path.d.ts +0 -2
- package/dist/bigquery/dot-path.js +0 -1
- package/dist/bigquery/fold.d.ts +8 -0
- package/dist/bigquery/fold.js +39 -0
- package/dist/bigquery/index.d.ts +7 -0
- package/dist/bigquery/index.js +10 -0
- package/dist/{infer/bigquery.d.ts → bigquery/infer.d.ts} +2 -2
- package/dist/{infer/bigquery.js → bigquery/infer.js} +4 -3
- package/dist/bigquery/lower.js +35 -12
- package/dist/bigquery/signatures.generated.d.ts +6 -0
- package/dist/bigquery/signatures.generated.js +1068 -0
- package/dist/completion/atn-walk.d.ts +13 -2
- package/dist/completion/atn-walk.js +13 -10
- package/dist/completion/complete.d.ts +4 -3
- package/dist/completion/complete.js +101 -45
- package/dist/completion/jinja-slot.d.ts +24 -0
- package/dist/completion/jinja-slot.js +126 -0
- package/dist/completion/parser-factory.d.ts +13 -1
- package/dist/completion/parser-factory.js +12 -0
- package/dist/databricks/behavior.d.ts +2 -0
- package/dist/databricks/behavior.js +19 -0
- package/dist/databricks/fold.d.ts +8 -0
- package/dist/databricks/fold.js +27 -0
- package/dist/databricks/index.d.ts +7 -0
- package/dist/databricks/index.js +10 -0
- package/dist/databricks/infer.d.ts +6 -0
- package/dist/databricks/infer.js +638 -0
- package/dist/databricks/signatures.generated.d.ts +6 -0
- package/dist/databricks/signatures.generated.js +1745 -0
- package/dist/dialect-behavior/behavior.d.ts +26 -0
- package/dist/dialect-behavior/behavior.js +1 -0
- package/dist/dialect-behavior/carrier.d.ts +5 -0
- package/dist/dialect-behavior/carrier.js +5 -0
- package/dist/dialect-behavior/coerce-rules.d.ts +7 -0
- package/dist/dialect-behavior/coerce-rules.js +69 -0
- package/dist/dialect-behavior/public-fold.d.ts +6 -0
- package/dist/dialect-behavior/public-fold.js +9 -0
- package/dist/dialect-behavior/registry.d.ts +6 -0
- package/dist/dialect-behavior/registry.js +34 -0
- package/dist/dialect-symbols.js +16 -19
- package/dist/document/document.d.ts +1 -1
- package/dist/document/document.js +8 -7
- package/dist/duckdb/behavior.d.ts +2 -0
- package/dist/duckdb/behavior.js +21 -0
- package/dist/duckdb/fold.d.ts +8 -0
- package/dist/duckdb/fold.js +29 -0
- package/dist/duckdb/index.d.ts +7 -0
- package/dist/duckdb/index.js +10 -0
- package/dist/{infer/duckdb.d.ts → duckdb/infer.d.ts} +2 -2
- package/dist/{infer/duckdb.js → duckdb/infer.js} +4 -3
- package/dist/duckdb/lower.js +47 -15
- package/dist/duckdb/signatures.generated.d.ts +6 -0
- package/dist/duckdb/signatures.generated.js +1072 -0
- package/dist/generated/bigquery/GoogleSQLParser.js +0 -7060
- package/dist/generated/databricks/DatabricksParser.js +0 -4800
- package/dist/generated/duckdb/DuckdbParser.js +0 -9100
- package/dist/generated/minijinja/MinijinjaParser.js +0 -380
- package/dist/generated/mysql/MysqlParser.js +0 -6330
- package/dist/generated/postgres/PostgresParser.js +0 -8530
- package/dist/generated/redshift/RedshiftParser.js +0 -10970
- package/dist/generated/snowflake/SnowflakeParser.js +0 -7320
- package/dist/generated/sqlite/SqliteParser.js +0 -1150
- package/dist/generated/trino/TrinoParser.js +0 -3770
- package/dist/generated/tsql/TSqlParser.js +0 -8420
- package/dist/ident/fold.d.ts +24 -15
- package/dist/ident/fold.js +12 -194
- package/dist/index.d.ts +2 -2
- package/dist/index.js +10 -8
- package/dist/infer/functions.d.ts +22 -11
- package/dist/infer/functions.js +26 -888
- package/dist/infer/infer.js +16 -14
- package/dist/infer/nullability.js +3 -4
- package/dist/infer/types.d.ts +1 -1
- package/dist/infer/types.js +7 -7
- package/dist/ir/ir.d.ts +26 -17
- package/dist/ir/span.js +0 -2
- package/dist/ir/walk.js +3 -3
- package/dist/lineage/hops.js +13 -10
- package/dist/lineage/lineage.js +10 -7
- package/dist/minijinja/apply-tags.d.ts +14 -7
- package/dist/minijinja/apply-tags.js +66 -121
- package/dist/minijinja/parse.js +5 -5
- package/dist/minijinja/tag-ast.d.ts +29 -36
- package/dist/minijinja/tag-ast.js +210 -90
- package/dist/mysql/behavior.d.ts +2 -0
- package/dist/mysql/behavior.js +21 -0
- package/dist/mysql/fold.d.ts +8 -0
- package/dist/mysql/fold.js +49 -0
- package/dist/mysql/index.d.ts +7 -0
- package/dist/mysql/index.js +10 -0
- package/dist/{infer/mysql.d.ts → mysql/infer.d.ts} +2 -2
- package/dist/{infer/mysql.js → mysql/infer.js} +3 -2
- package/dist/mysql/signatures.generated.d.ts +6 -0
- package/dist/mysql/signatures.generated.js +508 -0
- package/dist/postgres/behavior.d.ts +2 -0
- package/dist/postgres/behavior.js +19 -0
- package/dist/postgres/fold.d.ts +8 -0
- package/dist/postgres/fold.js +30 -0
- package/dist/postgres/index.d.ts +7 -0
- package/dist/postgres/index.js +10 -0
- package/dist/{infer/postgres.d.ts → postgres/infer.d.ts} +2 -2
- package/dist/{infer/postgres.js → postgres/infer.js} +4 -3
- package/dist/postgres/lower.js +2 -2
- package/dist/postgres/signatures.generated.d.ts +6 -0
- package/dist/postgres/signatures.generated.js +2973 -0
- package/dist/qualify/check-calls.js +80 -161
- package/dist/qualify/qualify.js +16 -14
- package/dist/qualify/schema-provider.js +2 -2
- package/dist/qualify/schema.js +3 -3
- package/dist/qualify/template-provider.d.ts +45 -12
- package/dist/qualify/template-provider.js +69 -38
- package/dist/redshift/behavior.d.ts +2 -0
- package/dist/redshift/behavior.js +19 -0
- package/dist/redshift/fold.d.ts +8 -0
- package/dist/redshift/fold.js +31 -0
- package/dist/redshift/index.d.ts +7 -0
- package/dist/redshift/index.js +10 -0
- package/dist/{infer/redshift.d.ts → redshift/infer.d.ts} +2 -2
- package/dist/{infer/redshift.js → redshift/infer.js} +4 -3
- package/dist/redshift/lower.js +2 -2
- package/dist/redshift/signatures.generated.d.ts +6 -0
- package/dist/redshift/signatures.generated.js +757 -0
- package/dist/references/references.js +17 -12
- package/dist/scope/like-pattern.d.ts +2 -0
- package/dist/scope/like-pattern.js +15 -0
- package/dist/scope/scope.d.ts +5 -5
- package/dist/scope/scope.js +48 -42
- package/dist/sema/resolve.js +17 -12
- package/dist/session.d.ts +2 -2
- package/dist/signature/signature.d.ts +14 -6
- package/dist/signature/signature.js +30 -22
- package/dist/signature/signatures.d.ts +14 -12
- package/dist/signature/signatures.js +42 -721
- package/dist/snowflake/behavior.d.ts +2 -0
- package/dist/snowflake/behavior.js +22 -0
- package/dist/snowflake/fold.d.ts +8 -0
- package/dist/snowflake/fold.js +25 -0
- package/dist/snowflake/index.d.ts +7 -0
- package/dist/snowflake/index.js +10 -0
- package/dist/{infer/snowflake.d.ts → snowflake/infer.d.ts} +2 -2
- package/dist/{infer/snowflake.js → snowflake/infer.js} +4 -3
- package/dist/snowflake/lower.js +59 -19
- package/dist/snowflake/signatures.generated.d.ts +6 -0
- package/dist/snowflake/signatures.generated.js +2080 -0
- package/dist/sqlite/behavior.d.ts +2 -0
- package/dist/sqlite/behavior.js +19 -0
- package/dist/sqlite/fold.d.ts +8 -0
- package/dist/sqlite/fold.js +41 -0
- package/dist/sqlite/index.d.ts +7 -0
- package/dist/sqlite/index.js +10 -0
- package/dist/{infer/sqlite.d.ts → sqlite/infer.d.ts} +2 -2
- package/dist/{infer/sqlite.js → sqlite/infer.js} +4 -3
- package/dist/sqlite/signatures.generated.d.ts +6 -0
- package/dist/sqlite/signatures.generated.js +277 -0
- package/dist/symbols/symbols.js +14 -12
- package/dist/trino/behavior.d.ts +2 -0
- package/dist/trino/behavior.js +21 -0
- package/dist/trino/fold.d.ts +8 -0
- package/dist/trino/fold.js +39 -0
- package/dist/trino/index.d.ts +7 -0
- package/dist/trino/index.js +10 -0
- package/dist/{infer/trino.d.ts → trino/infer.d.ts} +2 -2
- package/dist/{infer/trino.js → trino/infer.js} +4 -3
- package/dist/trino/lower.js +5 -5
- package/dist/trino/signatures.generated.d.ts +6 -0
- package/dist/trino/signatures.generated.js +968 -0
- package/dist/tsql/behavior.d.ts +2 -0
- package/dist/tsql/behavior.js +20 -0
- package/dist/tsql/fold.d.ts +8 -0
- package/dist/tsql/fold.js +34 -0
- package/dist/tsql/index.d.ts +7 -0
- package/dist/tsql/index.js +10 -0
- package/dist/tsql/infer.d.ts +16 -0
- package/dist/tsql/infer.js +289 -0
- package/dist/tsql/lower.js +4 -4
- package/dist/tsql/signatures.generated.d.ts +6 -0
- package/dist/tsql/signatures.generated.js +640 -0
- package/package.json +5 -10
- package/dist/generated/bigquery/GoogleSQLLexer.d.ts +0 -407
- package/dist/generated/bigquery/GoogleSQLParser.d.ts +0 -9558
- package/dist/generated/bigquery/GoogleSQLParserListener.d.ts +0 -7777
- package/dist/generated/bigquery/GoogleSQLParserListener.js +0 -7070
- package/dist/generated/databricks/DatabricksLexer.d.ts +0 -566
- package/dist/generated/databricks/DatabricksParser.d.ts +0 -7771
- package/dist/generated/databricks/DatabricksParserListener.d.ts +0 -5737
- package/dist/generated/databricks/DatabricksParserListener.js +0 -5256
- package/dist/generated/duckdb/DuckdbLexer.d.ts +0 -691
- package/dist/generated/duckdb/DuckdbParser.d.ts +0 -13932
- package/dist/generated/duckdb/DuckdbParserListener.d.ts +0 -10049
- package/dist/generated/duckdb/DuckdbParserListener.js +0 -9138
- package/dist/generated/minijinja/MinijinjaLexer.d.ts +0 -108
- package/dist/generated/minijinja/MinijinjaParser.d.ts +0 -604
- package/dist/generated/minijinja/MinijinjaParserListener.d.ts +0 -449
- package/dist/generated/minijinja/MinijinjaParserListener.js +0 -410
- package/dist/generated/mysql/MysqlLexer.d.ts +0 -1198
- package/dist/generated/mysql/MysqlParser.d.ts +0 -10992
- package/dist/generated/mysql/MysqlParserListener.d.ts +0 -7592
- package/dist/generated/mysql/MysqlParserListener.js +0 -6958
- package/dist/generated/postgres/PostgresLexer.d.ts +0 -663
- package/dist/generated/postgres/PostgresParser.d.ts +0 -12963
- package/dist/generated/postgres/PostgresParserListener.d.ts +0 -9408
- package/dist/generated/postgres/PostgresParserListener.js +0 -8554
- package/dist/generated/redshift/RedshiftLexer.d.ts +0 -954
- package/dist/generated/redshift/RedshiftParser.d.ts +0 -16939
- package/dist/generated/redshift/RedshiftParserListener.d.ts +0 -12092
- package/dist/generated/redshift/RedshiftParserListener.js +0 -10994
- package/dist/generated/snowflake/SnowflakeLexer.d.ts +0 -1046
- package/dist/generated/snowflake/SnowflakeParser.d.ts +0 -14196
- package/dist/generated/snowflake/SnowflakeParserListener.d.ts +0 -8063
- package/dist/generated/snowflake/SnowflakeParserListener.js +0 -7330
- package/dist/generated/sqlite/SqliteLexer.d.ts +0 -210
- package/dist/generated/sqlite/SqliteParser.d.ts +0 -2115
- package/dist/generated/sqlite/SqliteParserListener.d.ts +0 -1276
- package/dist/generated/sqlite/SqliteParserListener.js +0 -1160
- package/dist/generated/trino/TrinoLexer.d.ts +0 -381
- package/dist/generated/trino/TrinoParser.d.ts +0 -5340
- package/dist/generated/trino/TrinoParserListener.d.ts +0 -4704
- package/dist/generated/trino/TrinoParserListener.js +0 -4326
- package/dist/generated/tsql/TSqlLexer.d.ts +0 -1278
- package/dist/generated/tsql/TSqlParser.d.ts +0 -17267
- package/dist/generated/tsql/TSqlParserListener.d.ts +0 -9697
- package/dist/generated/tsql/TSqlParserListener.js +0 -8854
- package/dist/infer/dialect.d.ts +0 -21
- package/dist/infer/dialect.js +0 -100
- package/dist/infer/literals.d.ts +0 -6
- package/dist/infer/literals.js +0 -44
- package/dist/signature/generated/tsql.d.ts +0 -3
- package/dist/signature/generated/tsql.js +0 -260
|
@@ -1,41 +1,36 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
2
|
+
// R2 tag-AST: the NEUTRAL tag node with the EXACT span contract
|
|
3
|
+
// (docs/minijinja-front-end.md §R2). This is the HARD deliverable: the editor
|
|
4
|
+
// positions hover / rename / signature-help exactly on the spans emitted here, so
|
|
5
|
+
// every offset must be document-true.
|
|
6
6
|
//
|
|
7
7
|
// The walk is a small tree-navigation over the per-tag jinja parse tree, built by
|
|
8
|
-
// parse.ts from a DOCUMENT-native token slice (segment.ts's `tagTokens
|
|
8
|
+
// parse.ts from a DOCUMENT-native token slice (segment.ts's `tagTokens`, the one
|
|
9
9
|
// whole-document tokenization, sliced per tag via antlr4ng's `ListTokenSource`).
|
|
10
10
|
// Every token in that slice already carries its real document start/stop/line/
|
|
11
11
|
// column, so every span read off it is document-true with NO offset/anchor
|
|
12
|
-
// composition
|
|
13
|
-
// extractors, now by construction rather than by a doc-offset shift.
|
|
12
|
+
// composition.
|
|
14
13
|
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
// - a
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
// -
|
|
24
|
-
//
|
|
25
|
-
// - a `{% … %}` statement tag → "control" (inc1 treatment; precise
|
|
26
|
-
// if/for/set structure + `{% do macro() %}` macro-in-stmt is inc2)
|
|
27
|
-
// - a `{# … #}` comment tag → "other"
|
|
14
|
+
// The tag AST carries NO dbt vocabulary. Every expression tag classifies into one
|
|
15
|
+
// of three neutral kinds:
|
|
16
|
+
// - a leading call `name(args)` (bare or `pkg.name`) → a "call" node. ref/source/
|
|
17
|
+
// config/var/env_var/a user macro are ALL just callees, distinguished by `name`.
|
|
18
|
+
// Whether `ref` denotes a relation is the provider's knowledge, not the tag AST.
|
|
19
|
+
// Each arg carries {value, span, valueSpan}: a consumer that knows a call's arg
|
|
20
|
+
// roles (dbt: ref's model is the last arg) reads value + valueSpan straight off.
|
|
21
|
+
// - a `{% … %}` statement tag → "control" (if/for/set/macro structure).
|
|
22
|
+
// - any other expr (a bare name, a literal, arithmetic) or a `{# … #}` comment →
|
|
23
|
+
// "other".
|
|
28
24
|
//
|
|
29
|
-
// Never-wrong (global-constraints): a
|
|
30
|
-
//
|
|
31
|
-
//
|
|
32
|
-
//
|
|
25
|
+
// Never-wrong (global-constraints): a string arg's `value` is a real literal only
|
|
26
|
+
// (a computed arg like `ref(var('x'))` has `value: null`, never fabricated); a
|
|
27
|
+
// broken `{{ ref( }}` degrades to a best-effort node + the parse's positioned
|
|
28
|
+
// diagnostic, never a throw.
|
|
33
29
|
// ---------------------------------------------------------------------------
|
|
34
|
-
import { ParserRuleContext, TerminalNode } from "antlr4ng";
|
|
30
|
+
import { ParserRuleContext, TerminalNode, Token as AntlrToken } from "antlr4ng";
|
|
35
31
|
import { MinijinjaParser } from "../generated/minijinja/MinijinjaParser.js";
|
|
36
32
|
import { CallExprContext, Endraw_tagContext, MemberExprContext, NameExprContext, Raw_tagContext, StmtContext, } from "../generated/minijinja/MinijinjaParser.js";
|
|
37
33
|
import { endPosition } from "../ir/span.js";
|
|
38
|
-
import { NO_OUTPUT_BUILTINS } from "./segment.js";
|
|
39
34
|
// ---------------------------------------------------------------------------
|
|
40
35
|
// Span helpers. The tokens here come from parse.ts's per-tag `ListTokenSource`
|
|
41
36
|
// slice over the ONE whole-document tokenization (segment.ts's `tagTokens`), so
|
|
@@ -257,9 +252,9 @@ function argInfo(call) {
|
|
|
257
252
|
const args = [];
|
|
258
253
|
if (argList) {
|
|
259
254
|
for (const arg of argList.arg()) {
|
|
260
|
-
const
|
|
261
|
-
if (
|
|
262
|
-
args.push(
|
|
255
|
+
const a = argOf(arg);
|
|
256
|
+
if (a)
|
|
257
|
+
args.push(a);
|
|
263
258
|
}
|
|
264
259
|
}
|
|
265
260
|
let argsSpan;
|
|
@@ -271,6 +266,42 @@ function argInfo(call) {
|
|
|
271
266
|
}
|
|
272
267
|
return { args, ...(argsSpan ? { argsSpan } : {}) };
|
|
273
268
|
}
|
|
269
|
+
/** The bare literal token types an argument can be (besides STRING): a consumer reads their text as
|
|
270
|
+
* the arg value. */
|
|
271
|
+
const LITERAL_TOKENS = new Set([
|
|
272
|
+
MinijinjaParser.INT,
|
|
273
|
+
MinijinjaParser.FLOAT,
|
|
274
|
+
MinijinjaParser.TRUE,
|
|
275
|
+
MinijinjaParser.FALSE,
|
|
276
|
+
MinijinjaParser.NONE,
|
|
277
|
+
]);
|
|
278
|
+
/** One argument as `{value, span, valueSpan?}`. `span` is the WHOLE arg (incl. any `name=` and quotes).
|
|
279
|
+
* The literal VALUE + its quote-excluded `valueSpan` come from the value expression: for a kwarg
|
|
280
|
+
* (`model='x'`) that's the RHS, for a positional it's the arg itself. A STRING literal yields the
|
|
281
|
+
* stripped value + content span; a bare number/bool/none yields its text + its span; anything
|
|
282
|
+
* computed yields `value: null` (never fabricated). undefined only when the arg has no span at all. */
|
|
283
|
+
function argOf(arg) {
|
|
284
|
+
const span = spanOfNode(arg);
|
|
285
|
+
if (!span)
|
|
286
|
+
return undefined;
|
|
287
|
+
const valueCtx = isKwarg(arg) ? kwargValue(arg) : arg;
|
|
288
|
+
if (valueCtx) {
|
|
289
|
+
const str = directStringToken(valueCtx);
|
|
290
|
+
if (str)
|
|
291
|
+
return { value: stringValue(str), span, valueSpan: stringContentSpan(str) };
|
|
292
|
+
const v = valueCtx.start;
|
|
293
|
+
if (v && v === valueCtx.stop && LITERAL_TOKENS.has(v.type)) {
|
|
294
|
+
return { value: v.text ?? null, span, valueSpan: spanOfNode(valueCtx) };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
return { value: null, span };
|
|
298
|
+
}
|
|
299
|
+
/** The value expression of a kwarg (`id = expr` → the `expr`), or undefined. It is the last child;
|
|
300
|
+
* the ASSIGN and id precede it. */
|
|
301
|
+
function kwargValue(arg) {
|
|
302
|
+
const last = arg.getChild(arg.getChildCount() - 1);
|
|
303
|
+
return last instanceof ParserRuleContext ? last : undefined;
|
|
304
|
+
}
|
|
274
305
|
/**
|
|
275
306
|
* Extract a CallExprContext into the reusable MacroCall fields — name/nameSpan +
|
|
276
307
|
* optional package via `decomposeCallee`, args[] + argsSpan via `argInfo`. Returns
|
|
@@ -279,7 +310,7 @@ function argInfo(call) {
|
|
|
279
310
|
* fabricated (never-wrong). The `{{ }}` macro node and the `{% %}` control tag both
|
|
280
311
|
* build from this, so a call surfaces identically wherever it appears.
|
|
281
312
|
*/
|
|
282
|
-
|
|
313
|
+
function callToMacroCall(call) {
|
|
283
314
|
const callee = decomposeCallee(call);
|
|
284
315
|
if (!callee || !callee.nameSpan)
|
|
285
316
|
return undefined;
|
|
@@ -306,26 +337,159 @@ function macroNode(call, callee, tagSpan, tree) {
|
|
|
306
337
|
if (mc)
|
|
307
338
|
calls.push(mc);
|
|
308
339
|
}
|
|
340
|
+
const callSpan = spanOfNode(call) ?? tagSpan;
|
|
309
341
|
const mc = callToMacroCall(call);
|
|
310
342
|
if (mc)
|
|
311
|
-
return { kind: "
|
|
343
|
+
return { kind: "call", ...mc, callSpan, tagSpan, calls };
|
|
312
344
|
// Degenerate fallback: a callee with a name but no locatable span (a broken
|
|
313
345
|
// tree). Preserve the pre-refactor node shape — nameSpan defaults to tagSpan,
|
|
314
346
|
// args are still extracted. (Unreachable for a well-formed parsed call, where
|
|
315
347
|
// the identifier token always has a span; kept for behavioral parity.)
|
|
316
348
|
const { args, argsSpan } = argInfo(call);
|
|
317
349
|
return {
|
|
318
|
-
kind: "
|
|
350
|
+
kind: "call",
|
|
319
351
|
name: callee.name,
|
|
320
352
|
nameSpan: tagSpan,
|
|
321
353
|
...(callee.packageName !== undefined ? { packageName: callee.packageName } : {}),
|
|
322
354
|
...(callee.packageSpan !== undefined ? { packageSpan: callee.packageSpan } : {}),
|
|
355
|
+
callSpan,
|
|
323
356
|
tagSpan,
|
|
324
357
|
...(argsSpan ? { argsSpan } : {}),
|
|
325
358
|
args,
|
|
326
359
|
calls,
|
|
327
360
|
};
|
|
328
361
|
}
|
|
362
|
+
/** The tag's significant tokens: default channel, no EOF, no `{{`/`}}`/`{%` delimiters. */
|
|
363
|
+
function significantTokens(tokens) {
|
|
364
|
+
const P = MinijinjaParser;
|
|
365
|
+
return tokens.filter((t) => t.channel === AntlrToken.DEFAULT_CHANNEL &&
|
|
366
|
+
t.type !== AntlrToken.EOF &&
|
|
367
|
+
t.type !== P.EXPR_OPEN &&
|
|
368
|
+
t.type !== P.EXPR_CLOSE &&
|
|
369
|
+
t.type !== P.STMT_OPEN);
|
|
370
|
+
}
|
|
371
|
+
/** The INNERMOST still-open CALL paren in `sig` (the call being typed), or undefined when none is
|
|
372
|
+
* open. Every `(` is pushed (a call when the token before it is an `id` / `pkg.id`, else a grouping
|
|
373
|
+
* paren = null) and every `)` pops, so `outer(inner(` reports inner, a call after a keyword
|
|
374
|
+
* (`if is_incremental(`) is found past the leading words, and a grouping paren never masks the call
|
|
375
|
+
* it sits inside. */
|
|
376
|
+
function trailingOpenCall(sig) {
|
|
377
|
+
const P = MinijinjaParser;
|
|
378
|
+
const stack = [];
|
|
379
|
+
for (let i = 0; i < sig.length; i++) {
|
|
380
|
+
const t = sig[i];
|
|
381
|
+
if (t.type === P.LPAREN) {
|
|
382
|
+
const prev = sig[i - 1];
|
|
383
|
+
if (prev?.type === P.ID) {
|
|
384
|
+
const packageTok = sig[i - 2]?.type === P.DOT && sig[i - 3]?.type === P.ID ? sig[i - 3] : undefined;
|
|
385
|
+
stack.push({ nameTok: prev, ...(packageTok ? { packageTok } : {}), lparenIdx: i });
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
stack.push(null); // a grouping paren, not a call
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
else if (t.type === P.RPAREN) {
|
|
392
|
+
stack.pop();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
for (let k = stack.length - 1; k >= 0; k--) {
|
|
396
|
+
const e = stack[k];
|
|
397
|
+
if (e)
|
|
398
|
+
return e;
|
|
399
|
+
}
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
/** The args of an unclosed call: the significant tokens from just after its `(` (index `from`) to the
|
|
403
|
+
* end, split into args by top-level commas. Best-effort per `argFromTokens`. */
|
|
404
|
+
function argsFrom(sig, from) {
|
|
405
|
+
const P = MinijinjaParser;
|
|
406
|
+
const args = [];
|
|
407
|
+
let depth = 0;
|
|
408
|
+
let cur = [];
|
|
409
|
+
for (let i = from; i < sig.length; i++) {
|
|
410
|
+
const t = sig[i];
|
|
411
|
+
if (t.type === P.LPAREN) {
|
|
412
|
+
depth++;
|
|
413
|
+
cur.push(t);
|
|
414
|
+
}
|
|
415
|
+
else if (t.type === P.RPAREN) {
|
|
416
|
+
if (depth === 0)
|
|
417
|
+
break;
|
|
418
|
+
depth--;
|
|
419
|
+
cur.push(t);
|
|
420
|
+
}
|
|
421
|
+
else if (t.type === P.COMMA && depth === 0) {
|
|
422
|
+
if (cur.length > 0)
|
|
423
|
+
args.push(argFromTokens(cur));
|
|
424
|
+
cur = [];
|
|
425
|
+
}
|
|
426
|
+
else {
|
|
427
|
+
cur.push(t);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
if (cur.length > 0)
|
|
431
|
+
args.push(argFromTokens(cur));
|
|
432
|
+
return args;
|
|
433
|
+
}
|
|
434
|
+
/** The reusable MacroCall fields of the innermost unclosed call in `tokens`, or undefined when none is
|
|
435
|
+
* open (a bare name / non-call expr). Shared by the expr-tag node recovery and the control-tag
|
|
436
|
+
* embedded-call recovery. Args are best-effort: a complete STRING is value + quote-excluded span; a
|
|
437
|
+
* partial or computed arg is `value: null` + its span (never fabricated). */
|
|
438
|
+
function openCall(tokens) {
|
|
439
|
+
const sig = significantTokens(tokens);
|
|
440
|
+
const open = trailingOpenCall(sig);
|
|
441
|
+
if (!open)
|
|
442
|
+
return undefined;
|
|
443
|
+
const lparen = sig[open.lparenIdx];
|
|
444
|
+
const last = sig[sig.length - 1];
|
|
445
|
+
return {
|
|
446
|
+
name: open.nameTok.text ?? "",
|
|
447
|
+
nameSpan: spanFromTokens(open.nameTok, open.nameTok),
|
|
448
|
+
...(open.packageTok
|
|
449
|
+
? { packageName: open.packageTok.text ?? "", packageSpan: spanFromTokens(open.packageTok, open.packageTok) }
|
|
450
|
+
: {}),
|
|
451
|
+
argsSpan: spanFromTokens(lparen, last),
|
|
452
|
+
args: argsFrom(sig, open.lparenIdx + 1),
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
/** An UNCLOSED / mid-typing expr-tag call (`{{ ref('cu`, `{{ outer(inner(`) recovered as a neutral
|
|
456
|
+
* "call" node flagged `incomplete: true`, so completion can place the caret's slot. Reports the
|
|
457
|
+
* INNERMOST open call. Undefined when there's no open `id (` (a bare name / non-call expr stays
|
|
458
|
+
* "other"). */
|
|
459
|
+
function incompleteCall(tokens, tagSpan) {
|
|
460
|
+
const c = openCall(tokens);
|
|
461
|
+
if (!c || !c.argsSpan)
|
|
462
|
+
return undefined;
|
|
463
|
+
const from = c.packageSpan ?? c.nameSpan;
|
|
464
|
+
return {
|
|
465
|
+
kind: "call",
|
|
466
|
+
name: c.name,
|
|
467
|
+
nameSpan: c.nameSpan,
|
|
468
|
+
...(c.packageName !== undefined ? { packageName: c.packageName, packageSpan: c.packageSpan } : {}),
|
|
469
|
+
callSpan: {
|
|
470
|
+
start: from.start,
|
|
471
|
+
end: c.argsSpan.end,
|
|
472
|
+
line: from.line,
|
|
473
|
+
column: from.column,
|
|
474
|
+
endLine: c.argsSpan.endLine,
|
|
475
|
+
endColumn: c.argsSpan.endColumn,
|
|
476
|
+
},
|
|
477
|
+
tagSpan,
|
|
478
|
+
argsSpan: c.argsSpan,
|
|
479
|
+
args: c.args,
|
|
480
|
+
calls: [],
|
|
481
|
+
incomplete: true,
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
/** One arg of an incomplete call from its raw tokens: a lone complete STRING → value + quote-excluded
|
|
485
|
+
* span; anything else (a partial/unterminated string, a computed expression) → value null + span. */
|
|
486
|
+
function argFromTokens(toks) {
|
|
487
|
+
const span = spanFromTokens(toks[0], toks[toks.length - 1]);
|
|
488
|
+
if (toks.length === 1 && toks[0].type === MinijinjaParser.STRING) {
|
|
489
|
+
return { value: stringValue(toks[0]), span, valueSpan: stringContentSpan(toks[0]) };
|
|
490
|
+
}
|
|
491
|
+
return { value: null, span };
|
|
492
|
+
}
|
|
329
493
|
/** Keywords whose stmt declares a name we surface (set target / macro name / for loop var). */
|
|
330
494
|
const NAME_DECLARING = new Set(["set", "macro", "for"]);
|
|
331
495
|
/**
|
|
@@ -371,7 +535,7 @@ function controlNode(tree, tagSpan) {
|
|
|
371
535
|
* offset/anchor composition anywhere in this file). Returns undefined only for an
|
|
372
536
|
* empty/degenerate tree (never throws).
|
|
373
537
|
*/
|
|
374
|
-
export function tagNodesOf(seg, tree) {
|
|
538
|
+
export function tagNodesOf(seg, tree, tokens = []) {
|
|
375
539
|
// tagSpan is the whole tag including delimiters — the segment bounds are exact
|
|
376
540
|
// (they cover `{{ … }}` / `{% … %}` / `{# … #}` and any `-` whitespace control);
|
|
377
541
|
// its line/column come from the tree's own start token (the tag's OPEN token).
|
|
@@ -399,70 +563,26 @@ export function tagNodesOf(seg, tree) {
|
|
|
399
563
|
const selfKeyword = selfContainedKeyword(tree);
|
|
400
564
|
if (selfKeyword)
|
|
401
565
|
return { kind: "control", tagSpan, keyword: selfKeyword, calls: [] };
|
|
402
|
-
|
|
566
|
+
const node = controlNode(tree, tagSpan);
|
|
567
|
+
// Recover a trailing UNCLOSED call being typed (`{% if is_incremental(`), which the tree parse
|
|
568
|
+
// could not build, and add it to `calls` so completion can place the caret's slot inside it.
|
|
569
|
+
const partial = openCall(tokens);
|
|
570
|
+
return partial && node.kind === "control" ? { ...node, calls: [...node.calls, partial] } : node;
|
|
403
571
|
}
|
|
404
572
|
if (seg.tagKind === "comment")
|
|
405
573
|
return { kind: "other", tagSpan };
|
|
406
574
|
// Expr tag: classify by the leading call.
|
|
407
575
|
const call = findTopCall(tree);
|
|
408
|
-
if (!call)
|
|
409
|
-
|
|
576
|
+
if (!call) {
|
|
577
|
+
// No complete call node: recover an UNCLOSED/mid-typing call from the raw tokens
|
|
578
|
+
// (`{{ ref('cu`) so completion can place the caret's slot. A bare non-call expr stays "other".
|
|
579
|
+
return incompleteCall(tokens, tagSpan) ?? { kind: "other", tagSpan };
|
|
580
|
+
}
|
|
410
581
|
const callee = decomposeCallee(call);
|
|
411
582
|
if (!callee)
|
|
412
583
|
return { kind: "other", tagSpan };
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
//
|
|
416
|
-
if (bare && leading === "ref") {
|
|
417
|
-
// The model is the LAST positional arg (dbt: ref('pkg','model')), and only
|
|
418
|
-
// when THAT arg is a direct string literal — a computed target
|
|
419
|
-
// (`ref(var('x'))`) does not fabricate a model.
|
|
420
|
-
const pos = positionalArgs(call.arg_list());
|
|
421
|
-
const modelArg = pos.at(-1);
|
|
422
|
-
const modelTok = modelArg ? directStringToken(modelArg) : undefined;
|
|
423
|
-
if (modelTok) {
|
|
424
|
-
const callSpan = spanOfNode(call) ?? tagSpan;
|
|
425
|
-
return {
|
|
426
|
-
kind: "ref",
|
|
427
|
-
model: stringValue(modelTok),
|
|
428
|
-
modelSpan: stringContentSpan(modelTok),
|
|
429
|
-
callSpan,
|
|
430
|
-
tagSpan,
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
// computed / broken / argless ref → macro fallback (never a fabricated model).
|
|
434
|
-
return macroNode(call, callee, tagSpan, tree);
|
|
435
|
-
}
|
|
436
|
-
if (bare && leading === "source") {
|
|
437
|
-
// source('name', 'table') — both must be DIRECT string literals; a computed
|
|
438
|
-
// arg does not fabricate a name/table.
|
|
439
|
-
const pos = positionalArgs(call.arg_list());
|
|
440
|
-
const srcTok = pos[0] ? directStringToken(pos[0]) : undefined;
|
|
441
|
-
const tblTok = pos[1] ? directStringToken(pos[1]) : undefined;
|
|
442
|
-
if (srcTok && tblTok) {
|
|
443
|
-
const callSpan = spanOfNode(call) ?? tagSpan;
|
|
444
|
-
return {
|
|
445
|
-
kind: "source",
|
|
446
|
-
sourceName: stringValue(srcTok),
|
|
447
|
-
tableName: stringValue(tblTok),
|
|
448
|
-
sourceNameSpan: stringContentSpan(srcTok),
|
|
449
|
-
tableNameSpan: stringContentSpan(tblTok),
|
|
450
|
-
callSpan,
|
|
451
|
-
tagSpan,
|
|
452
|
-
};
|
|
453
|
-
}
|
|
454
|
-
return macroNode(call, callee, tagSpan, tree);
|
|
455
|
-
}
|
|
456
|
-
if (bare && leading === "var")
|
|
457
|
-
return { kind: "var", tagSpan };
|
|
458
|
-
if (bare && leading === "env_var")
|
|
459
|
-
return { kind: "env_var", tagSpan };
|
|
460
|
-
// No-output builtins (config/docs/print/log/return/exceptions) — config is its
|
|
461
|
-
// own kind; the rest map to "other". Applies to the leading name whether bare
|
|
462
|
-
// (`config(...)`) or member (`exceptions.raise_compiler_error(...)`).
|
|
463
|
-
if (NO_OUTPUT_BUILTINS.has(leading)) {
|
|
464
|
-
return leading === "config" ? { kind: "config", tagSpan } : { kind: "other", tagSpan };
|
|
465
|
-
}
|
|
466
|
-
// pkg.macro(...) or a bare unknown call → macro.
|
|
584
|
+
// Every expression-call tag is one neutral CALL node. ref/source/config/var/env_var are just
|
|
585
|
+
// callees distinguished by `name`; the provider (DbtTemplateProvider) interprets what each call
|
|
586
|
+
// MEANS (relation / no-output / scalar). The tag AST carries no dbt vocabulary.
|
|
467
587
|
return macroNode(call, callee, tagSpan, tree);
|
|
468
588
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// The mysql DialectBehavior: everything the semantic layer needs for mysql, assembled from
|
|
2
|
+
// this folder's own pieces. The registry wires it; nothing here reaches a central per-dialect table.
|
|
3
|
+
import { mysqlLiteral, mysqlParseType, MYSQL_FUNCTION_RETURNS } from "./infer.js";
|
|
4
|
+
import { displayName, fold, foldTableName, matchesSourceKey } from "./fold.js";
|
|
5
|
+
import { likePatternToRegExp } from "../scope/like-pattern.js";
|
|
6
|
+
import { SIGNATURES } from "../signature/signatures.js";
|
|
7
|
+
import { acceptsFor } from "../dialect-behavior/coerce-rules.js";
|
|
8
|
+
export const mysqlBehavior = {
|
|
9
|
+
fold,
|
|
10
|
+
displayName,
|
|
11
|
+
foldTableName,
|
|
12
|
+
matchesSourceKey,
|
|
13
|
+
likeMatch: (pattern, value) => likePatternToRegExp(pattern).test(value),
|
|
14
|
+
literal: mysqlLiteral,
|
|
15
|
+
parseType: mysqlParseType,
|
|
16
|
+
functions: MYSQL_FUNCTION_RETURNS,
|
|
17
|
+
division: "decimal",
|
|
18
|
+
signatures: SIGNATURES.mysql,
|
|
19
|
+
// STR_TO_NUM=true, BOOL_NUM=true: mysql implicitly coerces both string->number and bool<->number.
|
|
20
|
+
accepts: (argType, paramText) => acceptsFor(mysqlParseType, true, true, argType, paramText),
|
|
21
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FoldRule, type IdentKind } from "../ident/fold.js";
|
|
2
|
+
export declare const MYSQL_FOLD_RULE: FoldRule;
|
|
3
|
+
/** Fold an identifier to its MySQL identity key. */
|
|
4
|
+
export declare function fold(raw: string, kind?: IdentKind): string;
|
|
5
|
+
/** Presentation twin: strip delimiters, no case change. */
|
|
6
|
+
export declare function displayName(raw: string): string;
|
|
7
|
+
export declare function foldTableName(parts: string[]): string[];
|
|
8
|
+
export declare function matchesSourceKey(key: string, rawPart: string): boolean;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// MySQL identifier folding. The FoldRule plus its bound engine, colocated here because BOTH the
|
|
2
|
+
// upstream lower() and the downstream DialectBehavior need it (the fold rule is the one dialect concern
|
|
3
|
+
// used at two stages).
|
|
4
|
+
//
|
|
5
|
+
// dev.mysql.com/doc/refman/8.4/en/identifier-case-sensitivity.html — verified live. This module's
|
|
6
|
+
// fold rule is per-DIALECT, not per-identifier-KIND (only BigQuery's `tableCase` override draws
|
|
7
|
+
// that distinction), so one rule must cover both halves of a genuinely split MySQL reality:
|
|
8
|
+
// "Partition, subpartition, column, index, stored routine, event, and resource group names are
|
|
9
|
+
// not case-sensitive on any platform, nor are column aliases" — unconditional, so this module
|
|
10
|
+
// folds unquoted AND backtick-quoted identifiers to lower for equality (column/alias/CTE/field
|
|
11
|
+
// names, the identifier kinds this module actually threads through scope/qualify/infer today).
|
|
12
|
+
// KNOWN LIMITATION (documented here, not silently dropped): table/database names are genuinely
|
|
13
|
+
// platform/`lower_case_table_names`-dependent — "case sensitivity of the underlying operating
|
|
14
|
+
// system plays a part": case-SENSITIVE by default on Unix (lower_case_table_names=0), case-
|
|
15
|
+
// INSENSITIVE by default on Windows (=1) and macOS (=2), and the variable "can only be
|
|
16
|
+
// configured when initializing the server" (not discoverable from SQL text alone). A static
|
|
17
|
+
// analyzer with no server/OS context cannot resolve this per-installation choice; this module
|
|
18
|
+
// applies the same lower-fold to table identifiers too (foldTableName's kind:"table" path, since
|
|
19
|
+
// no `tableCase` override is set here) — correct for the Windows/macOS default and for Unix
|
|
20
|
+
// installs whose tables happen to be written in a single consistent case (the common case), WRONG
|
|
21
|
+
// only when a Unix-collation database genuinely holds two tables differing only in case (e.g.
|
|
22
|
+
// `Orders` and `orders` as distinct tables) — a real but narrow misresolution, called out here
|
|
23
|
+
// rather than left for a caller to discover by surprise. Identifier quote char is the backtick by
|
|
24
|
+
// default (ANSI_QUOTES mode, which repurposes `"` as the identifier quote and `` ` `` stops being
|
|
25
|
+
// special, is a session/server SQL-mode setting not visible in the SQL text either — out of
|
|
26
|
+
// scope for the same reason as lower_case_table_names). Doubled-quote escape: "To include a quote
|
|
27
|
+
// character within an identifier, quote the identifier and double the quote character" (example:
|
|
28
|
+
// `` `a``b` `` → `` a`b ``).
|
|
29
|
+
import { displayWith, foldWith } from "../ident/fold.js";
|
|
30
|
+
const BACKTICK = ["`", "`"];
|
|
31
|
+
export const MYSQL_FOLD_RULE = {
|
|
32
|
+
delimiters: [BACKTICK],
|
|
33
|
+
unquoted: "lower",
|
|
34
|
+
quoted: "lower",
|
|
35
|
+
};
|
|
36
|
+
/** Fold an identifier to its MySQL identity key. */
|
|
37
|
+
export function fold(raw, kind = "other") {
|
|
38
|
+
return foldWith(MYSQL_FOLD_RULE, raw, kind);
|
|
39
|
+
}
|
|
40
|
+
/** Presentation twin: strip delimiters, no case change. */
|
|
41
|
+
export function displayName(raw) {
|
|
42
|
+
return displayWith(MYSQL_FOLD_RULE, raw);
|
|
43
|
+
}
|
|
44
|
+
export function foldTableName(parts) {
|
|
45
|
+
return parts.map((p) => fold(p, "table"));
|
|
46
|
+
}
|
|
47
|
+
export function matchesSourceKey(key, rawPart) {
|
|
48
|
+
return key === fold(rawPart) || key === fold(rawPart, "table");
|
|
49
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The complete mysql dialect module: parse + lower (front end) and behavior (semantic knowledge).
|
|
2
|
+
// The registry wires this; to understand everything sqllens does for mysql, read this folder.
|
|
3
|
+
import { mysqlBehavior } from "./behavior.js";
|
|
4
|
+
import { lower } from "./lower.js";
|
|
5
|
+
import { parseMysql } from "./parse.js";
|
|
6
|
+
export const mysql = {
|
|
7
|
+
parse: parseMysql,
|
|
8
|
+
lower,
|
|
9
|
+
behavior: mysqlBehavior,
|
|
10
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { type Type } from "
|
|
2
|
-
import type { FnRule } from "
|
|
1
|
+
import { type Type } from "../infer/types.js";
|
|
2
|
+
import type { FnRule } from "../infer/functions.js";
|
|
3
3
|
export declare const MYSQL_ALIASES: Record<string, string>;
|
|
4
4
|
export declare function mysqlParseType(text: string): Type;
|
|
5
5
|
/** MySQL literal forms (dev.mysql.com/doc/refman/8.4/en/literals.html). Numerics split on MySQL's
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { parseType, scalar, UNKNOWN } from "
|
|
1
|
+
import { parseType, scalar, UNKNOWN } from "../infer/types.js";
|
|
2
|
+
import { fold } from "./fold.js";
|
|
2
3
|
// ---------------------------------------------------------------------------
|
|
3
4
|
// MySQL inference knowledge — function return types, literal forms, and scalar-type aliases,
|
|
4
5
|
// from the MySQL 8.4 Reference Manual (dev.mysql.com/doc/refman/8.4/en/). Same contract as every
|
|
@@ -58,7 +59,7 @@ export const MYSQL_ALIASES = {
|
|
|
58
59
|
unsignedint: "bigint",
|
|
59
60
|
};
|
|
60
61
|
export function mysqlParseType(text) {
|
|
61
|
-
return parseType(text, MYSQL_ALIASES,
|
|
62
|
+
return parseType(text, MYSQL_ALIASES, fold);
|
|
62
63
|
}
|
|
63
64
|
const S = scalar("string");
|
|
64
65
|
const BIG = scalar("bigint");
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FnSignature } from "../signature/signatures.js";
|
|
2
|
+
/** The merged function-signature table for mysql: curated overrides folded over the harvested
|
|
3
|
+
* doc-derived long tail (overrides win by key, replacing the whole overload set), keyed by
|
|
4
|
+
* lowercased name. Each name maps to an ORDERED overload set - a name with one documented shape
|
|
5
|
+
* is a one-element array. `origin` says which layer produced the set. */
|
|
6
|
+
export declare const MYSQL_SIGNATURES: Record<string, FnSignature[]>;
|