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