@askdb/core 1.0.0-beta.43 → 1.0.0-beta.45

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 (121) hide show
  1. package/README.md +15 -12
  2. package/dist/ask.d.ts +39 -38
  3. package/dist/ask.d.ts.map +1 -1
  4. package/dist/ask.js +135 -309
  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 +41 -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 +6 -2
  15. package/dist/enrichment/suggest.js.map +1 -1
  16. package/dist/enrichment/types.d.ts +20 -1
  17. package/dist/enrichment/types.d.ts.map +1 -1
  18. package/dist/errors.d.ts +68 -1
  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 +8 -5
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +6 -3
  25. package/dist/index.js.map +1 -1
  26. package/dist/retrieval/synthesize-ddl.d.ts +11 -1
  27. package/dist/retrieval/synthesize-ddl.d.ts.map +1 -1
  28. package/dist/retrieval/synthesize-ddl.js +39 -34
  29. package/dist/retrieval/synthesize-ddl.js.map +1 -1
  30. package/dist/schema/normalize.d.ts +16 -0
  31. package/dist/schema/normalize.d.ts.map +1 -1
  32. package/dist/schema/normalize.js +1 -0
  33. package/dist/schema/normalize.js.map +1 -1
  34. package/dist/schema/v2/format.d.ts +35 -1
  35. package/dist/schema/v2/format.d.ts.map +1 -1
  36. package/dist/schema/v2/format.js +69 -22
  37. package/dist/schema/v2/format.js.map +1 -1
  38. package/dist/schema/v2/index.d.ts +2 -0
  39. package/dist/schema/v2/index.d.ts.map +1 -1
  40. package/dist/schema/v2/index.js +1 -0
  41. package/dist/schema/v2/index.js.map +1 -1
  42. package/dist/schema/v2/loader.d.ts +30 -4
  43. package/dist/schema/v2/loader.d.ts.map +1 -1
  44. package/dist/schema/v2/loader.js +90 -28
  45. package/dist/schema/v2/loader.js.map +1 -1
  46. package/dist/schema/v2/mentions.d.ts +43 -0
  47. package/dist/schema/v2/mentions.d.ts.map +1 -0
  48. package/dist/schema/v2/mentions.js +101 -0
  49. package/dist/schema/v2/mentions.js.map +1 -0
  50. package/dist/schema/v2/normalized.d.ts +5 -0
  51. package/dist/schema/v2/normalized.d.ts.map +1 -1
  52. package/dist/schema/v2/tenant-policy.d.ts +2 -2
  53. package/dist/sql/bind.d.ts +18 -5
  54. package/dist/sql/bind.d.ts.map +1 -1
  55. package/dist/sql/bind.js +36 -15
  56. package/dist/sql/bind.js.map +1 -1
  57. package/dist/sql/dialect-spec.d.ts +27 -1
  58. package/dist/sql/dialect-spec.d.ts.map +1 -1
  59. package/dist/sql/dialect-spec.js +20 -1
  60. package/dist/sql/dialect-spec.js.map +1 -1
  61. package/dist/sql/generate.d.ts +19 -11
  62. package/dist/sql/generate.d.ts.map +1 -1
  63. package/dist/sql/generate.js +72 -34
  64. package/dist/sql/generate.js.map +1 -1
  65. package/dist/sql/guardrail-decide.d.ts +56 -0
  66. package/dist/sql/guardrail-decide.d.ts.map +1 -0
  67. package/dist/sql/guardrail-decide.js +161 -0
  68. package/dist/sql/guardrail-decide.js.map +1 -0
  69. package/dist/sql/guardrails.d.ts +52 -0
  70. package/dist/sql/guardrails.d.ts.map +1 -0
  71. package/dist/sql/guardrails.js +100 -0
  72. package/dist/sql/guardrails.js.map +1 -0
  73. package/dist/sql/index.d.ts +4 -2
  74. package/dist/sql/index.d.ts.map +1 -1
  75. package/dist/sql/index.js +4 -2
  76. package/dist/sql/index.js.map +1 -1
  77. package/dist/sql/lexer.d.ts +13 -0
  78. package/dist/sql/lexer.d.ts.map +1 -1
  79. package/dist/sql/lexer.js +42 -7
  80. package/dist/sql/lexer.js.map +1 -1
  81. package/dist/sql/prompt-identifiers.d.ts +20 -0
  82. package/dist/sql/prompt-identifiers.d.ts.map +1 -0
  83. package/dist/sql/prompt-identifiers.js +64 -0
  84. package/dist/sql/prompt-identifiers.js.map +1 -0
  85. package/dist/sql/prompt.d.ts +7 -1
  86. package/dist/sql/prompt.d.ts.map +1 -1
  87. package/dist/sql/prompt.js +24 -3
  88. package/dist/sql/prompt.js.map +1 -1
  89. package/dist/sql/rebind.d.ts +89 -0
  90. package/dist/sql/rebind.d.ts.map +1 -0
  91. package/dist/sql/rebind.js +175 -0
  92. package/dist/sql/rebind.js.map +1 -0
  93. package/dist/sql/reserved-words.d.ts +15 -0
  94. package/dist/sql/reserved-words.d.ts.map +1 -0
  95. package/dist/sql/reserved-words.js +102 -0
  96. package/dist/sql/reserved-words.js.map +1 -0
  97. package/dist/sql/sensitive-guardrail.d.ts +7 -2
  98. package/dist/sql/sensitive-guardrail.d.ts.map +1 -1
  99. package/dist/sql/sensitive-guardrail.js +20 -37
  100. package/dist/sql/sensitive-guardrail.js.map +1 -1
  101. package/dist/sql/tenant-guardrail.d.ts +3 -14
  102. package/dist/sql/tenant-guardrail.d.ts.map +1 -1
  103. package/dist/sql/tenant-guardrail.js +16 -65
  104. package/dist/sql/tenant-guardrail.js.map +1 -1
  105. package/dist/sql/tenant-placeholders.d.ts +12 -23
  106. package/dist/sql/tenant-placeholders.d.ts.map +1 -1
  107. package/dist/sql/tenant-placeholders.js +46 -27
  108. package/dist/sql/tenant-placeholders.js.map +1 -1
  109. package/dist/sql/tenant-scope-expand.d.ts +46 -0
  110. package/dist/sql/tenant-scope-expand.d.ts.map +1 -0
  111. package/dist/sql/tenant-scope-expand.js +101 -0
  112. package/dist/sql/tenant-scope-expand.js.map +1 -0
  113. package/dist/sql/tsql-batch.d.ts +56 -0
  114. package/dist/sql/tsql-batch.d.ts.map +1 -0
  115. package/dist/sql/tsql-batch.js +246 -0
  116. package/dist/sql/tsql-batch.js.map +1 -0
  117. package/dist/sql/validate.d.ts +16 -3
  118. package/dist/sql/validate.d.ts.map +1 -1
  119. package/dist/sql/validate.js +43 -8
  120. package/dist/sql/validate.js.map +1 -1
  121. package/package.json +9 -6
