@askdb/core 1.0.0-beta.44 → 1.0.0-beta.46

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 (138) hide show
  1. package/README.md +20 -19
  2. package/dist/ask.d.ts +45 -45
  3. package/dist/ask.d.ts.map +1 -1
  4. package/dist/ask.js +153 -310
  5. package/dist/ask.js.map +1 -1
  6. package/dist/enrichment/index.d.ts +1 -1
  7. package/dist/enrichment/index.d.ts.map +1 -1
  8. package/dist/enrichment/prompt.d.ts +3 -3
  9. package/dist/enrichment/prompt.d.ts.map +1 -1
  10. package/dist/enrichment/prompt.js +43 -17
  11. package/dist/enrichment/prompt.js.map +1 -1
  12. package/dist/enrichment/suggest.d.ts +2 -2
  13. package/dist/enrichment/suggest.d.ts.map +1 -1
  14. package/dist/enrichment/suggest.js +3 -1
  15. package/dist/enrichment/suggest.js.map +1 -1
  16. package/dist/enrichment/types.d.ts +29 -9
  17. package/dist/enrichment/types.d.ts.map +1 -1
  18. package/dist/errors.d.ts +97 -6
  19. package/dist/errors.d.ts.map +1 -1
  20. package/dist/errors.js +21 -0
  21. package/dist/errors.js.map +1 -1
  22. package/dist/index.d.ts +7 -5
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/logging/askdb-logger.d.ts +2 -0
  27. package/dist/logging/askdb-logger.d.ts.map +1 -1
  28. package/dist/logging/create-askdb-logger.d.ts.map +1 -1
  29. package/dist/logging/create-askdb-logger.js +3 -0
  30. package/dist/logging/create-askdb-logger.js.map +1 -1
  31. package/dist/logging/log-events.d.ts +8 -0
  32. package/dist/logging/log-events.d.ts.map +1 -1
  33. package/dist/logging/log-events.js +8 -0
  34. package/dist/logging/log-events.js.map +1 -1
  35. package/dist/retrieval/synthesize-ddl.d.ts +7 -1
  36. package/dist/retrieval/synthesize-ddl.d.ts.map +1 -1
  37. package/dist/retrieval/synthesize-ddl.js +36 -33
  38. package/dist/retrieval/synthesize-ddl.js.map +1 -1
  39. package/dist/schema/normalize.d.ts +6 -2
  40. package/dist/schema/normalize.d.ts.map +1 -1
  41. package/dist/schema/normalize.js +1 -4
  42. package/dist/schema/normalize.js.map +1 -1
  43. package/dist/schema/v2/format.d.ts +28 -1
  44. package/dist/schema/v2/format.d.ts.map +1 -1
  45. package/dist/schema/v2/format.js +53 -19
  46. package/dist/schema/v2/format.js.map +1 -1
  47. package/dist/schema/v2/index.d.ts +4 -3
  48. package/dist/schema/v2/index.d.ts.map +1 -1
  49. package/dist/schema/v2/index.js +4 -3
  50. package/dist/schema/v2/index.js.map +1 -1
  51. package/dist/schema/v2/loader.d.ts +28 -7
  52. package/dist/schema/v2/loader.d.ts.map +1 -1
  53. package/dist/schema/v2/loader.js +220 -96
  54. package/dist/schema/v2/loader.js.map +1 -1
  55. package/dist/schema/v2/mentions.d.ts +12 -0
  56. package/dist/schema/v2/mentions.d.ts.map +1 -1
  57. package/dist/schema/v2/mentions.js +65 -11
  58. package/dist/schema/v2/mentions.js.map +1 -1
  59. package/dist/schema/v2/save-file.d.ts +56 -0
  60. package/dist/schema/v2/save-file.d.ts.map +1 -0
  61. package/dist/schema/v2/save-file.js +138 -0
  62. package/dist/schema/v2/save-file.js.map +1 -0
  63. package/dist/schema/v2/tenant-policy-loader.d.ts +1 -10
  64. package/dist/schema/v2/tenant-policy-loader.d.ts.map +1 -1
  65. package/dist/schema/v2/tenant-policy-loader.js +65 -51
  66. package/dist/schema/v2/tenant-policy-loader.js.map +1 -1
  67. package/dist/schema/v2/tenant-policy.d.ts +7 -2
  68. package/dist/schema/v2/tenant-policy.d.ts.map +1 -1
  69. package/dist/schema/v2/tenant-policy.js +26 -0
  70. package/dist/schema/v2/tenant-policy.js.map +1 -1
  71. package/dist/sql/bind.d.ts +27 -7
  72. package/dist/sql/bind.d.ts.map +1 -1
  73. package/dist/sql/bind.js +33 -18
  74. package/dist/sql/bind.js.map +1 -1
  75. package/dist/sql/dialect-spec.d.ts +1 -0
  76. package/dist/sql/dialect-spec.d.ts.map +1 -1
  77. package/dist/sql/dialect-spec.js +7 -2
  78. package/dist/sql/dialect-spec.js.map +1 -1
  79. package/dist/sql/generate.d.ts +17 -11
  80. package/dist/sql/generate.d.ts.map +1 -1
  81. package/dist/sql/generate.js +65 -33
  82. package/dist/sql/generate.js.map +1 -1
  83. package/dist/sql/guardrail-decide.d.ts +58 -0
  84. package/dist/sql/guardrail-decide.d.ts.map +1 -0
  85. package/dist/sql/guardrail-decide.js +161 -0
  86. package/dist/sql/guardrail-decide.js.map +1 -0
  87. package/dist/sql/guardrails.d.ts +52 -0
  88. package/dist/sql/guardrails.d.ts.map +1 -0
  89. package/dist/sql/guardrails.js +100 -0
  90. package/dist/sql/guardrails.js.map +1 -0
  91. package/dist/sql/index.d.ts +2 -1
  92. package/dist/sql/index.d.ts.map +1 -1
  93. package/dist/sql/index.js +2 -1
  94. package/dist/sql/index.js.map +1 -1
  95. package/dist/sql/lexer.d.ts +13 -0
  96. package/dist/sql/lexer.d.ts.map +1 -1
  97. package/dist/sql/lexer.js +42 -7
  98. package/dist/sql/lexer.js.map +1 -1
  99. package/dist/sql/prompt-identifiers.d.ts +9 -0
  100. package/dist/sql/prompt-identifiers.d.ts.map +1 -1
  101. package/dist/sql/prompt-identifiers.js +22 -3
  102. package/dist/sql/prompt-identifiers.js.map +1 -1
  103. package/dist/sql/prompt.d.ts.map +1 -1
  104. package/dist/sql/prompt.js +5 -2
  105. package/dist/sql/prompt.js.map +1 -1
  106. package/dist/sql/rebind.d.ts +116 -0
  107. package/dist/sql/rebind.d.ts.map +1 -0
  108. package/dist/sql/rebind.js +215 -0
  109. package/dist/sql/rebind.js.map +1 -0
  110. package/dist/sql/sensitive-guardrail.d.ts +7 -2
  111. package/dist/sql/sensitive-guardrail.d.ts.map +1 -1
  112. package/dist/sql/sensitive-guardrail.js +14 -33
  113. package/dist/sql/sensitive-guardrail.js.map +1 -1
  114. package/dist/sql/tenant-guardrail.d.ts +3 -14
  115. package/dist/sql/tenant-guardrail.d.ts.map +1 -1
  116. package/dist/sql/tenant-guardrail.js +16 -65
  117. package/dist/sql/tenant-guardrail.js.map +1 -1
  118. package/dist/sql/tenant-placeholders.d.ts +40 -14
  119. package/dist/sql/tenant-placeholders.d.ts.map +1 -1
  120. package/dist/sql/tenant-placeholders.js +92 -48
  121. package/dist/sql/tenant-placeholders.js.map +1 -1
  122. package/dist/sql/tenant-policy-warnings.d.ts +8 -0
  123. package/dist/sql/tenant-policy-warnings.d.ts.map +1 -0
  124. package/dist/sql/tenant-policy-warnings.js +21 -0
  125. package/dist/sql/tenant-policy-warnings.js.map +1 -0
  126. package/dist/sql/tenant-scope-expand.d.ts +46 -0
  127. package/dist/sql/tenant-scope-expand.d.ts.map +1 -0
  128. package/dist/sql/tenant-scope-expand.js +101 -0
  129. package/dist/sql/tenant-scope-expand.js.map +1 -0
  130. package/dist/sql/tsql-batch.d.ts +56 -0
  131. package/dist/sql/tsql-batch.d.ts.map +1 -0
  132. package/dist/sql/tsql-batch.js +246 -0
  133. package/dist/sql/tsql-batch.js.map +1 -0
  134. package/dist/sql/validate.d.ts +18 -3
  135. package/dist/sql/validate.d.ts.map +1 -1
  136. package/dist/sql/validate.js +127 -8
  137. package/dist/sql/validate.js.map +1 -1
  138. package/package.json +2 -2
