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
package/dist/infer/infer.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { behaviorOf } from "../dialect-behavior/carrier.js";
|
|
2
|
+
import { resolveBehavior } from "../dialect-behavior/registry.js";
|
|
2
3
|
import { asProvider, tableSourceColumns } from "../qualify/relation-columns.js";
|
|
3
|
-
import {
|
|
4
|
+
import { resolveScopes } from "../scope/scope.js";
|
|
4
5
|
import { resolveColumnSource } from "../sema/resolve.js";
|
|
5
6
|
import { coerce, commonType } from "./coerce.js";
|
|
6
|
-
import { inferDialect } from "./dialect.js";
|
|
7
7
|
import { parseType, scalar, UNKNOWN } from "./types.js";
|
|
8
8
|
const BOOLEAN = scalar("boolean");
|
|
9
9
|
const INT = scalar("int");
|
|
10
10
|
const freshCtx = () => ({ seen: new Set(), env: new Map() });
|
|
11
11
|
export function inferType(expr, scope, schema, ctx = freshCtx()) {
|
|
12
|
-
const d =
|
|
12
|
+
const d = behaviorOf(scope);
|
|
13
13
|
switch (expr.kind) {
|
|
14
14
|
case "literal":
|
|
15
15
|
return d.literal(expr.text);
|
|
@@ -74,14 +74,14 @@ function columnType(col, scope, schema, ctx) {
|
|
|
74
74
|
return v ? VALUE_TYPES[v.type] : UNKNOWN;
|
|
75
75
|
}
|
|
76
76
|
if (col.parts.length === 1) {
|
|
77
|
-
const param = ctx.env.get(
|
|
77
|
+
const param = ctx.env.get(behaviorOf(scope).fold(col.parts[0])); // a lambda parameter shadows columns
|
|
78
78
|
if (param)
|
|
79
79
|
return param;
|
|
80
80
|
}
|
|
81
81
|
const found = resolveColumnSource(scope, col.parts, schema);
|
|
82
82
|
if (!found)
|
|
83
83
|
return UNKNOWN;
|
|
84
|
-
const base = sourceColumnType(found.source, found.column, schema, ctx,
|
|
84
|
+
const base = sourceColumnType(found.source, found.column, schema, ctx, behaviorOf(scope), scope.dialect);
|
|
85
85
|
return found.fields.length ? fieldType(base, found.fields, scope.dialect) : base;
|
|
86
86
|
}
|
|
87
87
|
function sourceColumnType(src, column, schema, ctx, d, dialect) {
|
|
@@ -139,7 +139,7 @@ function starPassthroughType(child, column, schema, ctx) {
|
|
|
139
139
|
continue; // renamed away
|
|
140
140
|
if (star.exclude?.some((e) => eq(e, under, d)))
|
|
141
141
|
continue;
|
|
142
|
-
if (star.ilike !== undefined && !
|
|
142
|
+
if (star.ilike !== undefined && !resolveBehavior(d).likeMatch(star.ilike, resolveBehavior(d).fold(under)))
|
|
143
143
|
continue;
|
|
144
144
|
ctx.seen.add(child);
|
|
145
145
|
const replaced = star.replace?.find((r) => eq(r.column, under, d));
|
|
@@ -159,7 +159,7 @@ function fieldType(type, fields, dialect) {
|
|
|
159
159
|
if (t.kind !== "struct")
|
|
160
160
|
return UNKNOWN;
|
|
161
161
|
// Struct-field names on a Type are stored FOLDED — fold only the reference side.
|
|
162
|
-
const hit = t.fields.find((ff) => ff.name ===
|
|
162
|
+
const hit = t.fields.find((ff) => ff.name === resolveBehavior(dialect).fold(f));
|
|
163
163
|
if (!hit)
|
|
164
164
|
return UNKNOWN;
|
|
165
165
|
t = hit.type;
|
|
@@ -170,7 +170,7 @@ function fieldType(type, fields, dialect) {
|
|
|
170
170
|
function functionType(fn, scope, schema, ctx) {
|
|
171
171
|
const name = fn.name.toLowerCase();
|
|
172
172
|
const args = fn.args;
|
|
173
|
-
const d =
|
|
173
|
+
const d = behaviorOf(scope);
|
|
174
174
|
// Higher-order and constructor forms are BARE-name Spark/GoogleSQL builtins; a qualified/dotted
|
|
175
175
|
// call (e.g. a `dataset.transform(...)` UDF) must not borrow them.
|
|
176
176
|
if (fn.qualifier === undefined) {
|
|
@@ -255,7 +255,7 @@ function lambdaResult(lambda, paramTypes, scope, schema, ctx) {
|
|
|
255
255
|
if (lambda?.kind !== "lambda")
|
|
256
256
|
return undefined;
|
|
257
257
|
const env = new Map(ctx.env);
|
|
258
|
-
lambda.params.forEach((p, i) => env.set(
|
|
258
|
+
lambda.params.forEach((p, i) => env.set(behaviorOf(scope).fold(p), paramTypes[i] ?? UNKNOWN));
|
|
259
259
|
return inferType(lambda.body, scope, schema, { seen: ctx.seen, env });
|
|
260
260
|
}
|
|
261
261
|
function arrayElem(t) {
|
|
@@ -275,7 +275,7 @@ function constructor(name, args, scope, schema, ctx) {
|
|
|
275
275
|
const key = args[i];
|
|
276
276
|
const fname = key.kind === "literal" ? stringValue(key.text) : `col${i / 2 + 1}`;
|
|
277
277
|
fields.push({
|
|
278
|
-
name:
|
|
278
|
+
name: behaviorOf(scope).fold(fname),
|
|
279
279
|
type: inferType(args[i + 1], scope, schema, ctx),
|
|
280
280
|
});
|
|
281
281
|
}
|
|
@@ -285,14 +285,16 @@ function constructor(name, args, scope, schema, ctx) {
|
|
|
285
285
|
return {
|
|
286
286
|
kind: "struct",
|
|
287
287
|
fields: args.map((a, i) => ({
|
|
288
|
-
name: a.kind === "column" ?
|
|
288
|
+
name: a.kind === "column" ? behaviorOf(scope).fold(a.parts[a.parts.length - 1]) : `col${i + 1}`,
|
|
289
289
|
type: inferType(a, scope, schema, ctx),
|
|
290
290
|
})),
|
|
291
291
|
};
|
|
292
292
|
}
|
|
293
293
|
if (name === "from_json") {
|
|
294
294
|
const s = args[1];
|
|
295
|
-
return s?.kind === "literal"
|
|
295
|
+
return s?.kind === "literal"
|
|
296
|
+
? parseType(stringValue(s.text), undefined, (n) => behaviorOf(scope).fold(n))
|
|
297
|
+
: UNKNOWN;
|
|
296
298
|
}
|
|
297
299
|
return undefined;
|
|
298
300
|
}
|
|
@@ -352,5 +354,5 @@ function stringValue(text) {
|
|
|
352
354
|
return quoted ? t.slice(1, -1) : t;
|
|
353
355
|
}
|
|
354
356
|
function eq(a, b, dialect) {
|
|
355
|
-
return
|
|
357
|
+
return resolveBehavior(dialect).fold(a) === resolveBehavior(dialect).fold(b);
|
|
356
358
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { resolveBehavior } from "../dialect-behavior/registry.js";
|
|
2
2
|
import { tableSourceColumns } from "../qualify/relation-columns.js";
|
|
3
|
-
import { likePatternToRegExp } from "../scope/scope.js";
|
|
4
3
|
import { resolveColumnSource } from "../sema/resolve.js";
|
|
5
4
|
const freshCtx = () => ({ seen: new Set() });
|
|
6
5
|
export function inferNullability(expr, scope, schema, ctx = freshCtx()) {
|
|
@@ -118,7 +117,7 @@ function starPassthroughNullability(child, column, schema, ctx) {
|
|
|
118
117
|
continue; // renamed away
|
|
119
118
|
if (star.exclude?.some((e) => eq(e, under, d)))
|
|
120
119
|
continue;
|
|
121
|
-
if (star.ilike !== undefined && !
|
|
120
|
+
if (star.ilike !== undefined && !resolveBehavior(d).likeMatch(star.ilike, resolveBehavior(d).fold(under)))
|
|
122
121
|
continue;
|
|
123
122
|
ctx.seen.add(child);
|
|
124
123
|
const replaced = star.replace?.find((r) => eq(r.column, under, d));
|
|
@@ -283,5 +282,5 @@ function functionNullability(fn, scope, schema, ctx) {
|
|
|
283
282
|
}
|
|
284
283
|
// --- helpers ---------------------------------------------------------------
|
|
285
284
|
function eq(a, b, dialect) {
|
|
286
|
-
return
|
|
285
|
+
return resolveBehavior(dialect).fold(a) === resolveBehavior(dialect).fold(b);
|
|
287
286
|
}
|
package/dist/infer/types.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare function scalar(name: string): Type;
|
|
|
26
26
|
* T-SQL, e.g. bit→boolean, datetime→timestamp, nvarchar→string). `dialect` folds struct field
|
|
27
27
|
* names with that dialect's identifier rules (fields are stored folded — identity keys); absent,
|
|
28
28
|
* the default fold (backtick-strip + lower) reproduces the legacy behavior. */
|
|
29
|
-
export declare function parseType(text: string, aliases?: Record<string, string>,
|
|
29
|
+
export declare function parseType(text: string, aliases?: Record<string, string>, foldField?: (name: string) => string): Type;
|
|
30
30
|
/** Spark/Databricks scalar type aliases → the shared canonical names (also the default table for
|
|
31
31
|
* `parseType` below). Exported so `src/dialect-symbols.ts` can build the databricks `types` set from
|
|
32
32
|
* it without duplicating the table. */
|
package/dist/infer/types.js
CHANGED
|
@@ -8,7 +8,6 @@
|
|
|
8
8
|
// identity keys. Comparisons against a raw reference fold only the reference side; re-folding a
|
|
9
9
|
// stored name would corrupt a preserved-case (quoted snowflake/postgres) field.
|
|
10
10
|
// ---------------------------------------------------------------------------
|
|
11
|
-
import { foldIdentifier } from "../ident/fold.js";
|
|
12
11
|
export const UNKNOWN = { kind: "unknown" };
|
|
13
12
|
/** A scalar type by canonical name (after alias normalisation). */
|
|
14
13
|
export function scalar(name) {
|
|
@@ -19,20 +18,20 @@ export function scalar(name) {
|
|
|
19
18
|
* T-SQL, e.g. bit→boolean, datetime→timestamp, nvarchar→string). `dialect` folds struct field
|
|
20
19
|
* names with that dialect's identifier rules (fields are stored folded — identity keys); absent,
|
|
21
20
|
* the default fold (backtick-strip + lower) reproduces the legacy behavior. */
|
|
22
|
-
export function parseType(text, aliases = SCALAR_ALIASES,
|
|
21
|
+
export function parseType(text, aliases = SCALAR_ALIASES, foldField) {
|
|
23
22
|
const s = text.trim();
|
|
24
23
|
if (s === "")
|
|
25
24
|
return UNKNOWN;
|
|
26
25
|
const array = /^array\s*<(.*)>$/is.exec(s);
|
|
27
26
|
if (array)
|
|
28
|
-
return { kind: "array", element: parseType(array[1], undefined,
|
|
27
|
+
return { kind: "array", element: parseType(array[1], undefined, foldField) };
|
|
29
28
|
const map = /^map\s*<(.*)>$/is.exec(s);
|
|
30
29
|
if (map) {
|
|
31
30
|
const [key, value] = splitTopLevel(map[1]);
|
|
32
31
|
return {
|
|
33
32
|
kind: "map",
|
|
34
|
-
key: parseType(key ?? "", undefined,
|
|
35
|
-
value: parseType(value ?? "", undefined,
|
|
33
|
+
key: parseType(key ?? "", undefined, foldField),
|
|
34
|
+
value: parseType(value ?? "", undefined, foldField),
|
|
36
35
|
};
|
|
37
36
|
}
|
|
38
37
|
const struct = /^struct\s*<(.*)>$/is.exec(s);
|
|
@@ -42,9 +41,10 @@ export function parseType(text, aliases = SCALAR_ALIASES, dialect) {
|
|
|
42
41
|
const colon = topLevelColon(part);
|
|
43
42
|
if (colon < 0)
|
|
44
43
|
continue;
|
|
45
|
-
const
|
|
44
|
+
const raw = part.slice(0, colon).trim();
|
|
45
|
+
const name = foldField ? foldField(raw) : raw;
|
|
46
46
|
if (name) {
|
|
47
|
-
fields.push({ name, type: parseType(stripComment(part.slice(colon + 1).trim()), undefined,
|
|
47
|
+
fields.push({ name, type: parseType(stripComment(part.slice(colon + 1).trim()), undefined, foldField) });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
return { kind: "struct", fields };
|
package/dist/ir/ir.d.ts
CHANGED
|
@@ -118,6 +118,11 @@ export interface PivotInfo {
|
|
|
118
118
|
/** The pivoted relation's alias (T-SQL `… PIVOT (…) AS pvt`), referenced by later columns.
|
|
119
119
|
* Absent for Spark, where the pivot transforms the SELECT directly. */
|
|
120
120
|
alias?: string;
|
|
121
|
+
/** True when the output columns are data-dependent and cannot be enumerated statically (DuckDB's
|
|
122
|
+
* statement `PIVOT … ON … USING` with no fixed IN-list — the distinct ON-values become columns).
|
|
123
|
+
* The reshape is still modelled (source + consumed columns visible); downstream resolves the output
|
|
124
|
+
* to "unknown" rather than guessing a column set. */
|
|
125
|
+
dynamic?: boolean;
|
|
121
126
|
}
|
|
122
127
|
export interface UnpivotInfo {
|
|
123
128
|
/** The value column the unpivot produces. */
|
|
@@ -612,28 +617,32 @@ export interface LateralViewSource {
|
|
|
612
617
|
pseudo?: true;
|
|
613
618
|
cst: ParserRuleContext;
|
|
614
619
|
}
|
|
615
|
-
/** Present when a source was written as a minijinja template tag
|
|
616
|
-
* a macro
|
|
617
|
-
* plain SQL parses never carry it.
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
621
|
-
*
|
|
620
|
+
/** Present when a source was written as a minijinja template tag: a call in a FROM slot
|
|
621
|
+
* ({{ ref('x') }} / {{ source('a','b') }} / a macro), or a non-call expression. Attached post-lower by
|
|
622
|
+
* the jinja front end (src/minijinja/apply-tags.ts); plain SQL parses never carry it. When the
|
|
623
|
+
* TemplateProvider resolves the call's relation, `name` carries the resolved name parts and the
|
|
624
|
+
* source's columns bind through the provider. When it does not (the neutral provider, a plain macro, a
|
|
625
|
+
* computed call), `name` stays the raw placeholder and the source is an opaque relation (no
|
|
626
|
+
* unknown-table/-column diagnostics). The tag AST and this marker are dbt-NEUTRAL: ref vs source is not
|
|
627
|
+
* a stored kind but the callee name on `call` (call.name === "ref"), resolved by the provider the same
|
|
628
|
+
* way a SQL function's type comes from a dialect registry. This type is IR-neutral (no import from
|
|
629
|
+
* src/minijinja); consumers needing the full TagNode correlate by `span` with parseTemplated().tags. */
|
|
622
630
|
export interface TemplateSourceInfo {
|
|
623
|
-
/** `"
|
|
624
|
-
*
|
|
625
|
-
*
|
|
626
|
-
|
|
631
|
+
/** `"call"` = a template call occupies the slot; its `call` is the provider key that names the
|
|
632
|
+
* source and types its columns. `"expr"` = a non-call templated expression (a bare variable
|
|
633
|
+
* `{{ t }}`, a concat `{{ a ~ b }}`) that carries no call, so it stays opaque and `name` is the
|
|
634
|
+
* raw placeholder. */
|
|
635
|
+
kind: "call" | "expr";
|
|
627
636
|
/** The whole tag's span ({{ … }} inclusive), document coordinates. */
|
|
628
637
|
span: PartSpan;
|
|
629
638
|
opaque?: true;
|
|
630
|
-
/** Present when a bare `{{ t }}` resolved through a literal `{% set t = ref('x') %}`: `
|
|
631
|
-
* carry the resolved
|
|
632
|
-
*
|
|
639
|
+
/** Present when a bare `{{ t }}` resolved through a literal `{% set t = ref('x') %}`: `name`/`call`
|
|
640
|
+
* carry the resolved relation, but the TagNode at `span` is the USE site (kind "other"), not the
|
|
641
|
+
* call node, so consumers correlating span to TagNode must not expect a call node here. */
|
|
633
642
|
indirect?: true;
|
|
634
|
-
/** The provider key for this source's tag (
|
|
635
|
-
* the semantic layer resolves the source through `TemplateProvider.expansion(call)`. Absent only
|
|
636
|
-
*
|
|
643
|
+
/** The provider key for this source's tag (any call in a FROM slot, and resolved set-indirection):
|
|
644
|
+
* the semantic layer resolves the source through `TemplateProvider.expansion(call)`. Absent only on
|
|
645
|
+
* the opaque `"expr"` kind (no call to ask about). */
|
|
637
646
|
call?: TemplateCall;
|
|
638
647
|
}
|
|
639
648
|
export interface TableSource {
|
package/dist/ir/span.js
CHANGED
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
// START line/column; when the last token's TEXT spans newlines (a multi-line string literal,
|
|
3
3
|
// dollar-quoted body, or block comment), the end position must advance the line and reset the
|
|
4
4
|
// column. This is the one place that does that math so symbols.ts and qualify.ts agree.
|
|
5
|
-
// (src/lsp/ranges.ts works on an antlr Token directly and carries an equivalent fix of its own —
|
|
6
|
-
// it cannot import from the application layer the other way around.)
|
|
7
5
|
/**
|
|
8
6
|
* End position (exclusive) of a token, given its 1-based start `line`, 0-based start `column`, and
|
|
9
7
|
* `text`. For a single-line token this is the start line and `column + text.length`. For a token
|
package/dist/ir/walk.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// ---------------------------------------------------------------------------
|
|
2
2
|
// Shared IR walk helpers — expr/query-tree traversal with no scope/document
|
|
3
|
-
// knowledge, so
|
|
4
|
-
//
|
|
5
|
-
//
|
|
3
|
+
// knowledge, so src/document/node-at.ts (offset → smallest covering Expr) and
|
|
4
|
+
// any other IR-only consumer single-source the same walk instead of carrying
|
|
5
|
+
// byte-for-byte duplicates.
|
|
6
6
|
// ---------------------------------------------------------------------------
|
|
7
7
|
/** Every QueryExpr reachable in the IR (the AST and its nested query blocks). */
|
|
8
8
|
export function allQueryExprs(root) {
|
package/dist/lineage/hops.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { behaviorOf } from "../dialect-behavior/carrier.js";
|
|
2
|
+
import { resolveBehavior } from "../dialect-behavior/registry.js";
|
|
2
3
|
import { OPEN_PROVIDER } from "../qualify/template-provider.js";
|
|
3
4
|
import { nodeAt } from "../document/node-at.js";
|
|
4
5
|
import { findProducerProjection, resolveColumnSource } from "../sema/resolve.js";
|
|
@@ -129,7 +130,7 @@ function followColumn(scope, parts, walk, trail = []) {
|
|
|
129
130
|
const { source, column } = binding;
|
|
130
131
|
if (source.kind === "table") {
|
|
131
132
|
// A recursive CTE's self-reference is a plain table here (see Walk.activeCtes) — cycle-guard it.
|
|
132
|
-
if (source.name.length === 1 && walk.activeCtes.has(
|
|
133
|
+
if (source.name.length === 1 && walk.activeCtes.has(behaviorOf(scope).fold(source.name[0])))
|
|
133
134
|
return [{ kind: "unresolved", via }];
|
|
134
135
|
return [{ kind: "origin", origin: { table: source.name, column }, via }];
|
|
135
136
|
}
|
|
@@ -141,7 +142,7 @@ function followColumn(scope, parts, walk, trail = []) {
|
|
|
141
142
|
}
|
|
142
143
|
if (walk.seen.has(child))
|
|
143
144
|
return [{ kind: "unresolved", via }]; // recursive-CTE cycle guard
|
|
144
|
-
const cteName = source.kind === "cte" ?
|
|
145
|
+
const cteName = source.kind === "cte" ? behaviorOf(scope).fold(source.ref.def.name) : undefined;
|
|
145
146
|
walk.seen.add(child);
|
|
146
147
|
if (cteName)
|
|
147
148
|
walk.activeCtes.add(cteName);
|
|
@@ -195,11 +196,12 @@ function forkLegs(setopScope, column, aliases, dialect, walk, trail = []) {
|
|
|
195
196
|
/** The position of `column` in a set-op's output: via declared CTE aliases if given, else the set-op's
|
|
196
197
|
* own output names (the left branch's, positionally). -1 when not determinable (falls back to name). */
|
|
197
198
|
function columnIndex(setopScope, column, aliases, dialect) {
|
|
198
|
-
const
|
|
199
|
+
const bh = resolveBehavior(dialect);
|
|
200
|
+
const want = bh.fold(column);
|
|
199
201
|
if (aliases)
|
|
200
|
-
return aliases.findIndex((a) =>
|
|
202
|
+
return aliases.findIndex((a) => bh.fold(a) === want);
|
|
201
203
|
const outs = setopScope.outputs;
|
|
202
|
-
return outs !== "unknown" ? outs.findIndex((o) =>
|
|
204
|
+
return outs !== "unknown" ? outs.findIndex((o) => bh.fold(o) === want) : -1;
|
|
203
205
|
}
|
|
204
206
|
/** The projection producing `column` in one set-op leg — by name for BY NAME, else by position with a
|
|
205
207
|
* name fallback (positional legs can differ in column name: `SELECT a AS x … UNION SELECT b …`). */
|
|
@@ -207,9 +209,9 @@ function legProducer(leg, column, idx, byName, dialect) {
|
|
|
207
209
|
if (leg.body.kind !== "select")
|
|
208
210
|
return undefined;
|
|
209
211
|
const projs = leg.body.projections;
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
212
|
+
const bh = resolveBehavior(dialect);
|
|
213
|
+
const wantName = bh.fold(column);
|
|
214
|
+
const byNameHit = () => projs.find((p) => !p.isStar && p.name !== undefined && bh.fold(p.name) === wantName);
|
|
213
215
|
if (byName)
|
|
214
216
|
return byNameHit();
|
|
215
217
|
if (idx >= 0 && idx < projs.length && !projs[idx].isStar)
|
|
@@ -407,8 +409,9 @@ function childExprs(expr) {
|
|
|
407
409
|
}
|
|
408
410
|
}
|
|
409
411
|
function dedupOrigins(origins, dialect) {
|
|
412
|
+
const b = resolveBehavior(dialect);
|
|
410
413
|
const by = new Map();
|
|
411
414
|
for (const o of origins)
|
|
412
|
-
by.set(`${foldTableName(o.table
|
|
415
|
+
by.set(`${b.foldTableName(o.table).join(".")}.${b.fold(o.column)}`, o);
|
|
413
416
|
return [...by.values()];
|
|
414
417
|
}
|
package/dist/lineage/lineage.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { behaviorOf } from "../dialect-behavior/carrier.js";
|
|
2
|
+
import { resolveBehavior } from "../dialect-behavior/registry.js";
|
|
2
3
|
import { resolveScopes } from "../scope/scope.js";
|
|
3
4
|
import { columnNamesOf, findProducerProjection, resolveColumnSource } from "../sema/resolve.js";
|
|
4
5
|
/** Lineage of a query's output columns: each output → the base-table columns it derives from. */
|
|
@@ -71,12 +72,12 @@ function pipeStageLineage(scope, schema, seen) {
|
|
|
71
72
|
return [...aggs, ...keys];
|
|
72
73
|
}
|
|
73
74
|
case "drop": {
|
|
74
|
-
const fold = (n) =>
|
|
75
|
+
const fold = (n) => behaviorOf(scope).fold(n);
|
|
75
76
|
const dropped = new Set(stage.drop.map(fold));
|
|
76
77
|
return passthrough().filter((l) => !dropped.has(fold(l.output)));
|
|
77
78
|
}
|
|
78
79
|
case "rename": {
|
|
79
|
-
const fold = (n) =>
|
|
80
|
+
const fold = (n) => behaviorOf(scope).fold(n);
|
|
80
81
|
const map = new Map(stage.renames.map((r) => [fold(r.from), r.to]));
|
|
81
82
|
return passthrough().map((l) => ({
|
|
82
83
|
output: map.get(fold(l.output)) ?? l.output,
|
|
@@ -84,7 +85,7 @@ function pipeStageLineage(scope, schema, seen) {
|
|
|
84
85
|
}));
|
|
85
86
|
}
|
|
86
87
|
case "set": {
|
|
87
|
-
const fold = (n) =>
|
|
88
|
+
const fold = (n) => behaviorOf(scope).fold(n);
|
|
88
89
|
const set = new Map(stage.assignments.map((a) => [fold(a.column), a.expr]));
|
|
89
90
|
return passthrough().map((l) => {
|
|
90
91
|
const e = set.get(fold(l.output));
|
|
@@ -113,7 +114,7 @@ function starLineage(scope, qualifier, schema, seen) {
|
|
|
113
114
|
const want = qualifier ? (qualifier[qualifier.length - 1] ?? "") : undefined;
|
|
114
115
|
const out = [];
|
|
115
116
|
for (const [key, src] of scope.sources) {
|
|
116
|
-
if (want !== undefined && !matchesSourceKey(key, want
|
|
117
|
+
if (want !== undefined && !behaviorOf(scope).matchesSourceKey(key, want))
|
|
117
118
|
continue;
|
|
118
119
|
for (const col of columnNamesOf(src, schema, undefined, scope.dialect) ?? []) {
|
|
119
120
|
out.push({ output: col, origins: dedup(columnOrigins(src, col, schema, seen), scope.dialect) });
|
|
@@ -217,12 +218,14 @@ function subqueryOrigins(query, schema, seen, dialect) {
|
|
|
217
218
|
return exprOrigins(root.body.projections[0].expr, root, schema, seen);
|
|
218
219
|
}
|
|
219
220
|
function dedup(origins, dialect) {
|
|
221
|
+
const b = resolveBehavior(dialect);
|
|
220
222
|
const byKey = new Map();
|
|
221
223
|
for (const o of origins) {
|
|
222
|
-
byKey.set(`${foldTableName(o.table
|
|
224
|
+
byKey.set(`${b.foldTableName(o.table).join(".")}.${b.fold(o.column)}`, o);
|
|
223
225
|
}
|
|
224
226
|
return [...byKey.values()];
|
|
225
227
|
}
|
|
226
228
|
function eq(a, b, dialect) {
|
|
227
|
-
|
|
229
|
+
const bh = resolveBehavior(dialect);
|
|
230
|
+
return bh.fold(a) === bh.fold(b);
|
|
228
231
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { QueryExpr } from "../ir/ir.js";
|
|
2
|
-
import type {
|
|
2
|
+
import type { TemplateCall, TemplateProvider } from "../qualify/template-provider.js";
|
|
3
|
+
import type { MacroCall, TagNode } from "./tag-ast.js";
|
|
3
4
|
/** The rebuilt AST plus the tag↔node correlations collected while building it. */
|
|
4
5
|
export interface TagCorrelation {
|
|
5
6
|
ast: QueryExpr;
|
|
@@ -9,10 +10,16 @@ export interface TagCorrelation {
|
|
|
9
10
|
byTag: Map<TagNode, object>;
|
|
10
11
|
}
|
|
11
12
|
/**
|
|
12
|
-
* Rewrite templated FROM/JOIN sources in `ast` to carry their
|
|
13
|
-
* a `template` marker, correlating each source to a tag by span
|
|
14
|
-
* Returns the SAME `ast` reference when nothing correlates (structural sharing);
|
|
15
|
-
* returns a re-frozen rebuilt tree otherwise. Total
|
|
16
|
-
*
|
|
13
|
+
* Rewrite templated FROM/JOIN sources in `ast` to carry their provider-resolved name (when the
|
|
14
|
+
* provider resolves the call) plus a `template` marker, correlating each source to a tag by span
|
|
15
|
+
* containment. Returns the SAME `ast` reference when nothing correlates (structural sharing);
|
|
16
|
+
* returns a re-frozen rebuilt tree otherwise. Total, never throws; the correlation maps are empty
|
|
17
|
+
* (not absent) on the no-op and error paths.
|
|
17
18
|
*/
|
|
18
|
-
export declare function applyTemplateTags(ast: QueryExpr, tags: TagNode[], text: string): TagCorrelation;
|
|
19
|
+
export declare function applyTemplateTags(ast: QueryExpr, tags: TagNode[], text: string, provider: TemplateProvider): TagCorrelation;
|
|
20
|
+
/**
|
|
21
|
+
* The provider key of a tag-AST MacroCall — name + package + literal args, with kwargs
|
|
22
|
+
* carried separately (the channel-agreed TemplateCall contract: quote-stripped, escapes
|
|
23
|
+
* NOT resolved, computed arguments null, kwargs never dropped).
|
|
24
|
+
*/
|
|
25
|
+
export declare function callOf(mc: MacroCall, text: string): TemplateCall;
|