@@ -0,0 +1,56 @@
1
+ import { type GuardrailFinding, type GuardrailForm, type GuardrailVerdict, type SensitiveReference, type SensitiveReferenceRuleCode, type SensitiveScopeReport, type TenantGuardrailWarning } from "../errors.js";
2
+ /**
3
+ * The decision point (ADR 0010, decision 2): the only code that knows what `strict`,
4
+ * `warn` and `off` mean, at each enforcement point. A leaf module: the checks and the
5
+ * enforcement points import it, and it imports only the error vocabulary.
6
+ */
7
+ /** `return`: `ask()` / `generateSelectSql()` handing SQL back. `rebind`: `bindPreparedQuery()`. */
8
+ export type GuardrailPoint = "return" | "rebind";
9
+ export type GuardrailModes = {
10
+ /** The tenant policy's `enforcement`; absent without a policy. */
11
+ tenant?: "warn" | "strict";
12
+ /** The caller's `sensitiveGuardrailMode`, default `"warn"`. With `"off"` the check doesn't run. */
13
+ sensitive: "off" | "warn" | "strict";
14
+ /** Rebind only: checks whose `warn` the caller accepts. */
15
+ acceptWarnings?: ReadonlyArray<"tenant">;
16
+ };
17
+ /**
18
+ * Map findings to a verdict. Read-only findings always deny. Tenant findings deny under
19
+ * `strict`; under `warn` they warn when returned, and deny at rebind unless the caller
20
+ * lists `"tenant"` in `acceptWarnings`. Sensitive findings deny under `strict` and warn
21
+ * otherwise, at both points. The outcome is the most severe finding's.
22
+ */
23
+ export declare function decide(findings: GuardrailFinding[], modes: GuardrailModes, point: GuardrailPoint): GuardrailVerdict;
24
+ /**
25
+ * Throw the typed error for a `deny` verdict, with the verdict attached. When several
26
+ * checks deny, the precedence is read-only (`SqlValidationError`), then tenant
27
+ * (`TenantGuardrailError`), then sensitive (`SensitiveReferenceError`).
28
+ */
29
+ export declare function throwIfDenied(verdict: GuardrailVerdict, modes: GuardrailModes, point: GuardrailPoint): void;
30
+ /** Tenant findings for one form, from the rule's warnings. */
31
+ export declare function tenantFindings(warnings: readonly TenantGuardrailWarning[], form: GuardrailForm | "generator"): GuardrailFinding[];
32
+ /** The tenant findings as warnings, once each across forms (same rule, table and message). */
33
+ export declare function tenantWarnings(findings: readonly GuardrailFinding[]): TenantGuardrailWarning[];
34
+ /** `result.tenantGuardrail`, derived from the verdict. */
35
+ export declare function tenantGuardrailResult(findings: readonly GuardrailFinding[]): {
36
+ passed: boolean;
37
+ warnings: TenantGuardrailWarning[];
38
+ };
39
+ type SensitiveScan = {
40
+ references: SensitiveReference[];
41
+ unresolvedScope?: SensitiveScopeReport;
42
+ };
43
+ /** Render a reference as `schema.table.column` (or `table.column` when the schema has no namespace). */
44
+ export declare function formatSensitiveReference(ref: SensitiveReference): string;
45
+ /** Sensitive findings for one form: one per reference, plus one when the scope wasn't resolved. */
46
+ export declare function sensitiveFindings(scan: SensitiveScan, form: GuardrailForm): GuardrailFinding[];
47
+ /** `result.sensitiveGuardrail`, derived from the verdict: references once each across forms. */
48
+ export declare function sensitiveGuardrailResult(findings: readonly GuardrailFinding[]): {
49
+ passed: boolean;
50
+ references: SensitiveReference[];
51
+ unresolvedScope?: SensitiveScopeReport;
52
+ };
53
+ export declare function sensitiveRuleFor(scan: SensitiveScan): SensitiveReferenceRuleCode;
54
+ export declare function sensitiveStrictMessage(scan: SensitiveScan): string;
55
+ export {};
56
+ //# sourceMappingURL=guardrail-decide.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrail-decide.d.ts","sourceRoot":"","sources":["../../src/sql/guardrail-decide.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAElB,KAAK,gBAAgB,EACrB,KAAK,kBAAkB,EACvB,KAAK,0BAA0B,EAC/B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC5B,MAAM,cAAc,CAAC;AAEtB;;;;GAIG;AAEH,mGAAmG;AACnG,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjD,MAAM,MAAM,cAAc,GAAG;IAC3B,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC3B,mGAAmG;IACnG,SAAS,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IACrC,2DAA2D;IAC3D,cAAc,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAC1C,CAAC;AAIF;;;;;GAKG;AACH,wBAAgB,MAAM,CACpB,QAAQ,EAAE,gBAAgB,EAAE,EAC5B,KAAK,EAAE,cAAc,EACrB,KAAK,EAAE,cAAc,GACpB,gBAAgB,CAOlB;AAiBD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI,CAgC3G;AAMD,8DAA8D;AAC9D,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,SAAS,sBAAsB,EAAE,EAC3C,IAAI,EAAE,aAAa,GAAG,WAAW,GAChC,gBAAgB,EAAE,CAEpB;AAED,8FAA8F;AAC9F,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG,sBAAsB,EAAE,CAW9F;AAED,0DAA0D;AAC1D,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG;IAC5E,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,sBAAsB,EAAE,CAAC;CACpC,CAGA;AAMD,KAAK,aAAa,GAAG;IAAE,UAAU,EAAE,kBAAkB,EAAE,CAAC;IAAC,eAAe,CAAC,EAAE,oBAAoB,CAAA;CAAE,CAAC;AAElG,wGAAwG;AACxG,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,kBAAkB,GAAG,MAAM,CAGxE;AAED,mGAAmG;AACnG,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,GAAG,gBAAgB,EAAE,CAuB9F;AAED,gGAAgG;AAChG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,GAAG;IAC/E,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,kBAAkB,EAAE,CAAC;IACjC,eAAe,CAAC,EAAE,oBAAoB,CAAC;CACxC,CAiBA;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,0BAA0B,CAIhF;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,aAAa,GAAG,MAAM,CAOlE"}
@@ -0,0 +1,161 @@
1
+ import { SensitiveReferenceError, SqlValidationError, TenantGuardrailError, } from "../errors.js";
2
+ const SEVERITY = { allow: 0, warn: 1, deny: 2 };
3
+ /**
4
+ * Map findings to a verdict. Read-only findings always deny. Tenant findings deny under
5
+ * `strict`; under `warn` they warn when returned, and deny at rebind unless the caller
6
+ * lists `"tenant"` in `acceptWarnings`. Sensitive findings deny under `strict` and warn
7
+ * otherwise, at both points. The outcome is the most severe finding's.
8
+ */
9
+ export function decide(findings, modes, point) {
10
+ let outcome = "allow";
11
+ for (const finding of findings) {
12
+ const o = outcomeOf(finding.check, modes, point);
13
+ if (SEVERITY[o] > SEVERITY[outcome])
14
+ outcome = o;
15
+ }
16
+ return { outcome, findings };
17
+ }
18
+ function outcomeOf(check, modes, point) {
19
+ switch (check) {
20
+ case "read-only":
21
+ return "deny";
22
+ case "tenant":
23
+ // A tenant finding without a recognized mode (no policy, or an unknown value) fails closed.
24
+ if (modes.tenant !== "warn")
25
+ return "deny";
26
+ if (point === "rebind" && !modes.acceptWarnings?.includes("tenant"))
27
+ return "deny";
28
+ return "warn";
29
+ case "sensitive":
30
+ // Like the tenant case, a mode it doesn't recognize fails closed.
31
+ return modes.sensitive === "warn" ? "warn" : modes.sensitive === "off" ? "allow" : "deny";
32
+ }
33
+ }
34
+ /**
35
+ * Throw the typed error for a `deny` verdict, with the verdict attached. When several
36
+ * checks deny, the precedence is read-only (`SqlValidationError`), then tenant
37
+ * (`TenantGuardrailError`), then sensitive (`SensitiveReferenceError`).
38
+ */
39
+ export function throwIfDenied(verdict, modes, point) {
40
+ if (verdict.outcome !== "deny")
41
+ return;
42
+ const denied = (check) => verdict.findings.filter((f) => f.check === check && outcomeOf(check, modes, point) === "deny");
43
+ let error;
44
+ const readOnly = denied("read-only");
45
+ const tenant = denied("tenant");
46
+ if (readOnly.length > 0) {
47
+ const first = readOnly[0];
48
+ error = new SqlValidationError(first.message, first.rule, first.hint);
49
+ }
50
+ else if (tenant.length > 0) {
51
+ const warnings = tenantWarnings(tenant);
52
+ const detail = warnings.map((w) => w.message).join("; ");
53
+ error = new TenantGuardrailError(modes.tenant === "warn"
54
+ ? "Tenant guardrail found issues (policy enforcement: warn), and bindPreparedQuery() refuses a " +
55
+ `tenant warning unless acceptWarnings includes "tenant": ${detail}`
56
+ : `Tenant guardrail validation failed (strict mode): ${detail}`, warnings);
57
+ }
58
+ else {
59
+ const result = sensitiveGuardrailResult(denied("sensitive"));
60
+ error = new SensitiveReferenceError(sensitiveStrictMessage(result), sensitiveRuleFor(result), result.references, result.unresolvedScope);
61
+ }
62
+ error.verdict = verdict;
63
+ throw error;
64
+ }
65
+ // ---------------------------------------------------------------------------
66
+ // Tenant findings ↔ the TenantGuardrailResult shape
67
+ // ---------------------------------------------------------------------------
68
+ /** Tenant findings for one form, from the rule's warnings. */
69
+ export function tenantFindings(warnings, form) {
70
+ return warnings.map((w) => ({ check: "tenant", form, rule: w.rule, message: w.message, tableId: w.tableId }));
71
+ }
72
+ /** The tenant findings as warnings, once each across forms (same rule, table and message). */
73
+ export function tenantWarnings(findings) {
74
+ const seen = new Set();
75
+ const out = [];
76
+ for (const f of findings) {
77
+ if (f.check !== "tenant")
78
+ continue;
79
+ const key = `${f.rule}\u0000${f.tableId}\u0000${f.message}`;
80
+ if (seen.has(key))
81
+ continue;
82
+ seen.add(key);
83
+ out.push({ rule: f.rule, tableId: f.tableId, message: f.message });
84
+ }
85
+ return out;
86
+ }
87
+ /** `result.tenantGuardrail`, derived from the verdict. */
88
+ export function tenantGuardrailResult(findings) {
89
+ const warnings = tenantWarnings(findings);
90
+ return { passed: warnings.length === 0, warnings };
91
+ }
92
+ /** Render a reference as `schema.table.column` (or `table.column` when the schema has no namespace). */
93
+ export function formatSensitiveReference(ref) {
94
+ const table = ref.schema ? `${ref.schema}.${ref.table}` : ref.table;
95
+ return `${table}.${ref.column}`;
96
+ }
97
+ /** Sensitive findings for one form: one per reference, plus one when the scope wasn't resolved. */
98
+ export function sensitiveFindings(scan, form) {
99
+ const findings = scan.references.map((reference) => {
100
+ const table = reference.matchKind === "table";
101
+ return {
102
+ check: "sensitive",
103
+ form,
104
+ rule: table ? "SENSITIVE_TABLE_REFERENCED" : "SENSITIVE_COLUMN_REFERENCED",
105
+ message: table
106
+ ? `SQL references sensitive table ${formatSensitiveReference(reference).replace(/\.\*$/, "")}`
107
+ : `SQL references sensitive column ${formatSensitiveReference(reference)}`,
108
+ reference,
109
+ };
110
+ });
111
+ if (scan.unresolvedScope) {
112
+ findings.push({
113
+ check: "sensitive",
114
+ form,
115
+ rule: "UNRESOLVED_TABLE_SCOPE",
116
+ message: scan.unresolvedScope.message,
117
+ unresolvedScope: scan.unresolvedScope,
118
+ });
119
+ }
120
+ return findings;
121
+ }
122
+ /** `result.sensitiveGuardrail`, derived from the verdict: references once each across forms. */
123
+ export function sensitiveGuardrailResult(findings) {
124
+ const references = [];
125
+ const seen = new Set();
126
+ let unresolvedScope;
127
+ let any = false;
128
+ for (const f of findings) {
129
+ if (f.check !== "sensitive")
130
+ continue;
131
+ any = true;
132
+ if (f.unresolvedScope)
133
+ unresolvedScope ??= f.unresolvedScope;
134
+ if (!f.reference)
135
+ continue;
136
+ const r = f.reference;
137
+ const key = `${r.schema ?? ""}\u0000${r.table}\u0000${r.column}\u0000${r.matchKind}`;
138
+ if (seen.has(key))
139
+ continue;
140
+ seen.add(key);
141
+ references.push(r);
142
+ }
143
+ return { passed: !any, references, ...(unresolvedScope ? { unresolvedScope } : {}) };
144
+ }
145
+ export function sensitiveRuleFor(scan) {
146
+ if (scan.references.some((r) => r.matchKind === "table"))
147
+ return "SENSITIVE_TABLE_REFERENCED";
148
+ if (scan.references.length > 0)
149
+ return "SENSITIVE_COLUMN_REFERENCED";
150
+ return "UNRESOLVED_TABLE_SCOPE";
151
+ }
152
+ export function sensitiveStrictMessage(scan) {
153
+ const parts = [];
154
+ if (scan.references.length > 0) {
155
+ parts.push(`SQL references sensitive identifiers: ${scan.references.map(formatSensitiveReference).join(", ")}`);
156
+ }
157
+ if (scan.unresolvedScope)
158
+ parts.push(scan.unresolvedScope.message);
159
+ return `Sensitive-identifier guardrail failed (strict mode): ${parts.join("; ")}`;
160
+ }
161
+ //# sourceMappingURL=guardrail-decide.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrail-decide.js","sourceRoot":"","sources":["../../src/sql/guardrail-decide.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ import { type GuardrailCheckId, type GuardrailFinding, type GuardrailForm, type GuardrailVerdict } from "../errors.js";
2
+ import type { AskDbLogger } from "../logging/askdb-logger.js";
3
+ import type { AnyNormalizedSchema } from "../schema/types.js";
4
+ import type { NormalizedTenantPolicy, TenantScope } from "../schema/v2/tenant-policy.js";
5
+ import type { DialectSpec } from "./dialect-spec.js";
6
+ import { type GuardrailModes } from "./guardrail-decide.js";
7
+ import { type SensitiveGuardrailMode } from "./sensitive-guardrail.js";
8
+ /**
9
+ * The checks as pure rules (ADR 0010, decision 1): each takes a candidate and returns
10
+ * findings. They know nothing about modes and never throw for a finding; `decide`
11
+ * (`guardrail-decide.ts`) maps the findings to a verdict. Internal to `@askdb/core`.
12
+ */
13
+ /** What every enforcement point checks: the untrusted forms, before AskDB renders them. */
14
+ export type Candidate = {
15
+ /** `sql`: the model's bound SQL. `template`: its `sql-unbound` block (`preparedQuery.namedSql`). */
16
+ forms: Partial<Record<GuardrailForm, string>>;
17
+ /** Undefined for a custom `AskDialect`. */
18
+ dialect: DialectSpec | undefined;
19
+ schema: AnyNormalizedSchema;
20
+ /** A v2 schema's tenant policy, with the enforcement point's (expanded) scope. */
21
+ tenant?: {
22
+ policy: NormalizedTenantPolicy;
23
+ scope: TenantScope;
24
+ };
25
+ };
26
+ /**
27
+ * ADR 0010, decision 3: which checks each enforcement point runs, and the modes `decide`
28
+ * reads there. `ask()` with a custom `AskDialect` has no `dialect`, so no read-only check;
29
+ * `generateSelectSql()` takes no sensitive mode, so no sensitive check; `acceptWarnings`
30
+ * means something at rebind only.
31
+ */
32
+ export declare function guardrailPlan(path: "ask" | "generate" | "rebind", input: {
33
+ dialect: DialectSpec | undefined;
34
+ schema: AnyNormalizedSchema;
35
+ tenantPolicy: NormalizedTenantPolicy | undefined;
36
+ sensitiveGuardrailMode?: SensitiveGuardrailMode | "off";
37
+ acceptWarnings?: ReadonlyArray<"tenant">;
38
+ }): {
39
+ checks: GuardrailCheckId[];
40
+ modes: GuardrailModes;
41
+ };
42
+ /** Run each listed check on every form in the candidate. */
43
+ export declare function evaluateGuardrails(candidate: Candidate, checks: readonly GuardrailCheckId[]): GuardrailFinding[];
44
+ /**
45
+ * Log a verdict the way `ask()` and `generateSelectSql()` always have: one tenant
46
+ * pass/fail event (with a tenant policy), and the sensitive references (when checked).
47
+ */
48
+ export declare function logGuardrailVerdict(logger: AskDbLogger | undefined, verdict: GuardrailVerdict, context: {
49
+ tenantPolicy: NormalizedTenantPolicy | undefined;
50
+ sensitiveChecked: boolean;
51
+ }): void;
52
+ //# sourceMappingURL=guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.d.ts","sourceRoot":"","sources":["../../src/sql/guardrails.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACtB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,KAAK,EAAE,sBAAsB,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EACL,KAAK,cAAc,EAMpB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAGL,KAAK,sBAAsB,EAC5B,MAAM,0BAA0B,CAAC;AAIlC;;;;GAIG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,SAAS,GAAG;IACtB,oGAAoG;IACpG,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IAC9C,2CAA2C;IAC3C,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,kFAAkF;IAClF,MAAM,CAAC,EAAE;QAAE,MAAM,EAAE,sBAAsB,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,CAAC;CACjE,CAAC;AAgCF;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,KAAK,GAAG,UAAU,GAAG,QAAQ,EACnC,KAAK,EAAE;IACL,OAAO,EAAE,WAAW,GAAG,SAAS,CAAC;IACjC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,YAAY,EAAE,sBAAsB,GAAG,SAAS,CAAC;IACjD,sBAAsB,CAAC,EAAE,sBAAsB,GAAG,KAAK,CAAC;IACxD,cAAc,CAAC,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC;CAC1C,GACA;IAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;IAAC,KAAK,EAAE,cAAc,CAAA;CAAE,CAcvD;AAED,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,MAAM,EAAE,SAAS,gBAAgB,EAAE,GAClC,gBAAgB,EAAE,CASpB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,OAAO,EAAE,gBAAgB,EACzB,OAAO,EAAE;IAAE,YAAY,EAAE,sBAAsB,GAAG,SAAS,CAAC;IAAC,gBAAgB,EAAE,OAAO,CAAA;CAAE,GACvF,IAAI,CA4BN"}
@@ -0,0 +1,100 @@
1
+ import { SqlValidationError, } from "../errors.js";
2
+ import { AskDbLogEvent } from "../logging/log-events.js";
3
+ import { formatSensitiveReference, sensitiveFindings, sensitiveGuardrailResult, tenantFindings, tenantWarnings, } from "./guardrail-decide.js";
4
+ import { scanSensitiveReferences, schemaHasSensitiveIdentifiers, } from "./sensitive-guardrail.js";
5
+ import { tenantRuleWarnings } from "./tenant-guardrail.js";
6
+ import { validateSelectSql } from "./validate.js";
7
+ const CHECKS = {
8
+ // Needs a DialectSpec: a custom AskDialect may target non-SELECT SQL (decision 3).
9
+ "read-only": ({ dialect }, form, sql) => {
10
+ if (!dialect)
11
+ return [];
12
+ try {
13
+ validateSelectSql(dialect, sql);
14
+ return [];
15
+ }
16
+ catch (error) {
17
+ if (!(error instanceof SqlValidationError))
18
+ throw error;
19
+ return [
20
+ {
21
+ check: "read-only",
22
+ form,
23
+ rule: error.rule,
24
+ message: error.message,
25
+ ...(error.hint !== undefined ? { hint: error.hint } : {}),
26
+ },
27
+ ];
28
+ }
29
+ },
30
+ tenant: ({ tenant, dialect }, form, sql) => tenant ? tenantFindings(tenantRuleWarnings(sql, tenant.policy, tenant.scope, dialect), form) : [],
31
+ sensitive: ({ schema, dialect }, form, sql) => sensitiveFindings(scanSensitiveReferences(sql, schema, dialect), form),
32
+ };
33
+ const FORMS = ["sql", "template"];
34
+ /**
35
+ * ADR 0010, decision 3: which checks each enforcement point runs, and the modes `decide`
36
+ * reads there. `ask()` with a custom `AskDialect` has no `dialect`, so no read-only check;
37
+ * `generateSelectSql()` takes no sensitive mode, so no sensitive check; `acceptWarnings`
38
+ * means something at rebind only.
39
+ */
40
+ export function guardrailPlan(path, input) {
41
+ const sensitive = path === "generate" ? "off" : (input.sensitiveGuardrailMode ?? "warn");
42
+ const checks = [];
43
+ if (input.dialect)
44
+ checks.push("read-only");
45
+ if (input.tenantPolicy)
46
+ checks.push("tenant");
47
+ if (sensitive !== "off" && schemaHasSensitiveIdentifiers(input.schema))
48
+ checks.push("sensitive");
49
+ return {
50
+ checks,
51
+ modes: {
52
+ ...(input.tenantPolicy ? { tenant: input.tenantPolicy.enforcement } : {}),
53
+ sensitive,
54
+ ...(path === "rebind" && input.acceptWarnings ? { acceptWarnings: input.acceptWarnings } : {}),
55
+ },
56
+ };
57
+ }
58
+ /** Run each listed check on every form in the candidate. */
59
+ export function evaluateGuardrails(candidate, checks) {
60
+ const findings = [];
61
+ for (const check of checks) {
62
+ for (const form of FORMS) {
63
+ const sql = candidate.forms[form];
64
+ if (sql !== undefined)
65
+ findings.push(...CHECKS[check](candidate, form, sql));
66
+ }
67
+ }
68
+ return findings;
69
+ }
70
+ /**
71
+ * Log a verdict the way `ask()` and `generateSelectSql()` always have: one tenant
72
+ * pass/fail event (with a tenant policy), and the sensitive references (when checked).
73
+ */
74
+ export function logGuardrailVerdict(logger, verdict, context) {
75
+ if (context.tenantPolicy) {
76
+ const warnings = tenantWarnings(verdict.findings);
77
+ if (warnings.length === 0) {
78
+ logger?.info({ event: AskDbLogEvent.TenantGuardrailPassed }, "tenant guardrail validation passed");
79
+ }
80
+ else {
81
+ logger?.info({
82
+ event: AskDbLogEvent.TenantGuardrailFailed,
83
+ warningCount: warnings.length,
84
+ enforcement: context.tenantPolicy.enforcement,
85
+ }, "tenant guardrail validation found issues");
86
+ }
87
+ }
88
+ if (!context.sensitiveChecked)
89
+ return;
90
+ const references = sensitiveGuardrailResult(verdict.findings).references;
91
+ if (references.length === 0)
92
+ return;
93
+ const sensitiveColumns = references.map(formatSensitiveReference);
94
+ logger?.info({
95
+ event: AskDbLogEvent.PipelineSensitiveSqlWarning,
96
+ sensitiveColumnCount: sensitiveColumns.length,
97
+ sensitiveColumns,
98
+ }, "generated SQL references sensitive identifiers");
99
+ }
100
+ //# sourceMappingURL=guardrails.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guardrails.js","sourceRoot":"","sources":["../../src/sql/guardrails.ts"],"names":[],"mappings":"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"}
@@ -1,9 +1,11 @@
1
1
  export { extractSqlFromModelText, extractUnboundSqlFromModelText } from "./extract-sql.js";