@@ -0,0 +1,101 @@
1
+ import { TenantScopeError } from "../errors.js";
2
+ import { subtreeRootIds } from "./tenant-hierarchy.js";
3
+ import { validateTenantScope } from "./tenant-scope-validate.js";
4
+ /**
5
+ * Replace a `subtree` access with the per-root access it expands to: `multi_root`
6
+ * with one entry per root the subtree covers (the scope's root first, then its
7
+ * descendant roots breadth-first), including a level with no IDs (`ids: []`), or `ids`
8
+ * when the subtree is the scope's root alone. Other access kinds pass through untouched.
9
+ *
10
+ * Each root's IDs stay under that root, so they bind only to its own placeholder:
11
+ * root tables have separate ID spaces, and folding a client ID into the agency
12
+ * placeholder would match another agency (#338). This relies on every root deriving a distinct
13
+ * placeholder, which `validateTenantScope()` and the policy loader enforce. The seeds are unioned into the
14
+ * `tenantRoot` entry here, not trusted to the resolver, so an ancestor never loses
15
+ * its own rows when a host returns strict descendants only.
16
+ *
17
+ * `ask()` runs this before generation, and a host runs it before a synchronous
18
+ * `bindPreparedQuery()` (`askdb.bind()` does it for you), so there is one expansion (ADR 0014).
19
+ * The scope is validated first (`validateTenantScope`), so an unknown root fails as in `ask()`.
20
+ *
21
+ * Fails closed with `SUBTREE_NOT_RESOLVABLE`: no resolver; a result that is an
22
+ * array (the old flat shape) or not a plain object; a key that isn't a root in
23
+ * this subtree; a value that isn't an array of non-empty strings; or no IDs at all.
24
+ */
25
+ export async function expandTenantScope(policy, scope, resolve) {
26
+ validateTenantScope(policy, scope);
27
+ const access = scope.access;
28
+ if (access.kind !== "subtree")
29
+ return scope;
30
+ const { tenantRoot, rootIds } = access;
31
+ const levels = subtreeRootIds(policy, tenantRoot);
32
+ const shape = `{ ${levels.map((root) => `"${root}": [...]`).join(", ")} }`;
33
+ const fail = (message) => new TenantScopeError(message, "SUBTREE_NOT_RESOLVABLE");
34
+ if (!resolve) {
35
+ throw fail(`tenantScope.access is a 'subtree' of '${tenantRoot}', but no ` +
36
+ "resolveTenantDescendants was passed. AskDB does not query your database " +
37
+ "to find descendants: pass resolveTenantDescendants to expand the seed IDs, or pass " +
38
+ "an 'ids' or 'multi_root' access with each root's IDs already expanded.");
39
+ }
40
+ const result = await resolve(tenantRoot, rootIds);
41
+ if (Array.isArray(result)) {
42
+ throw fail(`resolveTenantDescendants for '${tenantRoot}' returned an array. It must return IDs per ` +
43
+ `tenant root, keyed by root table id: ${shape}. Root tables have separate ID spaces, so ` +
44
+ "a flat list can't say which root each ID belongs to, and binding them all to " +
45
+ `'${tenantRoot}' would match other tenants. Put the seeds and any same-table ` +
46
+ `descendants under '${tenantRoot}', and each descendant root's IDs under that root.`);
47
+ }
48
+ if (!isPlainObject(result)) {
49
+ throw fail(`resolveTenantDescendants for '${tenantRoot}' must return an object mapping each ` +
50
+ `tenant root in the subtree to its IDs: ${shape}.`);
51
+ }
52
+ // Read the result exactly once: validate and build from this snapshot, so a getter
53
+ // can't return one value to validation and another to the scope, and a property
54
+ // validation can't see (non-enumerable) is never read at all.
55
+ const idsByRoot = new Map();
56
+ const knownRoots = new Set(policy.roots.map((root) => root.id));
57
+ let returned = 0;
58
+ for (const [root, value] of Object.entries(result)) {
59
+ const ids = Array.isArray(value) ? [...value] : value;
60
+ if (!knownRoots.has(root)) {
61
+ throw fail(`resolveTenantDescendants returned IDs under a key the subtree can't have: '${root}' is ` +
62
+ `not a tenant root in the policy. Allowed keys for a subtree of '${tenantRoot}': ` +
63
+ `${levels.join(", ")}.`);
64
+ }
65
+ if (!levels.includes(root)) {
66
+ throw fail(`resolveTenantDescendants returned IDs under a key the subtree can't have: '${root}' is ` +
67
+ `not in the subtree of '${tenantRoot}' (the policy's hierarchy doesn't reach it from ` +
68
+ `there). Allowed keys: ${levels.join(", ")}.`);
69
+ }
70
+ if (!isTenantIdArray(ids)) {
71
+ throw fail(`resolveTenantDescendants: the IDs for '${root}' must be an array of non-empty strings.`);
72
+ }
73
+ idsByRoot.set(root, ids);
74
+ returned += ids.length;
75
+ }
76
+ if (returned === 0) {
77
+ throw fail(`resolveTenantDescendants returned no IDs for '${tenantRoot}' (seeds: ${rootIds.join(", ")}). ` +
78
+ `Return at least the seeds under '${tenantRoot}'; refusing to build an empty tenant scope.`);
79
+ }
80
+ // Every level the subtree covers stays in the scope, even one with no IDs. The
81
+ // guardrail then checks a read of that root on its own placeholder, and binding that
82
+ // placeholder fails closed (UNRESOLVED_TENANT_PLACEHOLDER). Dropping the level would
83
+ // leave its rows readable through a parent's foreign key or an ancestor, which is
84
+ // less restricted than a level the resolver narrowed to some IDs.
85
+ const scopes = levels.map((root) => {
86
+ const own = idsByRoot.get(root) ?? [];
87
+ return { tenantRoot: root, ids: [...new Set(root === tenantRoot ? [...rootIds, ...own] : own)] };
88
+ });
89
+ const expanded = scopes.length === 1 ? { kind: "ids", ...scopes[0] } : { kind: "multi_root", scopes };
90
+ return { ...scope, access: expanded };
91
+ }
92
+ function isTenantIdArray(value) {
93
+ return Array.isArray(value) && value.every((id) => typeof id === "string" && id.length > 0);
94
+ }
95
+ function isPlainObject(value) {
96
+ if (typeof value !== "object" || value === null)
97
+ return false;
98
+ const proto = Object.getPrototypeOf(value);
99
+ return proto === Object.prototype || proto === null;
100
+ }
101
+ //# sourceMappingURL=tenant-scope-expand.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-scope-expand.js","sourceRoot":"","sources":["../../src/sql/tenant-scope-expand.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Where a T-SQL batch starts a second statement without a `;`.
3
+ *
4
+ * SQL Server needs no separator between statements: `SELECT 1 SELECT 2` is two
5
+ * statements, and Studio's Execute runs the validated SQL inside a batch. This check
6
+ * finds the second statement in SQL that is otherwise a single SELECT. Only the top
7
+ * level (outside every bracket) is checked: a second statement inside brackets is a
8
+ * syntax error, and SQL Server runs nothing from a batch that does not compile.
9
+ *
10
+ * Verbs that start a statement (`DECLARE`, `WHILE`, `EXEC`, …) are the keyword
11
+ * denylist's job (`SQLSERVER_DIALECT.extraForbiddenKeywords`). This module covers the
12
+ * statements no verb announces:
13
+ *
14
+ * - a top-level `SELECT` that follows no set operator and is not the main query
15
+ * after a leading `WITH` list;
16
+ * - a top-level `(SELECT …` right after a complete expression, such as
17
+ * `SELECT 1 (SELECT 2)`. After a keyword, an operator, a comma or a function name
18
+ * the bracket opens a subquery instead;
19
+ * - a label: a `:` written directly against anything but `(`, a comma or an operator.
20
+ *
21
+ * It also rejects code SQL Server may split into tokens differently from the lexer
22
+ * ({@link findAmbiguousTsqlText}): rather than copy every corner of SQL Server's
23
+ * tokenizer, the check accepts plain code only.
24
+ */
25
+ import type { SqlToken } from "./lexer.js";
26
+ /** The token where a second statement starts. */
27
+ export type TsqlStatementBoundary = {
28
+ token: SqlToken;
29
+ /**
30
+ * `select`: a top-level SELECT that continues nothing. `query`: a top-level
31
+ * `(SELECT …` after a complete expression. `label`: a `:` that ends a label.
32
+ */
33
+ kind: "select" | "query" | "label";
34
+ };
35
+ /**
36
+ * Code SQL Server may split into tokens differently from the lexer, described for an
37
+ * error message, or `undefined`. Text inside strings and quoted identifiers is not
38
+ * checked; `tokens` holds no comments. Fails closed on:
39
+ *
40
+ * - a character outside {@link PLAIN_CODE}: Unicode whitespace and format characters
41
+ * (U+00A0, U+200B, U+FEFF), symbols, currency signs other than `$`, `\` (a money
42
+ * sign to SQL Server) and ASCII control characters;
43
+ * - a money sign with no digits after it (`$ .5` is one money literal to SQL Server);
44
+ * - a number written directly against a word or variable (`1ESELECT`, `0xSELECT`);
45
+ * - a `?` written directly against a name or number: Studio binds it as `@p0`, which
46
+ * SQL Server reads as part of that name (`x?:` becomes the label `x@p0:`).
47
+ *
48
+ * Model-written SQL contains none of these.
49
+ */
50
+ export declare function findAmbiguousTsqlText(sql: string, tokens: readonly SqlToken[]): string | undefined;
51
+ /**
52
+ * The first place `tokens` starts a second T-SQL statement, or `undefined`.
53
+ * `tokens` holds no comments and no trailing `;`.
54
+ */
55
+ export declare function findTsqlStatementBoundary(tokens: readonly SqlToken[]): TsqlStatementBoundary | undefined;
56
+ //# sourceMappingURL=tsql-batch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsql-batch.d.ts","sourceRoot":"","sources":["../../src/sql/tsql-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAG3C,iDAAiD;AACjD,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,IAAI,EAAE,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;CACpC,CAAC;AAqCF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,MAAM,GAAG,SAAS,CA6BlG;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,qBAAqB,GAAG,SAAS,CA8HxG"}
@@ -0,0 +1,246 @@
1
+ import { SQLSERVER_RESERVED_WORDS } from "./reserved-words.js";
2
+ /**
3
+ * Reserved words after which an expression or a subquery follows, so a `(SELECT …`
4
+ * after one of them is a subquery. Each is reserved in T-SQL, so none is an alias.
5
+ */
6
+ const EXPRESSION_KEYWORDS = new Set([
7
+ "select", "distinct", "all", "any", "some", "top", "percent",
8
+ "from", "join", "where", "on", "having", "by",
9
+ "and", "or", "not", "in", "exists", "like", "between", "escape",
10
+ "case", "when", "then", "else",
11
+ "union", "except", "intersect",
12
+ ]);
13
+ /** Reserved words that are also function names, so `name((SELECT …))` is a call. */
14
+ const RESERVED_FUNCTIONS = new Set([
15
+ "coalesce", "nullif", "convert", "try_convert", "left", "right",
16
+ "contains", "containstable", "freetext", "freetexttable", "openxml", "tsequal",
17
+ ]);
18
+ /**
19
+ * Punctuation an expression may follow: `(`, a comma, an operator, and `.` or `::`
20
+ * before a qualified function name.
21
+ */
22
+ const EXPRESSION_PUNCTUATION = new Set(["(", ",", "=", "<", ">", "+", "-", "*", "/", "%", "&", "|", "^", "~", ".", "::"]);
23
+ const RESERVED = new Set(SQLSERVER_RESERVED_WORDS);
24
+ const SET_OPERATORS = new Set(["union", "except", "intersect"]);
25
+ /**
26
+ * A character allowed in T-SQL code outside strings and quoted names: printable ASCII
27
+ * except `\`, tab, line feed, carriage return, and Unicode letters, combining marks
28
+ * and decimal digits.
29
+ */
30
+ const PLAIN_CODE = /[\t\n\r\x20-\x5b\x5d-\x7e\p{L}\p{M}\p{Nd}]/u;
31
+ const MONEY_SIGN = /^\p{Sc}$/u;
32
+ /**
33
+ * Code SQL Server may split into tokens differently from the lexer, described for an
34
+ * error message, or `undefined`. Text inside strings and quoted identifiers is not
35
+ * checked; `tokens` holds no comments. Fails closed on:
36
+ *
37
+ * - a character outside {@link PLAIN_CODE}: Unicode whitespace and format characters
38
+ * (U+00A0, U+200B, U+FEFF), symbols, currency signs other than `$`, `\` (a money
39
+ * sign to SQL Server) and ASCII control characters;
40
+ * - a money sign with no digits after it (`$ .5` is one money literal to SQL Server);
41
+ * - a number written directly against a word or variable (`1ESELECT`, `0xSELECT`);
42
+ * - a `?` written directly against a name or number: Studio binds it as `@p0`, which
43
+ * SQL Server reads as part of that name (`x?:` becomes the label `x@p0:`).
44
+ *
45
+ * Model-written SQL contains none of these.
46
+ */
47
+ export function findAmbiguousTsqlText(sql, tokens) {
48
+ let from = 0;
49
+ const scanTo = (end) => {
50
+ for (const ch of sql.slice(from, end)) {
51
+ if (PLAIN_CODE.test(ch))
52
+ continue;
53
+ if (ch === "\\")
54
+ return "the character '\\'";
55
+ return `the character U+${ch.codePointAt(0).toString(16).toUpperCase().padStart(4, "0")}`;
56
+ }
57
+ return undefined;
58
+ };
59
+ for (const [i, t] of tokens.entries()) {
60
+ if (t.kind === "string" || t.kind === "quoted_identifier") {
61
+ const found = scanTo(t.start);
62
+ if (found)
63
+ return found;
64
+ from = t.end;
65
+ continue;
66
+ }
67
+ const prev = tokens[i - 1];
68
+ if (isPunct(t, "?") && prev?.end === t.start && prev.kind !== "punct" && prev.kind !== "string") {
69
+ return `'${prev.text}?', a parameter marker written against a name`;
70
+ }
71
+ if (t.kind !== "number")
72
+ continue;
73
+ if (MONEY_SIGN.test(t.text))
74
+ return `the money sign '${t.text}'`;
75
+ const next = tokens[i + 1];
76
+ if (next?.start === t.end && (next.kind === "word" || next.kind === "parameter")) {
77
+ return `'${t.text}${next.text}', a number written against a word`;
78
+ }
79
+ }
80
+ return scanTo(sql.length);
81
+ }
82
+ /**
83
+ * The first place `tokens` starts a second T-SQL statement, or `undefined`.
84
+ * `tokens` holds no comments and no trailing `;`.
85
+ */
86
+ export function findTsqlStatementBoundary(tokens) {
87
+ /** Index of each `)` mapped to the index of the `(` it closes. */
88
+ const opener = new Map();
89
+ /** A top-level `ORDER BY` came earlier, so a later `OFFSET` is the keyword. */
90
+ let orderBy = false;
91
+ /** `tokens[i]` is `(` and a `SELECT` follows it, after any further `(`. */
92
+ const opensQuery = (i) => {
93
+ let j = i;
94
+ while (isPunct(tokens[j], "("))
95
+ j++;
96
+ return isWord(tokens[j], "select");
97
+ };
98
+ /**
99
+ * The word at `j` is a keyword an expression follows. Keywords SQL Server does not
100
+ * reserve count only in their clause; anywhere else they are aliases
101
+ * (`SELECT 1 offset (SELECT 2)` is two statements).
102
+ */
103
+ const introducesExpression = (j) => {
104
+ const word = tokens[j].lower;
105
+ if (EXPRESSION_KEYWORDS.has(word))
106
+ return true;
107
+ const prev = tokens[j - 1];
108
+ switch (word) {
109
+ case "offset":
110
+ // After a sort item, not as one (`ORDER BY offset` sorts by a column).
111
+ return orderBy && !isWord(prev, "by") && !isPunct(prev, ",");
112
+ case "next":
113
+ case "first":
114
+ return isWord(prev, "fetch");
115
+ case "ties":
116
+ return isWord(prev, "with");
117
+ case "apply":
118
+ return isWord(prev, "cross") || isWord(prev, "outer");
119
+ case "zone":
120
+ return isWord(prev, "time") && isWord(tokens[j - 2], "at");
121
+ default:
122
+ return false;
123
+ }
124
+ };
125
+ /**
126
+ * An expression may start right after `tokens[j]`, so a `(SELECT …` there is a
127
+ * subquery. Fails closed: anything not listed (a value, a name, `}`, an unknown
128
+ * keyword) ends the statement, and a `(SELECT …` after it starts a second one.
129
+ */
130
+ const expressionMayFollow = (j) => {
131
+ const t = tokens[j];
132
+ if (!t)
133
+ return true;
134
+ switch (t.kind) {
135
+ case "number":
136
+ case "string":
137
+ case "parameter":
138
+ // `TOP 5 (SELECT …)`: the count, then the first select-list item.
139
+ return isWord(tokens[j - 1], "top");
140
+ case "word":
141
+ return introducesExpression(j);
142
+ case "punct": {
143
+ if (t.text !== ")")
144
+ return EXPRESSION_PUNCTUATION.has(t.text);
145
+ const openIndex = opener.get(j);
146
+ // `TOP (5) (SELECT …)`.
147
+ return openIndex !== undefined && isWord(tokens[openIndex - 1], "top");
148
+ }
149
+ default:
150
+ return false;
151
+ }
152
+ };
153
+ /**
154
+ * `name((SELECT …))` where `name` is a function: SQL Server reads an unquoted `name(`
155
+ * as a call whenever `name` is not an alias, that is, when an expression may start
156
+ * before it. A quoted name is a call only when qualified (`[dbo].[fn](`); alone,
157
+ * `[name]` is a column.
158
+ */
159
+ const opensCallArgument = (i) => {
160
+ if (!isPunct(tokens[i + 1], "("))
161
+ return false;
162
+ const name = tokens[i - 1];
163
+ const callable = (name?.kind === "quoted_identifier" && isPunct(tokens[i - 2], ".")) ||
164
+ (name?.kind === "word" && (!RESERVED.has(name.lower) || RESERVED_FUNCTIONS.has(name.lower)));
165
+ return callable && expressionMayFollow(i - 2);
166
+ };
167
+ const open = [];
168
+ let headSeen = false;
169
+ /** After a leading `WITH`, the main query that follows the CTE list is still to come. */
170
+ let mainPending = false;
171
+ for (const [i, t] of tokens.entries()) {
172
+ const topLevel = open.length === 0;
173
+ if (isPunct(t, "(")) {
174
+ if (topLevel && opensQuery(i)) {
175
+ const prev = tokens[i - 1];
176
+ if (mainPending && (isWord(prev, "as") || isWord(prev, "materialized"))) {
177
+ // A CTE body.
178
+ }
179
+ else if (mainPending && isPunct(prev, ")")) {
180
+ mainPending = false;
181
+ }
182
+ else if (!expressionMayFollow(i - 1) && !opensCallArgument(i)) {
183
+ return { token: t, kind: "query" };
184
+ }
185
+ }
186
+ open.push(i);
187
+ continue;
188
+ }
189
+ if (isPunct(t, ")")) {
190
+ const openIndex = open.pop();
191
+ if (openIndex !== undefined)
192
+ opener.set(i, openIndex);
193
+ if (open.length === 0 && startsLabel(t, tokens[i + 1]))
194
+ return { token: t, kind: "label" };
195
+ continue;
196
+ }
197
+ if (!headSeen) {
198
+ headSeen = true;
199
+ mainPending = isWord(t, "with");
200
+ continue;
201
+ }
202
+ if (!topLevel)
203
+ continue;
204
+ if (startsLabel(t, tokens[i + 1]))
205
+ return { token: t, kind: "label" };
206
+ if (isWord(t, "by") && isWord(tokens[i - 1], "order"))
207
+ orderBy = true;
208
+ if (isWord(t, "select")) {
209
+ if (mainPending && isPunct(tokens[i - 1], ")")) {
210
+ mainPending = false;
211
+ }
212
+ else if (!followsSetOperator(tokens, i)) {
213
+ return { token: t, kind: "select" };
214
+ }
215
+ }
216
+ }
217
+ return undefined;
218
+ }
219
+ /** `UNION [ALL | DISTINCT]`, `EXCEPT` or `INTERSECT` right before `tokens[i]`. */
220
+ function followsSetOperator(tokens, i) {
221
+ let prev = tokens[i - 1];
222
+ if (isWord(prev, "all") || isWord(prev, "distinct"))
223
+ prev = tokens[i - 2];
224
+ return prev?.kind === "word" && SET_OPERATORS.has(prev.lower);
225
+ }
226
+ /**
227
+ * A `:` written directly against `t`, which T-SQL reads as ending a label (`name :` is a
228
+ * syntax error). Fails closed: any `t` counts except `(`, a comma or an operator, where
229
+ * an AskDB `:name` placeholder may follow, because SQL Server's label names include
230
+ * forms the lexer splits (`#@:`, `#1#:`). The lexer reads `l:SELECT` as `l` and a
231
+ * `:SELECT` parameter; `::` is one token and never a label.
232
+ */
233
+ function startsLabel(t, next) {
234
+ if (next?.start !== t.end)
235
+ return false;
236
+ if (t.kind === "punct" && t.text !== "." && t.text !== "::" && EXPRESSION_PUNCTUATION.has(t.text))
237
+ return false;
238
+ return isPunct(next, ":") || (next.kind === "parameter" && next.text.startsWith(":"));
239
+ }
240
+ function isPunct(t, text) {
241
+ return t?.kind === "punct" && t.text === text;
242
+ }
243
+ function isWord(t, lower) {
244
+ return t?.kind === "word" && t.lower === lower;
245
+ }
246
+ //# sourceMappingURL=tsql-batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tsql-batch.js","sourceRoot":"","sources":["../../src/sql/tsql-batch.ts"],"names":[],"mappings":"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"}
@@ -14,9 +14,17 @@ import { type DialectSpec } from "./dialect-spec.js";
14
14
  * 3. `SQL_MULTI_STATEMENT` — any `;` other than a single trailing one.
