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.
Files changed (244) hide show
  1. package/LICENSE +0 -10
  2. package/README.md +95 -85
  3. package/THIRD-PARTY-NOTICES.md +70 -5
  4. package/dist/api.d.ts +5 -3
  5. package/dist/api.js +17 -4
  6. package/dist/bigquery/behavior.d.ts +2 -0
  7. package/dist/bigquery/behavior.js +20 -0
  8. package/dist/bigquery/dot-path.d.ts +0 -2
  9. package/dist/bigquery/dot-path.js +0 -1
  10. package/dist/bigquery/fold.d.ts +8 -0
  11. package/dist/bigquery/fold.js +39 -0
  12. package/dist/bigquery/index.d.ts +7 -0
  13. package/dist/bigquery/index.js +10 -0
  14. package/dist/{infer/bigquery.d.ts → bigquery/infer.d.ts} +2 -2
  15. package/dist/{infer/bigquery.js → bigquery/infer.js} +4 -3
  16. package/dist/bigquery/lower.js +35 -12
  17. package/dist/bigquery/signatures.generated.d.ts +6 -0
  18. package/dist/bigquery/signatures.generated.js +1068 -0
  19. package/dist/completion/atn-walk.d.ts +13 -2
  20. package/dist/completion/atn-walk.js +13 -10
  21. package/dist/completion/complete.d.ts +4 -3
  22. package/dist/completion/complete.js +101 -45
  23. package/dist/completion/config.js +66 -0
  24. package/dist/completion/jinja-slot.d.ts +24 -0
  25. package/dist/completion/jinja-slot.js +126 -0
  26. package/dist/completion/parser-factory.d.ts +13 -1
  27. package/dist/completion/parser-factory.js +48 -0
  28. package/dist/databricks/behavior.d.ts +2 -0
  29. package/dist/databricks/behavior.js +19 -0
  30. package/dist/databricks/fold.d.ts +8 -0
  31. package/dist/databricks/fold.js +27 -0
  32. package/dist/databricks/index.d.ts +7 -0
  33. package/dist/databricks/index.js +10 -0
  34. package/dist/databricks/infer.d.ts +6 -0
  35. package/dist/databricks/infer.js +638 -0
  36. package/dist/databricks/signatures.generated.d.ts +6 -0
  37. package/dist/databricks/signatures.generated.js +1745 -0
  38. package/dist/derived-dialects.js +19 -1
  39. package/dist/dialect-behavior/behavior.d.ts +26 -0
  40. package/dist/dialect-behavior/behavior.js +1 -0
  41. package/dist/dialect-behavior/carrier.d.ts +5 -0
  42. package/dist/dialect-behavior/carrier.js +5 -0
  43. package/dist/dialect-behavior/coerce-rules.d.ts +7 -0
  44. package/dist/dialect-behavior/coerce-rules.js +69 -0
  45. package/dist/dialect-behavior/public-fold.d.ts +6 -0
  46. package/dist/dialect-behavior/public-fold.js +9 -0
  47. package/dist/dialect-behavior/registry.d.ts +6 -0
  48. package/dist/dialect-behavior/registry.js +34 -0
  49. package/dist/dialect-symbols.js +22 -17
  50. package/dist/dialect.d.ts +2 -2
  51. package/dist/document/document.d.ts +1 -1
  52. package/dist/document/document.js +8 -7
  53. package/dist/duckdb/behavior.d.ts +2 -0
  54. package/dist/duckdb/behavior.js +21 -0
  55. package/dist/duckdb/fold.d.ts +8 -0
  56. package/dist/duckdb/fold.js +29 -0
  57. package/dist/duckdb/index.d.ts +7 -0
  58. package/dist/duckdb/index.js +10 -0
  59. package/dist/{infer/duckdb.d.ts → duckdb/infer.d.ts} +2 -2
  60. package/dist/{infer/duckdb.js → duckdb/infer.js} +4 -3
  61. package/dist/duckdb/lower.js +47 -15
  62. package/dist/duckdb/signatures.generated.d.ts +6 -0
  63. package/dist/duckdb/signatures.generated.js +1072 -0
  64. package/dist/generated/bigquery/GoogleSQLParser.js +0 -7060
  65. package/dist/generated/databricks/DatabricksParser.js +0 -4800
  66. package/dist/generated/duckdb/DuckdbParser.js +0 -9100
  67. package/dist/generated/minijinja/MinijinjaParser.js +0 -380
  68. package/dist/generated/mysql/MysqlLexer.js +7357 -0
  69. package/dist/generated/mysql/MysqlParser.js +78520 -0
  70. package/dist/generated/postgres/PostgresParser.js +0 -8530
  71. package/dist/generated/redshift/RedshiftParser.js +0 -10970
  72. package/dist/generated/snowflake/SnowflakeParser.js +0 -7320
  73. package/dist/generated/sqlite/SqliteLexer.js +945 -0
  74. package/dist/generated/sqlite/SqliteParser.js +14682 -0
  75. package/dist/generated/trino/TrinoParser.js +0 -3770
  76. package/dist/generated/tsql/TSqlParser.js +0 -8420
  77. package/dist/ident/fold.d.ts +24 -15
  78. package/dist/ident/fold.js +12 -145
  79. package/dist/index.d.ts +6 -2
  80. package/dist/index.js +14 -8
  81. package/dist/infer/functions.d.ts +22 -11
  82. package/dist/infer/functions.js +26 -888
  83. package/dist/infer/infer.js +16 -14
  84. package/dist/infer/nullability.js +3 -4
  85. package/dist/infer/types.d.ts +1 -1
  86. package/dist/infer/types.js +7 -7
  87. package/dist/ir/ir.d.ts +26 -17
  88. package/dist/ir/part-span.d.ts +16 -1
  89. package/dist/ir/part-span.js +38 -10
  90. package/dist/ir/span.js +0 -2
  91. package/dist/ir/walk.js +3 -3
  92. package/dist/lineage/hops.js +13 -10
  93. package/dist/lineage/lineage.js +10 -7
  94. package/dist/minijinja/apply-tags.d.ts +14 -7
  95. package/dist/minijinja/apply-tags.js +66 -121
  96. package/dist/minijinja/parse.js +6 -6
  97. package/dist/minijinja/tag-ast.d.ts +29 -36
  98. package/dist/minijinja/tag-ast.js +210 -90
  99. package/dist/mysql/behavior.d.ts +2 -0
  100. package/dist/mysql/behavior.js +21 -0
  101. package/dist/mysql/fold.d.ts +8 -0
  102. package/dist/mysql/fold.js +49 -0
  103. package/dist/mysql/index.d.ts +7 -0
  104. package/dist/mysql/index.js +10 -0
  105. package/dist/mysql/infer.d.ts +20 -0
  106. package/dist/mysql/infer.js +156 -0
  107. package/dist/mysql/lower.d.ts +13 -0
  108. package/dist/mysql/lower.js +1443 -0
  109. package/dist/mysql/parse.d.ts +10 -0
  110. package/dist/mysql/parse.js +70 -0
  111. package/dist/mysql/signatures.generated.d.ts +6 -0
  112. package/dist/mysql/signatures.generated.js +508 -0
  113. package/dist/postgres/behavior.d.ts +2 -0
  114. package/dist/postgres/behavior.js +19 -0
  115. package/dist/postgres/fold.d.ts +8 -0
  116. package/dist/postgres/fold.js +30 -0
  117. package/dist/postgres/index.d.ts +7 -0
  118. package/dist/postgres/index.js +10 -0
  119. package/dist/{infer/postgres.d.ts → postgres/infer.d.ts} +2 -2
  120. package/dist/{infer/postgres.js → postgres/infer.js} +4 -3
  121. package/dist/postgres/lower.js +2 -2
  122. package/dist/postgres/signatures.generated.d.ts +6 -0
  123. package/dist/postgres/signatures.generated.js +2973 -0
  124. package/dist/qualify/check-calls.js +80 -134
  125. package/dist/qualify/qualify.js +16 -14
  126. package/dist/qualify/schema-provider.js +2 -2
  127. package/dist/qualify/schema.js +3 -3
  128. package/dist/qualify/template-provider.d.ts +45 -12
  129. package/dist/qualify/template-provider.js +69 -38
  130. package/dist/redshift/behavior.d.ts +2 -0
  131. package/dist/redshift/behavior.js +19 -0
  132. package/dist/redshift/fold.d.ts +8 -0
  133. package/dist/redshift/fold.js +31 -0
  134. package/dist/redshift/index.d.ts +7 -0
  135. package/dist/redshift/index.js +10 -0
  136. package/dist/{infer/redshift.d.ts → redshift/infer.d.ts} +2 -2
  137. package/dist/{infer/redshift.js → redshift/infer.js} +4 -3
  138. package/dist/redshift/lower.js +2 -2
  139. package/dist/redshift/signatures.generated.d.ts +6 -0
  140. package/dist/redshift/signatures.generated.js +757 -0
  141. package/dist/references/references.js +17 -12
  142. package/dist/scope/like-pattern.d.ts +2 -0
  143. package/dist/scope/like-pattern.js +15 -0
  144. package/dist/scope/scope.d.ts +5 -5
  145. package/dist/scope/scope.js +48 -42
  146. package/dist/sema/resolve.js +17 -12
  147. package/dist/session.d.ts +2 -2
  148. package/dist/signature/signature.d.ts +14 -6
  149. package/dist/signature/signature.js +30 -22
  150. package/dist/signature/signatures.d.ts +14 -12
  151. package/dist/signature/signatures.js +42 -582
  152. package/dist/snowflake/behavior.d.ts +2 -0
  153. package/dist/snowflake/behavior.js +22 -0
  154. package/dist/snowflake/fold.d.ts +8 -0
  155. package/dist/snowflake/fold.js +25 -0
  156. package/dist/snowflake/index.d.ts +7 -0
  157. package/dist/snowflake/index.js +10 -0
  158. package/dist/{infer/snowflake.d.ts → snowflake/infer.d.ts} +2 -2
  159. package/dist/{infer/snowflake.js → snowflake/infer.js} +4 -3
  160. package/dist/snowflake/lower.js +59 -19
  161. package/dist/snowflake/signatures.generated.d.ts +6 -0
  162. package/dist/snowflake/signatures.generated.js +2080 -0
  163. package/dist/sqlite/behavior.d.ts +2 -0
  164. package/dist/sqlite/behavior.js +19 -0
  165. package/dist/sqlite/fold.d.ts +8 -0
  166. package/dist/sqlite/fold.js +41 -0
  167. package/dist/sqlite/index.d.ts +7 -0
  168. package/dist/sqlite/index.js +10 -0
  169. package/dist/sqlite/infer.d.ts +12 -0
  170. package/dist/sqlite/infer.js +122 -0
  171. package/dist/sqlite/lower.d.ts +11 -0
  172. package/dist/sqlite/lower.js +1093 -0
  173. package/dist/sqlite/parse.d.ts +10 -0
  174. package/dist/sqlite/parse.js +70 -0
  175. package/dist/sqlite/signatures.generated.d.ts +6 -0
  176. package/dist/sqlite/signatures.generated.js +277 -0
  177. package/dist/symbols/symbols.js +14 -12
  178. package/dist/token/classify.js +31 -0
  179. package/dist/token/tokenize.js +4 -0
  180. package/dist/trino/behavior.d.ts +2 -0
  181. package/dist/trino/behavior.js +21 -0
  182. package/dist/trino/fold.d.ts +8 -0
  183. package/dist/trino/fold.js +39 -0
  184. package/dist/trino/index.d.ts +7 -0
  185. package/dist/trino/index.js +10 -0
  186. package/dist/{infer/trino.d.ts → trino/infer.d.ts} +2 -2
  187. package/dist/{infer/trino.js → trino/infer.js} +4 -3
  188. package/dist/trino/lower.js +5 -5
  189. package/dist/trino/signatures.generated.d.ts +6 -0
  190. package/dist/trino/signatures.generated.js +968 -0
  191. package/dist/tsql/behavior.d.ts +2 -0
  192. package/dist/tsql/behavior.js +20 -0
  193. package/dist/tsql/fold.d.ts +8 -0
  194. package/dist/tsql/fold.js +34 -0
  195. package/dist/tsql/index.d.ts +7 -0
  196. package/dist/tsql/index.js +10 -0
  197. package/dist/tsql/infer.d.ts +16 -0
  198. package/dist/tsql/infer.js +289 -0
  199. package/dist/tsql/lower.js +4 -4
  200. package/dist/tsql/signatures.generated.d.ts +6 -0
  201. package/dist/tsql/signatures.generated.js +640 -0
  202. package/package.json +15 -11
  203. package/dist/generated/bigquery/GoogleSQLLexer.d.ts +0 -407
  204. package/dist/generated/bigquery/GoogleSQLParser.d.ts +0 -9558
  205. package/dist/generated/bigquery/GoogleSQLParserListener.d.ts +0 -7777
  206. package/dist/generated/bigquery/GoogleSQLParserListener.js +0 -7070
  207. package/dist/generated/databricks/DatabricksLexer.d.ts +0 -566
  208. package/dist/generated/databricks/DatabricksParser.d.ts +0 -7771
  209. package/dist/generated/databricks/DatabricksParserListener.d.ts +0 -5737
  210. package/dist/generated/databricks/DatabricksParserListener.js +0 -5256
  211. package/dist/generated/duckdb/DuckdbLexer.d.ts +0 -691
  212. package/dist/generated/duckdb/DuckdbParser.d.ts +0 -13932
  213. package/dist/generated/duckdb/DuckdbParserListener.d.ts +0 -10049
  214. package/dist/generated/duckdb/DuckdbParserListener.js +0 -9138
  215. package/dist/generated/minijinja/MinijinjaLexer.d.ts +0 -108
  216. package/dist/generated/minijinja/MinijinjaParser.d.ts +0 -604
  217. package/dist/generated/minijinja/MinijinjaParserListener.d.ts +0 -449
  218. package/dist/generated/minijinja/MinijinjaParserListener.js +0 -410
  219. package/dist/generated/postgres/PostgresLexer.d.ts +0 -663
  220. package/dist/generated/postgres/PostgresParser.d.ts +0 -12963
  221. package/dist/generated/postgres/PostgresParserListener.d.ts +0 -9408
  222. package/dist/generated/postgres/PostgresParserListener.js +0 -8554
  223. package/dist/generated/redshift/RedshiftLexer.d.ts +0 -954
  224. package/dist/generated/redshift/RedshiftParser.d.ts +0 -16939
  225. package/dist/generated/redshift/RedshiftParserListener.d.ts +0 -12092
  226. package/dist/generated/redshift/RedshiftParserListener.js +0 -10994
  227. package/dist/generated/snowflake/SnowflakeLexer.d.ts +0 -1046
  228. package/dist/generated/snowflake/SnowflakeParser.d.ts +0 -14196
  229. package/dist/generated/snowflake/SnowflakeParserListener.d.ts +0 -8063
  230. package/dist/generated/snowflake/SnowflakeParserListener.js +0 -7330
  231. package/dist/generated/trino/TrinoLexer.d.ts +0 -381
  232. package/dist/generated/trino/TrinoParser.d.ts +0 -5340
  233. package/dist/generated/trino/TrinoParserListener.d.ts +0 -4704
  234. package/dist/generated/trino/TrinoParserListener.js +0 -4326
  235. package/dist/generated/tsql/TSqlLexer.d.ts +0 -1278
  236. package/dist/generated/tsql/TSqlParser.d.ts +0 -17267
  237. package/dist/generated/tsql/TSqlParserListener.d.ts +0 -9697
  238. package/dist/generated/tsql/TSqlParserListener.js +0 -8854
  239. package/dist/infer/dialect.d.ts +0 -21
  240. package/dist/infer/dialect.js +0 -74
  241. package/dist/infer/literals.d.ts +0 -6
  242. package/dist/infer/literals.js +0 -44
  243. package/dist/signature/generated/tsql.d.ts +0 -3
  244. package/dist/signature/generated/tsql.js +0 -260
@@ -0,0 +1,2 @@
1
+ import type { DialectBehavior } from "../dialect-behavior/behavior.js";
2
+ export declare const tsqlBehavior: DialectBehavior;
@@ -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,7 @@
1
+ import { lower } from "./lower.js";
2
+ import { parseTSql } from "./parse.js";
3
+ export declare const tsql: {
4
+ parse: typeof parseTSql;
5
+ lower: typeof lower;
6
+ behavior: import("../dialect-behavior/behavior.js").DialectBehavior;
7
+ };
@@ -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
+ }
@@ -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, foldIdentifier } from "../ident/fold.js";
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(), "tsql").toLowerCase() : "",
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 = foldIdentifier(name, "tsql");
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 || foldIdentifier(defName, "tsql") !== key)
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[]>;