2
- export { bindPreparedQuery, escapeSqlLiteral, type QueryParameterType, type QueryParameterValue, type QueryParamSlot, type QueryParameterBinding, type PreparedQuery, type BoundQuery, } from "./bind.js";
2
+ export { escapeSqlLiteral, type QueryParameterType, type QueryParameterValue, type QueryParamSlot, type QueryParameterBinding, type PreparedQuery, } from "./bind.js";
3
+ export { bindPreparedQuery, type BindGuardrails, type BoundQuery } from "./rebind.js";
3
4
  export { parseParameterManifest, type ParameterManifest, type ManifestParameter, } from "./parameter-manifest.js";
4
5
  export { generateSelectSql, type GenerateSelectSqlResult, type GenerateSqlDeps, } from "./generate.js";
5
6
  export { validateSelectSql, buildSelectGuardrailExplanation, type SelectGuardrailExplain, } from "./validate.js";
6
7
  export { buildNlToSqlUserPrompt, buildNlToSqlSystemPrompt, } from "./prompt.js";
8
+ export { promptIdentifierQuoter } from "./prompt-identifiers.js";
7
9
  export { assertNlToSqlInputs, nlToSqlAmbiguityNotes, } from "./schema-question-precheck.js";
8
- export { type DialectId, type DialectSpec, type BuiltInDialectId, POSTGRES_DIALECT, COCKROACHDB_DIALECT, MYSQL_DIALECT, MARIADB_DIALECT, SQLITE_DIALECT, SQLSERVER_DIALECT, BUILT_IN_DIALECTS, SUPPORTED_DIALECT_IDS, isBuiltInDialectId, getDialectSpec, } from "./dialect-spec.js";
10
+ export { type DialectId, type DialectSpec, type BuiltInDialectId, POSTGRES_DIALECT, COCKROACHDB_DIALECT, MYSQL_DIALECT, MARIADB_DIALECT, SQLITE_DIALECT, SQLSERVER_DIALECT, SINGLE_NAMESPACE_LABEL, BUILT_IN_DIALECTS, SUPPORTED_DIALECT_IDS, isBuiltInDialectId, getDialectSpec, } from "./dialect-spec.js";
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sql/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/sql/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EACL,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,aAAa,GACnB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,sBAAsB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,GACvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,EACjB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,EAC/B,KAAK,sBAAsB,GAC5B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
package/dist/sql/index.js CHANGED
@@ -1,9 +1,11 @@
1
1
  export { extractSqlFromModelText, extractUnboundSqlFromModelText } from "./extract-sql.js";