15
15
  * 4. `SQL_COMMENT` — any comment (`--`, `/* *\/`, and MySQL `#`).
16
16
  * 5. `SQL_NOT_SELECT_OR_WITH` — the first token (after any `(`) is not `SELECT`/`WITH`.
17
- * 6. `SQL_FORBIDDEN_KEYWORD` — an unquoted keyword from the shared denylist or the
17
+ * 6. `SQL_MULTI_STATEMENT`, SQL Server lexer only (so also for a dialect id that is no
18
+ * built-in engine) — a second statement written without `;`, which T-SQL doesn't
19
+ * need, or code SQL Server may split differently from the lexer (see `tsql-batch.ts`).
20
+ * 7. `SQL_LOCKING_READ` — a locking read, at any depth: `FOR UPDATE`, `FOR NO KEY UPDATE`,
21
+ * `FOR SHARE`, `FOR KEY SHARE` or `LOCK IN SHARE MODE` on every lexer, and on the SQL Server
22
+ * lexer a lock hint such as `UPDLOCK` or `HOLDLOCK` unquoted anywhere, or quoted alone in
23
+ * parentheses right after a name, at any depth and with trailing spaces, and any such name
24
+ * that isn't printable ASCII, which SQL Server may read as a hint (#319).
25
+ * 8. `SQL_FORBIDDEN_KEYWORD` — an unquoted keyword from the shared denylist or the
18
26
  * dialect's `extraForbiddenKeywords`.
