sqllens 1.0.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/LICENSE +0 -10
- package/README.md +95 -85
- package/THIRD-PARTY-NOTICES.md +70 -5
- package/dist/api.d.ts +5 -3
- package/dist/api.js +17 -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/config.js +66 -0
- 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 +48 -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/derived-dialects.js +19 -1
- 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 +22 -17
- package/dist/dialect.d.ts +2 -2
- 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/MysqlLexer.js +7357 -0
- package/dist/generated/mysql/MysqlParser.js +78520 -0
- 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/SqliteLexer.js +945 -0
- package/dist/generated/sqlite/SqliteParser.js +14682 -0
- 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 -145
- package/dist/index.d.ts +6 -2
- package/dist/index.js +14 -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/part-span.d.ts +16 -1
- package/dist/ir/part-span.js +38 -10
- 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 +6 -6
- 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/mysql/infer.d.ts +20 -0
- package/dist/mysql/infer.js +156 -0
- package/dist/mysql/lower.d.ts +13 -0
- package/dist/mysql/lower.js +1443 -0
- package/dist/mysql/parse.d.ts +10 -0
- package/dist/mysql/parse.js +70 -0
- 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 -134
- 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 -582
- 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/sqlite/infer.d.ts +12 -0
- package/dist/sqlite/infer.js +122 -0
- package/dist/sqlite/lower.d.ts +11 -0
- package/dist/sqlite/lower.js +1093 -0
- package/dist/sqlite/parse.d.ts +10 -0
- package/dist/sqlite/parse.js +70 -0
- 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/token/classify.js +31 -0
- package/dist/token/tokenize.js +4 -0
- 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 +15 -11
- 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/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/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 -74
- 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
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { acceptsFor } from "../dialect-behavior/coerce-rules.js";
|
|
2
|
+
import { likePatternToRegExp } from "../scope/like-pattern.js";
|
|
3
|
+
import { SIGNATURES } from "../signature/signatures.js";
|
|
4
|
+
import { displayName, fold, foldTableName, matchesSourceKey } from "./fold.js";
|
|
5
|
+
import { tsqlLiteral, tsqlParseType, tsqlSpecial, TSQL_FUNCTION_RETURNS } from "./infer.js";
|
|
6
|
+
export const tsqlBehavior = {
|
|
7
|
+
fold,
|
|
8
|
+
displayName,
|
|
9
|
+
foldTableName,
|
|
10
|
+
matchesSourceKey,
|
|
11
|
+
likeMatch: (pattern, value) => likePatternToRegExp(pattern).test(value),
|
|
12
|
+
literal: tsqlLiteral,
|
|
13
|
+
parseType: tsqlParseType,
|
|
14
|
+
functions: TSQL_FUNCTION_RETURNS,
|
|
15
|
+
division: "integer",
|
|
16
|
+
special: tsqlSpecial,
|
|
17
|
+
signatures: SIGNATURES.tsql,
|
|
18
|
+
// T-SQL implicit coercion: VARCHAR containing a number coerces to numeric (STR_TO_NUM=true), bool<->num coerces too (BOOL_NUM=true).
|
|
19
|
+
accepts: (argType, paramText) => acceptsFor(tsqlParseType, true, true, argType, paramText),
|
|
20
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type FoldRule, type IdentKind } from "../ident/fold.js";
|
|
2
|
+
export declare const TSQL_FOLD_RULE: FoldRule;
|
|
3
|
+
/** Fold an identifier to its T-SQL 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,34 @@
|
|
|
1
|
+
// T-SQL 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
|
+
// learn.microsoft.com/en-us/sql/relational-databases/databases/database-identifiers +
|
|
5
|
+
// .../sql/t-sql/statements/collations — verified live: case folding is NOT a hardcoded T-SQL
|
|
6
|
+
// rule, it's a function of the identifier's COLLATION: "you can create two tables with names
|
|
7
|
+
// that differ only in case in a database that has case-sensitive collation, but you can't...
|
|
8
|
+
// in a database that has case-insensitive collation." Most SQL Server / Fabric SQL database
|
|
9
|
+
// defaults are case-insensitive (e.g. SQL_Latin1_General_CP1_CI_AS), but this is not universal
|
|
10
|
+
// — Fabric Warehouse defaults to the case-SENSITIVE Latin1_General_100_BIN2_UTF8. This module
|
|
11
|
+
// encodes the common default-collation (CI) behavior; a caller with a known case-sensitive
|
|
12
|
+
// collation is a documented boundary this module does not cross. Delimiting with `[ ]` or
|
|
13
|
+
// `" "` does not itself change case behavior — quoting only unlocks reserved words/specials.
|
|
14
|
+
import { displayWith, foldWith } from "../ident/fold.js";
|
|
15
|
+
const DOUBLE_QUOTE = ['"', '"'];
|
|
16
|
+
export const TSQL_FOLD_RULE = {
|
|
17
|
+
delimiters: [["[", "]"], DOUBLE_QUOTE],
|
|
18
|
+
unquoted: "lower",
|
|
19
|
+
quoted: "lower",
|
|
20
|
+
};
|
|
21
|
+
/** Fold an identifier to its T-SQL identity key. */
|
|
22
|
+
export function fold(raw, kind = "other") {
|
|
23
|
+
return foldWith(TSQL_FOLD_RULE, raw, kind);
|
|
24
|
+
}
|
|
25
|
+
/** Presentation twin: strip delimiters, no case change. */
|
|
26
|
+
export function displayName(raw) {
|
|
27
|
+
return displayWith(TSQL_FOLD_RULE, raw);
|
|
28
|
+
}
|
|
29
|
+
export function foldTableName(parts) {
|
|
30
|
+
return parts.map((p) => fold(p, "table"));
|
|
31
|
+
}
|
|
32
|
+
export function matchesSourceKey(key, rawPart) {
|
|
33
|
+
return key === fold(rawPart) || key === fold(rawPart, "table");
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
// The complete tsql dialect module: parse + lower (front end) and behavior (semantic knowledge).
|
|
2
|
+
// The registry wires this; to understand everything sqllens does for tsql, read this folder.
|
|
3
|
+
import { tsqlBehavior } from "./behavior.js";
|
|
4
|
+
import { lower } from "./lower.js";
|
|
5
|
+
import { parseTSql } from "./parse.js";
|
|
6
|
+
export const tsql = {
|
|
7
|
+
parse: parseTSql,
|
|
8
|
+
lower,
|
|
9
|
+
behavior: tsqlBehavior,
|
|
10
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type FnRule } from "../infer/functions.js";
|
|
2
|
+
import { type Type } from "../infer/types.js";
|
|
3
|
+
import type { Expr } from "../ir/ir.js";
|
|
4
|
+
export declare const TSQL_FUNCTION_RETURNS: Record<string, FnRule>;
|
|
5
|
+
/** T-SQL pre-registry inference hook — the XML data type methods, which `lowerUdtElem` lowers to
|
|
6
|
+
* `function` nodes with the receiver as arg 0. `value(xpath, 'sqltype')` is typed by its literal
|
|
7
|
+
* sqltype argument (never-wrong: no XML shredding — the declared sqltype is the value's runtime
|
|
8
|
+
* type; a non-literal sqltype falls through to unknown); `exist()` → boolean (bit); `query()` → xml.
|
|
9
|
+
* https://learn.microsoft.com/en-us/sql/t-sql/xml/xml-data-type-methods */
|
|
10
|
+
export declare function tsqlSpecial(fn: Extract<Expr, {
|
|
11
|
+
kind: "function";
|
|
12
|
+
}>): Type | undefined;
|
|
13
|
+
/** T-SQL literal forms: `'str'` / `N'unicode'` → string, `0x…` → binary, `1` → int, `1.5` → decimal
|
|
14
|
+
* (numeric), `1e3` → float. */
|
|
15
|
+
export declare function tsqlLiteral(text: string): Type;
|
|
16
|
+
export declare function tsqlParseType(text: string): Type;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { commonType } from "../infer/coerce.js";
|
|
2
|
+
import { B, BIG, BIN, D, DATE, I, S, TS, common, dateArg, fixed, firstArg, group, } from "../infer/functions.js";
|
|
3
|
+
import { parseType, scalar, TSQL_ALIASES, UNKNOWN } from "../infer/types.js";
|
|
4
|
+
import { fold } from "./fold.js";
|
|
5
|
+
/** T-SQL SUM/AVG return type (per the MS reference): tinyint/smallint promote to int; int/bigint/
|
|
6
|
+
* decimal/float keep their (canonical) type. Unlike Spark, SUM(int) is int, not bigint. */
|
|
7
|
+
const tsqlNumericAgg = (args) => {
|
|
8
|
+
const t = args[0];
|
|
9
|
+
if (t?.kind !== "scalar")
|
|
10
|
+
return UNKNOWN;
|
|
11
|
+
return t.name === "tinyint" || t.name === "smallint" ? I : t;
|
|
12
|
+
};
|
|
13
|
+
// Function return-type registry for T-SQL (Transact-SQL), from Microsoft's built-in function
|
|
14
|
+
// reference (verified against MS Learn). Same discipline as the Spark registry: a missing rule
|
|
15
|
+
// yields `unknown`, never a wrong type. Where T-SQL and Spark share a name but differ in meaning,
|
|
16
|
+
// the T-SQL rule wins here (e.g. `count` is int, not bigint; `sum(int)` is int, not bigint;
|
|
17
|
+
// `isnull(check, repl)` returns check's type, not a boolean predicate). CAST/CONVERT/TRY_CAST/PARSE
|
|
18
|
+
// lower to cast nodes, not functions, so they aren't here.
|
|
19
|
+
export const TSQL_FUNCTION_RETURNS = {
|
|
20
|
+
// string → string
|
|
21
|
+
...group(fixed(S), [
|
|
22
|
+
"left",
|
|
23
|
+
"right",
|
|
24
|
+
"substring",
|
|
25
|
+
"upper",
|
|
26
|
+
"lower",
|
|
27
|
+
"ltrim",
|
|
28
|
+
"rtrim",
|
|
29
|
+
"trim",
|
|
30
|
+
"replace",
|
|
31
|
+
"replicate",
|
|
32
|
+
"reverse",
|
|
33
|
+
"stuff",
|
|
34
|
+
"concat",
|
|
35
|
+
"concat_ws",
|
|
36
|
+
"format",
|
|
37
|
+
"str",
|
|
38
|
+
"quotename",
|
|
39
|
+
"space",
|
|
40
|
+
"soundex",
|
|
41
|
+
"translate",
|
|
42
|
+
"string_escape",
|
|
43
|
+
"nchar",
|
|
44
|
+
"char",
|
|
45
|
+
"parsename",
|
|
46
|
+
"string_agg",
|
|
47
|
+
"json_value",
|
|
48
|
+
"json_query",
|
|
49
|
+
"json_modify",
|
|
50
|
+
]),
|
|
51
|
+
// string → int
|
|
52
|
+
...group(fixed(I), ["len", "datalength", "charindex", "patindex", "ascii", "unicode", "difference"]),
|
|
53
|
+
// date/time → datetime (canonical timestamp)
|
|
54
|
+
...group(fixed(TS), [
|
|
55
|
+
"getdate",
|
|
56
|
+
"getutcdate",
|
|
57
|
+
"sysdatetime",
|
|
58
|
+
"sysutcdatetime",
|
|
59
|
+
"sysdatetimeoffset",
|
|
60
|
+
"current_timestamp",
|
|
61
|
+
"eomonth",
|
|
62
|
+
"switchoffset",
|
|
63
|
+
"todatetimeoffset",
|
|
64
|
+
"datetimefromparts",
|
|
65
|
+
"datetime2fromparts",
|
|
66
|
+
"smalldatetimefromparts",
|
|
67
|
+
]),
|
|
68
|
+
datefromparts: fixed(DATE),
|
|
69
|
+
timefromparts: fixed(scalar("time")),
|
|
70
|
+
// date → int / bigint / string
|
|
71
|
+
...group(fixed(I), ["datediff", "datepart", "year", "month", "day"]),
|
|
72
|
+
datediff_big: fixed(BIG),
|
|
73
|
+
datename: fixed(S),
|
|
74
|
+
// DATEADD keeps the date argument's type (datetime by default)
|
|
75
|
+
dateadd: dateArg,
|
|
76
|
+
// predicates that return int 0/1
|
|
77
|
+
...group(fixed(I), [
|
|
78
|
+
"isdate",
|
|
79
|
+
"isnumeric",
|
|
80
|
+
"isjson",
|
|
81
|
+
"checksum",
|
|
82
|
+
"binary_checksum",
|
|
83
|
+
"object_id",
|
|
84
|
+
"grouping",
|
|
85
|
+
"grouping_id",
|
|
86
|
+
]),
|
|
87
|
+
// numeric → same type as input (MS: ABS, CEILING, FLOOR, ROUND, SIGN, POWER, DEGREES, RADIANS)
|
|
88
|
+
...group(firstArg, ["abs", "ceiling", "floor", "round", "sign", "power", "degrees", "radians"]),
|
|
89
|
+
// numeric → float (MS: EXP, LOG, LOG10, SQUARE, SQRT and the trig fns cast to float → canonical double)
|
|
90
|
+
...group(fixed(D), [
|
|
91
|
+
"sqrt",
|
|
92
|
+
"square",
|
|
93
|
+
"exp",
|
|
94
|
+
"log",
|
|
95
|
+
"log10",
|
|
96
|
+
"sin",
|
|
97
|
+
"cos",
|
|
98
|
+
"tan",
|
|
99
|
+
"atn2",
|
|
100
|
+
"acos",
|
|
101
|
+
"asin",
|
|
102
|
+
"atan",
|
|
103
|
+
"cot",
|
|
104
|
+
"pi",
|
|
105
|
+
"rand",
|
|
106
|
+
]),
|
|
107
|
+
// aggregates — COUNT is int (COUNT_BIG bigint); SUM/AVG promote small ints to int; MIN/MAX keep type
|
|
108
|
+
count: fixed(I),
|
|
109
|
+
count_big: fixed(BIG),
|
|
110
|
+
sum: tsqlNumericAgg,
|
|
111
|
+
avg: tsqlNumericAgg,
|
|
112
|
+
...group(firstArg, ["min", "max"]),
|
|
113
|
+
...group(fixed(D), ["stdev", "stdevp", "var", "varp"]),
|
|
114
|
+
// window/ranking — ROW_NUMBER/RANK/DENSE_RANK/NTILE → bigint; PERCENT_RANK/CUME_DIST → float;
|
|
115
|
+
// the value-returning analytics keep their argument's type
|
|
116
|
+
...group(fixed(BIG), ["row_number", "rank", "dense_rank", "ntile"]),
|
|
117
|
+
...group(fixed(D), ["percent_rank", "cume_dist"]),
|
|
118
|
+
...group(firstArg, ["lag", "lead", "first_value", "last_value"]),
|
|
119
|
+
// null / choice — ISNULL/NULLIF return the first argument's type; COALESCE/IIF/CHOOSE a common type
|
|
120
|
+
isnull: firstArg,
|
|
121
|
+
nullif: firstArg,
|
|
122
|
+
...group(common, ["coalesce"]),
|
|
123
|
+
iif: (args) => commonType(args.slice(-2)),
|
|
124
|
+
choose: (args) => commonType(args.slice(1)),
|
|
125
|
+
// logical / choice (SQL Server 2022+)
|
|
126
|
+
...group(common, ["greatest", "least"]),
|
|
127
|
+
// window/analytic value functions keep the argument's type; PERCENTILE_CONT → float
|
|
128
|
+
...group(firstArg, ["first_value", "last_value"]),
|
|
129
|
+
percentile_cont: fixed(D),
|
|
130
|
+
// JSON builders → string; JSON_PATH_EXISTS → int (bit)
|
|
131
|
+
...group(fixed(S), ["json_object", "json_array"]),
|
|
132
|
+
json_path_exists: fixed(I),
|
|
133
|
+
// date/time helpers — DATETRUNC/DATE_BUCKET keep the date argument's type
|
|
134
|
+
...group(dateArg, ["datetrunc", "date_bucket"]),
|
|
135
|
+
datetimeoffsetfromparts: fixed(TS),
|
|
136
|
+
...group(fixed(S), ["current_timezone", "current_timezone_id"]),
|
|
137
|
+
// string-returning system / user / metadata-name functions
|
|
138
|
+
...group(fixed(S), [
|
|
139
|
+
"formatmessage",
|
|
140
|
+
"system_user",
|
|
141
|
+
"session_user",
|
|
142
|
+
"current_user",
|
|
143
|
+
"user_name",
|
|
144
|
+
"suser_name",
|
|
145
|
+
"suser_sname",
|
|
146
|
+
"host_name",
|
|
147
|
+
"host_id",
|
|
148
|
+
"app_name",
|
|
149
|
+
"original_login",
|
|
150
|
+
"error_message",
|
|
151
|
+
"error_procedure",
|
|
152
|
+
"db_name",
|
|
153
|
+
"object_name",
|
|
154
|
+
"object_schema_name",
|
|
155
|
+
"schema_name",
|
|
156
|
+
"col_name",
|
|
157
|
+
]),
|
|
158
|
+
// int-returning error / metadata-id / property functions
|
|
159
|
+
...group(fixed(I), [
|
|
160
|
+
"error_number",
|
|
161
|
+
"error_severity",
|
|
162
|
+
"error_state",
|
|
163
|
+
"error_line",
|
|
164
|
+
"db_id",
|
|
165
|
+
"schema_id",
|
|
166
|
+
"col_length",
|
|
167
|
+
"columnproperty",
|
|
168
|
+
"objectproperty",
|
|
169
|
+
"objectpropertyex",
|
|
170
|
+
"type_id",
|
|
171
|
+
"xact_state",
|
|
172
|
+
"checksum_agg",
|
|
173
|
+
]),
|
|
174
|
+
approx_count_distinct: fixed(BIG),
|
|
175
|
+
rowcount_big: fixed(BIG),
|
|
176
|
+
current_transaction_id: fixed(BIG),
|
|
177
|
+
// binary-returning compression / context functions
|
|
178
|
+
...group(fixed(BIN), ["compress", "decompress", "context_info"]),
|
|
179
|
+
// system / metadata
|
|
180
|
+
newid: fixed(S),
|
|
181
|
+
newsequentialid: fixed(S),
|
|
182
|
+
hashbytes: fixed(BIN),
|
|
183
|
+
...group(fixed(scalar("decimal")), ["scope_identity", "ident_current"]),
|
|
184
|
+
// ------------------------------------------------------------------
|
|
185
|
+
// Entries below: return types fetched from MS Learn 2026-06-10
|
|
186
|
+
// (per-function "Return types" sections, ver17 — the regex / fuzzy /
|
|
187
|
+
// vector / JSON-agg families are SQL Server 2025+). Functions whose
|
|
188
|
+
// documented type depends on an argument value or is a bare
|
|
189
|
+
// sql_variant stay absent → unknown.
|
|
190
|
+
// ------------------------------------------------------------------
|
|
191
|
+
// bit manipulation (SQL Server 2022): shifts/set keep the input's type
|
|
192
|
+
bit_count: fixed(BIG),
|
|
193
|
+
get_bit: fixed(B),
|
|
194
|
+
...group(firstArg, ["left_shift", "right_shift", "set_bit"]),
|
|
195
|
+
// aggregates / analytic
|
|
196
|
+
any_value: firstArg,
|
|
197
|
+
approx_percentile_cont: fixed(D), // float(53)
|
|
198
|
+
// regex (2025; REGEXP_LIKE is documented as boolean-valued)
|
|
199
|
+
regexp_like: fixed(B),
|
|
200
|
+
regexp_count: fixed(I),
|
|
201
|
+
regexp_instr: fixed(I), // doc states "Integer."
|
|
202
|
+
...group(fixed(S), ["regexp_replace", "regexp_substr"]),
|
|
203
|
+
// fuzzy string match (2025)
|
|
204
|
+
...group(fixed(I), ["edit_distance", "edit_distance_similarity", "jaro_winkler_similarity"]),
|
|
205
|
+
jaro_winkler_distance: fixed(D), // float
|
|
206
|
+
// JSON (2025): aggregates are nvarchar(max); JSON_CONTAINS is int (0/1/NULL), not bit
|
|
207
|
+
...group(fixed(S), ["json_arrayagg", "json_objectagg"]),
|
|
208
|
+
json_contains: fixed(I),
|
|
209
|
+
// encoding (2025)
|
|
210
|
+
base64_encode: fixed(S),
|
|
211
|
+
base64_decode: fixed(BIN),
|
|
212
|
+
// vectors (2025)
|
|
213
|
+
...group(fixed(D), ["vector_distance", "vector_norm"]),
|
|
214
|
+
vector_normalize: fixed(scalar("vector")),
|
|
215
|
+
// metadata / system. object_definition is nvarchar(max); original_db_name and
|
|
216
|
+
// trigger_nestlevel state no type on their pages — a name and a count, typed as such.
|
|
217
|
+
...group(fixed(S), ["type_name", "object_definition", "original_db_name"]),
|
|
218
|
+
...group(fixed(I), [
|
|
219
|
+
"has_dbaccess",
|
|
220
|
+
"is_rolemember",
|
|
221
|
+
"is_srvrolemember",
|
|
222
|
+
"suser_id",
|
|
223
|
+
"user_id",
|
|
224
|
+
"database_principal_id",
|
|
225
|
+
"textvalid",
|
|
226
|
+
"trigger_nestlevel",
|
|
227
|
+
]),
|
|
228
|
+
...group(fixed(scalar("decimal")), ["ident_incr", "ident_seed"]),
|
|
229
|
+
cursor_status: fixed(scalar("smallint")),
|
|
230
|
+
...group(fixed(BIN), [
|
|
231
|
+
"suser_sid",
|
|
232
|
+
"textptr",
|
|
233
|
+
"columns_updated",
|
|
234
|
+
"encryptbypassphrase",
|
|
235
|
+
"decryptbypassphrase",
|
|
236
|
+
"encryptbykey",
|
|
237
|
+
"decryptbykey",
|
|
238
|
+
"crypt_gen_random",
|
|
239
|
+
]),
|
|
240
|
+
eventdata: fixed(scalar("xml")),
|
|
241
|
+
};
|
|
242
|
+
/** T-SQL pre-registry inference hook — the XML data type methods, which `lowerUdtElem` lowers to
|
|
243
|
+
* `function` nodes with the receiver as arg 0. `value(xpath, 'sqltype')` is typed by its literal
|
|
244
|
+
* sqltype argument (never-wrong: no XML shredding — the declared sqltype is the value's runtime
|
|
245
|
+
* type; a non-literal sqltype falls through to unknown); `exist()` → boolean (bit); `query()` → xml.
|
|
246
|
+
* https://learn.microsoft.com/en-us/sql/t-sql/xml/xml-data-type-methods */
|
|
247
|
+
export function tsqlSpecial(fn) {
|
|
248
|
+
if (fn.qualifier !== undefined)
|
|
249
|
+
return undefined;
|
|
250
|
+
switch (fn.name.toLowerCase()) {
|
|
251
|
+
case "value": {
|
|
252
|
+
// args = [receiver, xpath, sqltype]; the second method argument (arg 2) is the declared type.
|
|
253
|
+
const sqltype = fn.args[2];
|
|
254
|
+
return sqltype?.kind === "literal" ? parseType(unquoteLiteral(sqltype.text), TSQL_ALIASES) : undefined;
|
|
255
|
+
}
|
|
256
|
+
case "exist":
|
|
257
|
+
return scalar("boolean"); // .exist() returns bit
|
|
258
|
+
case "query":
|
|
259
|
+
return scalar("xml"); // .query() returns xml
|
|
260
|
+
default:
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
/** Strip a surrounding SQL string-literal quote (`'varchar(100)'` → `varchar(100)`). */
|
|
265
|
+
function unquoteLiteral(text) {
|
|
266
|
+
const t = text.trim();
|
|
267
|
+
return t.length >= 2 && t.startsWith("'") && t.endsWith("'") ? t.slice(1, -1) : t;
|
|
268
|
+
}
|
|
269
|
+
/** T-SQL literal forms: `'str'` / `N'unicode'` → string, `0x…` → binary, `1` → int, `1.5` → decimal
|
|
270
|
+
* (numeric), `1e3` → float. */
|
|
271
|
+
export function tsqlLiteral(text) {
|
|
272
|
+
const t = text.trim();
|
|
273
|
+
if (/^n?['"]/i.test(t))
|
|
274
|
+
return scalar("string");
|
|
275
|
+
if (/^0x/i.test(t))
|
|
276
|
+
return scalar("binary");
|
|
277
|
+
if (/^null$/i.test(t))
|
|
278
|
+
return UNKNOWN;
|
|
279
|
+
if (/^[+-]?\d+$/.test(t))
|
|
280
|
+
return scalar("int");
|
|
281
|
+
if (/^[+-]?(\d+\.?\d*|\.\d+)e[+-]?\d+$/i.test(t))
|
|
282
|
+
return scalar("float");
|
|
283
|
+
if (/^[+-]?(\d+\.\d*|\.\d+)$/.test(t))
|
|
284
|
+
return scalar("decimal");
|
|
285
|
+
return UNKNOWN;
|
|
286
|
+
}
|
|
287
|
+
export function tsqlParseType(text) {
|
|
288
|
+
return parseType(text, TSQL_ALIASES, fold);
|
|
289
|
+
}
|
package/dist/tsql/lower.js
CHANGED
|
@@ -3,7 +3,7 @@ import { TSqlParser as P } from "../generated/tsql/TSqlParser.js";
|
|
|
3
3
|
import { keywordCategory, swallowedCategories, swallowedStatements } from "../ir/statement.js";
|
|
4
4
|
import { partSpansOf } from "../ir/part-span.js";
|
|
5
5
|
import { freezeIR } from "../ir/freeze.js";
|
|
6
|
-
import { displayName,
|
|
6
|
+
import { displayName, fold } from "./fold.js";
|
|
7
7
|
// ---------------------------------------------------------------------------
|
|
8
8
|
// Lowering — T-SQL (grammars-v4 sql/tsql) CST -> the shared, dialect-neutral IR
|
|
9
9
|
// (src/ir/ir.ts). The semantic layer (scope/qualify/infer/lineage/symbols) runs
|
|
@@ -447,7 +447,7 @@ function lowerUdtElem(udt) {
|
|
|
447
447
|
: [];
|
|
448
448
|
return {
|
|
449
449
|
kind: "function",
|
|
450
|
-
name: method ? displayName(method.getText()
|
|
450
|
+
name: method ? displayName(method.getText()).toLowerCase() : "",
|
|
451
451
|
args: [receiverExpr, ...methodArgs],
|
|
452
452
|
aggregate: false,
|
|
453
453
|
distinct: false,
|
|
@@ -924,7 +924,7 @@ function windowParts(node) {
|
|
|
924
924
|
/** Resolve a window name against the enclosing query spec's WINDOW clause. A definition may
|
|
925
925
|
* itself start with another window's name (chained); `seen` guards against cycles. */
|
|
926
926
|
function resolveNamedWindow(from, name, seen) {
|
|
927
|
-
const key =
|
|
927
|
+
const key = fold(name);
|
|
928
928
|
if (seen.has(key))
|
|
929
929
|
return undefined;
|
|
930
930
|
seen.add(key);
|
|
@@ -938,7 +938,7 @@ function resolveNamedWindow(from, name, seen) {
|
|
|
938
938
|
return undefined;
|
|
939
939
|
for (const def of directChildrenOfRule(clause, P.RULE_window_definition)) {
|
|
940
940
|
const defName = directChildrenOfRule(def, P.RULE_id_)[0]?.getText();
|
|
941
|
-
if (!defName ||
|
|
941
|
+
if (!defName || fold(defName) !== key)
|
|
942
942
|
continue;
|
|
943
943
|
const ws = directChildrenOfRule(def, P.RULE_window_specification)[0];
|
|
944
944
|
if (!ws)
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FnSignature } from "../signature/signatures.js";
|
|
2
|
+
/** The merged function-signature table for tsql: 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 TSQL_SIGNATURES: Record<string, FnSignature[]>;
|