2
- export { bindPreparedQuery, escapeSqlLiteral, } from "./bind.js";
2
+ export { escapeSqlLiteral, } from "./bind.js";
3
+ export { bindPreparedQuery } from "./rebind.js";
3
4
  export { parseParameterManifest, } from "./parameter-manifest.js";
4
5
  export { generateSelectSql, } from "./generate.js";
5
6
  export { validateSelectSql, buildSelectGuardrailExplanation, } from "./validate.js";
6
7
  export { buildNlToSqlUserPrompt, buildNlToSqlSystemPrompt, } from "./prompt.js";
8
+ export { promptIdentifierQuoter } from "./prompt-identifiers.js";
7
9
  export { assertNlToSqlInputs, nlToSqlAmbiguityNotes, } from "./schema-question-precheck.js";
8
- export { POSTGRES_DIALECT, COCKROACHDB_DIALECT, MYSQL_DIALECT, MARIADB_DIALECT, SQLITE_DIALECT, SQLSERVER_DIALECT, BUILT_IN_DIALECTS, SUPPORTED_DIALECT_IDS, isBuiltInDialectId, getDialectSpec, } from "./dialect-spec.js";
10
+ export { POSTGRES_DIALECT, COCKROACHDB_DIALECT, MYSQL_DIALECT, MARIADB_DIALECT, SQLITE_DIALECT, SQLSERVER_DIALECT, SINGLE_NAMESPACE_LABEL, BUILT_IN_DIALECTS, SUPPORTED_DIALECT_IDS, isBuiltInDialectId, getDialectSpec, } from "./dialect-spec.js";
9
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sql/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EACL,iBAAiB,EACjB,gBAAgB,GAOjB,MAAM,WAAW,CAAC;AACnB,OAAO,EACL,sBAAsB,GAGvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,GAEhC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAIL,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sql/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,8BAA8B,EAAE,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EACL,gBAAgB,GAMjB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,iBAAiB,EAAwC,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,sBAAsB,GAGvB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,iBAAiB,GAGlB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,iBAAiB,EACjB,+BAA+B,GAEhC,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,EACL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAIL,gBAAgB,EAChB,mBAAmB,EACnB,aAAa,EACb,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,sBAAsB,EACtB,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,GACf,MAAM,mBAAmB,CAAC"}
@@ -19,6 +19,11 @@
19
19
  * | `#` line comment | — | yes | — | — |