19
- * 7. `SQL_FORBIDDEN_FUNCTION` — a call (`name(`) to a function in the dialect's
27
+ * 9. `SQL_FORBIDDEN_FUNCTION` — a call (`name(`) to a function in the dialect's
20
28
  * `blockedFunctions` (side-effecting or file/network/sleep functions).
21
29
  *
22
30
  * Then the dialect's `extraValidate` runs on the statement without its trailing `;`.
@@ -32,9 +40,16 @@ export declare function validateSelectSql(dialect: DialectSpec, sql: string): st
32
40
  /**
33
41
  * The words `validateSelectSql` rejects unquoted for a dialect, lowercase: the shared denylist
34
42
  * and the spec's `extraForbiddenKeywords`, plus every built-in spec's for an id that is no
35
- * built-in engine family. The NL→SQL prompt quotes names in this set.
43
+ * built-in engine family, and T-SQL's lock hints wherever the SQL Server lexer runs. The
44
+ * NL→SQL prompt quotes names in this set.
36
45
  */
37
46
  export declare function keywordsRejectedUnquoted(dialect: Pick<DialectSpec, "id" | "backslashEscapes" | "extraForbiddenKeywords">): Set<string>;
47
+ /**
48
+ * Whether `validateSelectSql` runs the SQL Server lexer, and so the T-SQL hint checks, for a
49
+ * dialect: `sqlserver`, and an id that is no built-in engine family, which is checked every
50
+ * engine's way. The NL→SQL prompt follows it (#319).
51
+ */
52
+ export declare function checkedAsTsql(dialect: Pick<DialectSpec, "id" | "backslashEscapes">): boolean;
38
53
  /** Explanation of guardrails satisfied by a string already passing {@link validateSelectSql}. */
39
54
  export type SelectGuardrailExplain = {
40
55
  statementKind: "select" | "with";
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/sql/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAwCxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAyB3E;AAmFD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,kBAAkB,GAAG,wBAAwB,CAAC,GAC/E,GAAG,CAAC,MAAM,CAAC,CAIb;AAsCD,iGAAiG;AACjG,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,6FAA6F;AAC7F,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,sBAAsB,CAiB5F"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/sql/validate.ts"],"names":[],"mappings":"AACA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAyCxE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAyB3E;AAsHD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,kBAAkB,GAAG,wBAAwB,CAAC,GAC/E,GAAG,CAAC,MAAM,CAAC,CAIb;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,kBAAkB,CAAC,GAAG,OAAO,CAG5F;AAqHD,iGAAiG;AACjG,MAAM,MAAM,sBAAsB,GAAG;IACnC,aAAa,EAAE,QAAQ,GAAG,MAAM,CAAC;IACjC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,6FAA6F;AAC7F,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,sBAAsB,CAkB5F"}