20
20
  * | `--` line comment | yes | only before whitespace | yes | yes |
21
21
  * | nested `/* … *\/` | yes | no (`/*! … *\/` runs) | yes | no |
22
+ * | `0x`, `1E`, `$`/`€` money | — | — | literal | — |
23
+ * | U+00A0 etc. end a name | — | — | yes | — |
24
+ * | `@` continues a name | — | — | yes | — |
25
+ *
26
+ * The last three rows are `tsqlLiterals`; U+FEFF does not end a SQL Server name.
22
27
  *
23
28
  * The lexer never throws: a string, quoted identifier, or comment that runs off the end
24
29
  * of the input is returned with `unterminated: true` so callers can fail closed.
@@ -49,6 +54,14 @@ export type SqlLexerProfile = {
49
54
  atVariables: boolean;
50
55
  /** SQL Server `#temp` / `##temp` table names. */
51
56
  hashIdentifiers: boolean;
57
+ /**
58
+ * SQL Server's token ends: `0x` with no hex digits, an exponent with no digits (`1E`,
59
+ * `1E+`), and money literals that start with a currency symbol (`$`, `$1.5`, `€`) are
60
+ * complete literals; U+200B and U+0085 are whitespace; and a name or variable continues
61
+ * through `@` and ends at any whitespace character except U+FEFF. A keyword written right after one of these
62
+ * is its own token, as it is to SQL Server.
63
+ */
64
+ tsqlLiterals: boolean;
52
65
  };
53
66
  export declare const POSTGRES_LEXER: SqlLexerProfile;
54
67
  export declare const MYSQL_LEXER: SqlLexerProfile;
@@ -1 +1 @@
1
- {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/sql/lexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,iBAAiB,CAAC;IACtB,sEAAsE;IACtE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,4FAA4F;IAC5F,WAAW,EAAE,YAAY,GAAG,QAAQ,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,yFAAyF;IACzF,kBAAkB,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,wCAAwC;IACxC,YAAY,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,aAAa,EAAE,OAAO,CAAC;IACvB,+BAA+B;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,6FAA6F;IAC7F,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gGAAgG;IAChG,kBAAkB,EAAE,OAAO,CAAC;IAC5B,+FAA+F;IAC/F,WAAW,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAc5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAczB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAc7B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAc1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,eAc3B,CAAC;AAEF,qGAAqG;AACrG,eAAO,MAAM,qBAAqB,EAAE,SAAS,eAAe,EAK3D,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,kBAAkB,CAAC,GACpD,eAAe,GAAG,SAAS,CAmB7B;AAED,MAAM,MAAM,YAAY;AACtB,sCAAsC;AACpC,MAAM,GACN,mBAAmB,GACnB,QAAQ,GACR,QAAQ;AACV,wCAAwC;GACtC,WAAW;AACb,+EAA+E;GAC7E,OAAO,GACP,SAAS,CAAC;AAEd,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB,CAAC;AAQF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAItF;AAED,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,QAAQ,EAAE,CAsQxE;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAEzE"}
1
+ {"version":3,"file":"lexer.d.ts","sourceRoot":"","sources":["../../src/sql/lexer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,OAAO,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE1F,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,iBAAiB,CAAC;IACtB,sEAAsE;IACtE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,4FAA4F;IAC5F,WAAW,EAAE,YAAY,GAAG,QAAQ,CAAC;IACrC,mBAAmB,EAAE,OAAO,CAAC;IAC7B,yFAAyF;IACzF,kBAAkB,EAAE,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;IACjD,wCAAwC;IACxC,YAAY,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,aAAa,EAAE,OAAO,CAAC;IACvB,+BAA+B;IAC/B,YAAY,EAAE,OAAO,CAAC;IACtB,6FAA6F;IAC7F,qBAAqB,EAAE,OAAO,CAAC;IAC/B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,gGAAgG;IAChG,kBAAkB,EAAE,OAAO,CAAC;IAC5B,+FAA+F;IAC/F,WAAW,EAAE,OAAO,CAAC;IACrB,iDAAiD;IACjD,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;;OAMG;IACH,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,eAe5B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,eAezB,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,eAe7B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,eAe1B,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,eAe3B,CAAC;AAEF,qGAAqG;AACrG,eAAO,MAAM,qBAAqB,EAAE,SAAS,eAAe,EAK3D,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,kBAAkB,CAAC,GACpD,eAAe,GAAG,SAAS,CAmB7B;AAED,MAAM,MAAM,YAAY;AACtB,sCAAsC;AACpC,MAAM,GACN,mBAAmB,GACnB,QAAQ,GACR,QAAQ;AACV,wCAAwC;GACtC,WAAW;AACb,+EAA+E;GAC7E,OAAO,GACP,SAAS,CAAC;AAEd,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,8FAA8F;IAC9F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,YAAY,CAAC,EAAE,IAAI,CAAC;CACrB,CAAC;AAgBF;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,GAAG,OAAO,CAItF;AAED,yFAAyF;AACzF,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,QAAQ,EAAE,CA4RxE;AAED,wDAAwD;AACxD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,QAAQ,EAAE,GAAG,OAAO,CAEzE"}
package/dist/sql/lexer.js CHANGED
@@ -12,6 +12,7 @@ export const POSTGRES_LEXER = {
12
12
  executableComments: false,
13
13
  atVariables: false,
14
14
  hashIdentifiers: false,
15
+ tsqlLiterals: false,
15
16
  };
16
17
  export const MYSQL_LEXER = {
17
18
  id: "mysql",
@@ -27,6 +28,7 @@ export const MYSQL_LEXER = {
27
28
  executableComments: true,
28
29
  atVariables: true,
29
30
  hashIdentifiers: false,
31
+ tsqlLiterals: false,
30
32
  };
31
33
  export const SQLSERVER_LEXER = {
32
34
  id: "sqlserver",
@@ -42,6 +44,7 @@ export const SQLSERVER_LEXER = {
42
44
  executableComments: false,
43
45
  atVariables: true,
44
46
  hashIdentifiers: true,
47
+ tsqlLiterals: true,
45
48
  };
46
49
  export const SQLITE_LEXER = {
47
50
  id: "sqlite",
@@ -57,6 +60,7 @@ export const SQLITE_LEXER = {
57
60
  executableComments: false,
58
61
  atVariables: true,
59
62
  hashIdentifiers: false,
63
+ tsqlLiterals: false,
60
64
  };
61
65
  /**
62
66
  * Dialect-agnostic profile for callers that have no dialect (the tenant placeholder
@@ -78,6 +82,7 @@ export const GENERIC_LEXER = {
78
82
  executableComments: false,
79
83
  atVariables: true,
80
84
  hashIdentifiers: false,
85
+ tsqlLiterals: false,
81
86
  };
82
87
  /** Every engine-specific profile. Used when the dialect is unknown and each reading must be safe. */
83
88
  export const ENGINE_LEXER_PROFILES = [
@@ -118,6 +123,14 @@ const WORD_START = /[A-Za-z_\u0080-
118
123
  const WORD_CHAR = /[A-Za-z0-9_$\u0080-￿]/;
119
124
  const DIGIT = /[0-9]/;
120
125
  const HEX = /[0-9A-Fa-f]/;
126
+ /** Currency symbols. SQL Server starts a money literal with one (its list is a subset). */
127
+ const CURRENCY = /\p{Sc}/u;
128
+ /**
129
+ * Characters SQL Server reads as whitespace that `\s` does not match. With `\s` minus
130
+ * U+FEFF they are the only characters at or above U+0080 that end a T-SQL name or
131
+ * number (every BMP character probed on SQL Server 2022, #327); U+FEFF continues a name.
132
+ */
133
+ const TSQL_EXTRA_SPACE = /[\u200b\u0085]/;
121
134
  const DOLLAR_TAG = /^\$([A-Za-z_\u0080-￿][A-Za-z0-9_\u0080-￿]*)?\$/;
122
135
  /**
123
136
  * Whether the `--` at `i` starts a line comment. It always does, except on MySQL/MariaDB
@@ -137,6 +150,13 @@ export function lexSql(sql, profile) {
137
150
  let i = 0;
138
151
  /** Index in `tokens` of the open MySQL `/*! … *\/` executable-comment opener, or -1. */
139
152
  let executableOpener = -1;
153
+ const isSpace = (c) => /\s/.test(c) || (profile.tsqlLiterals && TSQL_EXTRA_SPACE.test(c));
154
+ /**
155
+ * `WORD_CHAR`, except that a SQL Server name also continues through `@` (`a@b` is one
156
+ * name, as `a$b` and `a#b` are) and ends at Unicode whitespace other than U+FEFF.
157
+ */
158
+ const isWordChar = (c) => (WORD_CHAR.test(c) || (profile.tsqlLiterals && c === "@")) &&
159
+ !(profile.tsqlLiterals && c !== "\ufeff" && isSpace(c));
140
160
  const push = (kind, start, end, extra) => {
141
161
  const text = sql.slice(start, end);
142
162
  const value = extra?.value ?? text;
@@ -187,7 +207,7 @@ export function lexSql(sql, profile) {
187
207
  while (i < n) {
188
208
  const ch = sql[i];
189
209
  const next = sql[i + 1];
190
- if (/\s/.test(ch)) {
210
+ if (isSpace(ch)) {
191
211
  i++;
192
212
  continue;
193
213
  }
@@ -285,6 +305,21 @@ export function lexSql(sql, profile) {
285
305
  i = j;
286
306
  continue;
287
307
  }
308
+ // SQL Server money: a currency symbol, then optional digits (`$`, `$1.5`, `€2`). `$`
309
+ // before a letter is a pseudocolumn (`$action`), lexed below as a parameter.
310
+ if (profile.tsqlLiterals && CURRENCY.test(ch) && !(ch === "$" && next !== undefined && WORD_START.test(next))) {
311
+ let j = i + 1;
312
+ while (j < n && DIGIT.test(sql[j]))
313
+ j++;
314
+ if (sql[j] === ".") {
315
+ j++;
316
+ while (j < n && DIGIT.test(sql[j]))
317
+ j++;
318
+ }
319
+ push("number", i, j);
320
+ i = j;
321
+ continue;
322
+ }
288
323
  if (ch === "$") {
289
324
  if (profile.dollarQuotes) {
290
325
  const tag = DOLLAR_TAG.exec(sql.slice(i));
@@ -299,7 +334,7 @@ export function lexSql(sql, profile) {
299
334
  }
300
335
  if (next !== undefined && (DIGIT.test(next) || (!profile.dollarQuotes && WORD_START.test(next)))) {
301
336
  let j = i + 1;
302
- while (j < n && WORD_CHAR.test(sql[j]) && sql[j] !== "$")
337
+ while (j < n && isWordChar(sql[j]) && sql[j] !== "$")
303
338
  j++;
304
339
  push("parameter", i, j);
305
340
  i = j;
@@ -309,7 +344,7 @@ export function lexSql(sql, profile) {
309
344
  // ---- words (and prefixed string literals) ---------------------------
310
345
  if (WORD_START.test(ch) || (ch === "#" && profile.hashIdentifiers && next !== undefined && (next === "#" || WORD_START.test(next)))) {
311
346
  let j = i + 1;
312
- while (j < n && (WORD_CHAR.test(sql[j]) || (profile.hashIdentifiers && sql[j] === "#")))
347
+ while (j < n && (isWordChar(sql[j]) || (profile.hashIdentifiers && sql[j] === "#")))
313
348
  j++;
314
349
  const word = sql.slice(i, j);
315
350
  if (sql[j] === "'" && j - i === 1) {
@@ -330,7 +365,7 @@ export function lexSql(sql, profile) {
330
365
  // ---- numbers --------------------------------------------------------
331
366
  if (DIGIT.test(ch) || (ch === "." && next !== undefined && DIGIT.test(next))) {
332
367
  let j = i;
333
- if (ch === "0" && (next === "x" || next === "X") && HEX.test(sql[i + 2] ?? "")) {
368
+ if (ch === "0" && (next === "x" || next === "X") && (profile.tsqlLiterals || HEX.test(sql[i + 2] ?? ""))) {
334
369
  j = i + 2;
335
370
  while (j < n && HEX.test(sql[j]))
336
371
  j++;
@@ -343,7 +378,7 @@ export function lexSql(sql, profile) {
343
378
  while (j < n && DIGIT.test(sql[j]))
344
379
  j++;
345
380
  }
346
- if ((sql[j] === "e" || sql[j] === "E") && /^[eE][+-]?[0-9]/.test(sql.slice(j))) {
381
+ if ((sql[j] === "e" || sql[j] === "E") && (profile.tsqlLiterals || /^[eE][+-]?[0-9]/.test(sql.slice(j)))) {
347
382
  j += sql[j + 1] === "+" || sql[j + 1] === "-" ? 2 : 1;
348
383
  while (j < n && DIGIT.test(sql[j]))
349
384
  j++;
@@ -363,7 +398,7 @@ export function lexSql(sql, profile) {
363
398
  }
364
399
  if (ch === ":" && next !== undefined && WORD_START.test(next)) {
365
400
  let j = i + 1;
366
- while (j < n && WORD_CHAR.test(sql[j]))
401
+ while (j < n && isWordChar(sql[j]))
367
402
  j++;
368
403
  push("parameter", i, j);
369
404
  i = j;
@@ -374,7 +409,7 @@ export function lexSql(sql, profile) {
374
409
  if (sql[j] === "@")
375
410
  j++;
376
411
  if (j < n && WORD_START.test(sql[j])) {
377
- while (j < n && WORD_CHAR.test(sql[j]))
412
+ while (j < n && isWordChar(sql[j]))
378
413
  j++;
379
414
  push("parameter", i, j);
380
415
  i = j;