@uptimizr/db 0.8.2 → 1.0.1

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 (43) hide show
  1. package/bin/uptimizr-db-migrate.js +4 -0
  2. package/bin/uptimizr-db-new-project.js +4 -0
  3. package/dist/cli/createProject.js +0 -0
  4. package/dist/cli/migrate.js +0 -0
  5. package/dist/env.d.ts +57 -2
  6. package/dist/env.d.ts.map +1 -1
  7. package/dist/env.js +31 -2
  8. package/dist/env.js.map +1 -1
  9. package/dist/index.d.ts +6 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +5 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/query/aggregations.d.ts +4 -4
  14. package/dist/query/aggregations.d.ts.map +1 -1
  15. package/dist/query/aggregations.js +200 -143
  16. package/dist/query/aggregations.js.map +1 -1
  17. package/dist/query/clickhouseDialect.d.ts.map +1 -1
  18. package/dist/query/clickhouseDialect.js +7 -2
  19. package/dist/query/clickhouseDialect.js.map +1 -1
  20. package/dist/query/dialect.d.ts +55 -11
  21. package/dist/query/dialect.d.ts.map +1 -1
  22. package/dist/query/dialect.js +6 -4
  23. package/dist/query/dialect.js.map +1 -1
  24. package/dist/query/duckdbDialect.d.ts.map +1 -1
  25. package/dist/query/duckdbDialect.js +7 -2
  26. package/dist/query/duckdbDialect.js.map +1 -1
  27. package/dist/query/index.d.ts +3 -0
  28. package/dist/query/index.d.ts.map +1 -1
  29. package/dist/query/index.js +6 -0
  30. package/dist/query/index.js.map +1 -1
  31. package/dist/query/mssqlDialect.d.ts +99 -0
  32. package/dist/query/mssqlDialect.d.ts.map +1 -0
  33. package/dist/query/mssqlDialect.js +398 -0
  34. package/dist/query/mssqlDialect.js.map +1 -0
  35. package/dist/query/postgresDialect.d.ts +48 -0
  36. package/dist/query/postgresDialect.d.ts.map +1 -0
  37. package/dist/query/postgresDialect.js +167 -0
  38. package/dist/query/postgresDialect.js.map +1 -0
  39. package/dist/query/relational.d.ts +69 -0
  40. package/dist/query/relational.d.ts.map +1 -0
  41. package/dist/query/relational.js +94 -0
  42. package/dist/query/relational.js.map +1 -0
  43. package/package.json +5 -4
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Microsoft SQL Server (T-SQL) dialect for the query layer (ADR 0020, #85).
3
+ *
4
+ * SQL Server is the optional single-tenant *relational* store for self-hosters
5
+ * standardized on SQL Server / Azure SQL: a multi-writer server, so several
6
+ * collector instances can share one database. Like the Postgres dialect it is a
7
+ * *re-home + dialect emitter* — every aggregation stays authored once in
8
+ * `aggregations.ts` — but it is the heaviest relational port, because T-SQL
9
+ * lacks three things the shared SQL takes for granted:
10
+ *
11
+ * 1. **No array type.** Vector columns (`position`, `direction`, `hit_point`,
12
+ * `ray_origin`, `ray_direction`, `screen`, `rotation`, `scale`) are stored
13
+ * as JSON number arrays (`'[x,y,z]'`, `nvarchar`). Every element access goes
14
+ * through {@link mssqlVectorElement} (`JSON_VALUE(col, '$[i]')` +
15
+ * `TRY_CONVERT(float)`), including the shared SQL's inline `position[1]`
16
+ * indexing, which {@link toTsql} rewrites — this is the single place where
17
+ * the JSON-vector representation is known. `vectorNorm` and `arrayLength`
18
+ * are built over the same representation.
19
+ * 2. **No `ASOF JOIN`.** `asofJoin` renders the shared nearest-row emulation
20
+ * (`renderNearestRowJoin`, `relational.ts`) as `CROSS APPLY` / `OUTER APPLY
21
+ * (SELECT TOP 1 … ORDER BY ts DESC)`.
22
+ * 3. **No aggregate percentile.** T-SQL's `PERCENTILE_CONT` is a *window*
23
+ * function only, so it cannot appear in a `GROUP BY` select list next to
24
+ * `avg`/`count`. `quantile` therefore packs the group's values with
25
+ * `STRING_AGG` — a real aggregate — and a small T-SQL scalar function
26
+ * (`dbo.uptimizr_quantile`, created by the `@uptimizr/db-mssql` migrations)
27
+ * sorts them and computes the type-7 linear interpolation DuckDB's
28
+ * `quantile_cont` / Postgres' `percentile_cont` use. Exact, O(n log n) per
29
+ * group; documented as the store's performance trade-off.
30
+ *
31
+ * Two further T-SQL syntax gaps are closed by {@link toTsql} rather than by the
32
+ * `Dialect` members, because the shared SQL cannot express them portably:
33
+ * `LIMIT n` (→ `OFFSET 0 ROWS FETCH NEXT n ROWS ONLY`) and `GROUP BY <select
34
+ * alias>`, which T-SQL rejects (`Invalid column name`) and is replaced by the
35
+ * alias's defining expression. `toTsql` is applied once, at execution time, by
36
+ * `runMssqlQuery` in `@uptimizr/db-mssql`.
37
+ *
38
+ * Other notes:
39
+ * - `epochMs` / `timeBucketMs` return `float` (exact for epoch milliseconds,
40
+ * which are far below 2^53). `bigint` would come back as a *string* from the
41
+ * TDS driver and `avg(bigint)` truncates in T-SQL; `float` avoids both.
42
+ * - Integer `avg` truncates in T-SQL, so `avgIf` / `avgMerge` / `quantile`
43
+ * cast their input to `float`; the promoted numeric columns are `float`
44
+ * already.
45
+ * - String columns use the binary collation {@link MSSQL_BIN_COLLATION}, so
46
+ * equality, `GROUP BY` and ordering are case-sensitive and code-point ordered
47
+ * exactly like DuckDB / Postgres; JSON extractions carry the same collation.
48
+ * - Parameters are emitted as named `$name` tokens (cast in place for `f64`,
49
+ * `timestamp`, `date`) and rewritten to positional `@p1…@pn` by the shared
50
+ * `toPositionalParams`; timestamp params are bound as ISO-8601 `T`-separated
51
+ * naive-UTC strings (language/`DATEFORMAT`-independent) against
52
+ * `datetime2(3)` columns.
53
+ * - Requires SQL Server 2022 (16.x) / Azure SQL: `GREATEST` / `LEAST` (2022),
54
+ * `STRING_AGG` / `STRING_SPLIT` / `JSON_VALUE` / `DATEDIFF_BIG` (2016+).
55
+ */
56
+ import type { Dialect } from "./dialect.js";
57
+ import { type NearestRowJoinTokens } from "./relational.js";
58
+ /**
59
+ * Format an epoch-millisecond timestamp as an ISO-8601 naive-UTC
60
+ * `YYYY-MM-DDTHH:MM:SS.mmm` string for binding to a `datetime2(3)` column /
61
+ * parameter. The `T` separator makes the literal language- and
62
+ * `SET DATEFORMAT`-independent; the wall-clock value is the same one the DuckDB
63
+ * / ClickHouse / Postgres stores bind.
64
+ */
65
+ export declare function toMssqlTimestamp(epochMs: number): string;
66
+ /** Join keywords for the SQL Server flavour of the nearest-row ASOF emulation. */
67
+ export declare const MSSQL_NEAREST_ROW_JOIN: NearestRowJoinTokens;
68
+ /**
69
+ * Binary (case-sensitive, code-point ordered) collation applied to every string
70
+ * column and JSON extraction, so string semantics match DuckDB / Postgres
71
+ * instead of SQL Server's case-insensitive default.
72
+ */
73
+ export declare const MSSQL_BIN_COLLATION = "Latin1_General_100_BIN2";
74
+ /** Schema-qualified name of the quantile helper function (see module doc). */
75
+ export declare const MSSQL_QUANTILE_FUNCTION = "dbo.uptimizr_quantile";
76
+ /**
77
+ * The `index0`-th (0-based) element of a JSON number array as `float`, or NULL
78
+ * when the array is shorter / the value is not numeric. The one place that
79
+ * knows vectors are JSON on SQL Server (see module doc).
80
+ */
81
+ export declare function mssqlVectorElement(expr: string, index0: number): string;
82
+ export declare const mssqlDialect: Dialect;
83
+ /**
84
+ * Rewrite a query rendered with {@link mssqlDialect} into T-SQL. Three
85
+ * constructs of the shared SQL are not T-SQL and cannot be expressed through a
86
+ * `Dialect` member without touching every aggregation:
87
+ *
88
+ * - `col[n]` (1-based vector indexing) → {@link mssqlVectorElement}`(col, n-1)`.
89
+ * - `LIMIT n` → `OFFSET 0 ROWS FETCH NEXT n ROWS ONLY` (T-SQL requires an
90
+ * `ORDER BY`, which every `LIMIT` in the shared SQL has).
91
+ * - `GROUP BY <alias>` → the alias's select-list expression (T-SQL: "Invalid
92
+ * column name"). Both DuckDB and Postgres accept output-column aliases in
93
+ * `GROUP BY`; T-SQL only accepts input columns and expressions.
94
+ *
95
+ * Plus the spelling difference `atan2` → `ATN2`. Pure and idempotent; applied
96
+ * once per query by `runMssqlQuery`.
97
+ */
98
+ export declare function toTsql(sql: string): string;
99
+ //# sourceMappingURL=mssqlDialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mssqlDialect.d.ts","sourceRoot":"","sources":["../../src/query/mssqlDialect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AACvD,OAAO,EAAwB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAElF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAOxD;AAED,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,EAAE,oBAMpC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,4BAA4B,CAAC;AAE7D,8EAA8E;AAC9E,eAAO,MAAM,uBAAuB,0BAA0B,CAAC;AAK/D;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvE;AA0BD,eAAO,MAAM,YAAY,EAAE,OA4F1B,CAAC;AAgBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAO1C"}
@@ -0,0 +1,398 @@
1
+ /**
2
+ * Microsoft SQL Server (T-SQL) dialect for the query layer (ADR 0020, #85).
3
+ *
4
+ * SQL Server is the optional single-tenant *relational* store for self-hosters
5
+ * standardized on SQL Server / Azure SQL: a multi-writer server, so several
6
+ * collector instances can share one database. Like the Postgres dialect it is a
7
+ * *re-home + dialect emitter* — every aggregation stays authored once in
8
+ * `aggregations.ts` — but it is the heaviest relational port, because T-SQL
9
+ * lacks three things the shared SQL takes for granted:
10
+ *
11
+ * 1. **No array type.** Vector columns (`position`, `direction`, `hit_point`,
12
+ * `ray_origin`, `ray_direction`, `screen`, `rotation`, `scale`) are stored
13
+ * as JSON number arrays (`'[x,y,z]'`, `nvarchar`). Every element access goes
14
+ * through {@link mssqlVectorElement} (`JSON_VALUE(col, '$[i]')` +
15
+ * `TRY_CONVERT(float)`), including the shared SQL's inline `position[1]`
16
+ * indexing, which {@link toTsql} rewrites — this is the single place where
17
+ * the JSON-vector representation is known. `vectorNorm` and `arrayLength`
18
+ * are built over the same representation.
19
+ * 2. **No `ASOF JOIN`.** `asofJoin` renders the shared nearest-row emulation
20
+ * (`renderNearestRowJoin`, `relational.ts`) as `CROSS APPLY` / `OUTER APPLY
21
+ * (SELECT TOP 1 … ORDER BY ts DESC)`.
22
+ * 3. **No aggregate percentile.** T-SQL's `PERCENTILE_CONT` is a *window*
23
+ * function only, so it cannot appear in a `GROUP BY` select list next to
24
+ * `avg`/`count`. `quantile` therefore packs the group's values with
25
+ * `STRING_AGG` — a real aggregate — and a small T-SQL scalar function
26
+ * (`dbo.uptimizr_quantile`, created by the `@uptimizr/db-mssql` migrations)
27
+ * sorts them and computes the type-7 linear interpolation DuckDB's
28
+ * `quantile_cont` / Postgres' `percentile_cont` use. Exact, O(n log n) per
29
+ * group; documented as the store's performance trade-off.
30
+ *
31
+ * Two further T-SQL syntax gaps are closed by {@link toTsql} rather than by the
32
+ * `Dialect` members, because the shared SQL cannot express them portably:
33
+ * `LIMIT n` (→ `OFFSET 0 ROWS FETCH NEXT n ROWS ONLY`) and `GROUP BY <select
34
+ * alias>`, which T-SQL rejects (`Invalid column name`) and is replaced by the
35
+ * alias's defining expression. `toTsql` is applied once, at execution time, by
36
+ * `runMssqlQuery` in `@uptimizr/db-mssql`.
37
+ *
38
+ * Other notes:
39
+ * - `epochMs` / `timeBucketMs` return `float` (exact for epoch milliseconds,
40
+ * which are far below 2^53). `bigint` would come back as a *string* from the
41
+ * TDS driver and `avg(bigint)` truncates in T-SQL; `float` avoids both.
42
+ * - Integer `avg` truncates in T-SQL, so `avgIf` / `avgMerge` / `quantile`
43
+ * cast their input to `float`; the promoted numeric columns are `float`
44
+ * already.
45
+ * - String columns use the binary collation {@link MSSQL_BIN_COLLATION}, so
46
+ * equality, `GROUP BY` and ordering are case-sensitive and code-point ordered
47
+ * exactly like DuckDB / Postgres; JSON extractions carry the same collation.
48
+ * - Parameters are emitted as named `$name` tokens (cast in place for `f64`,
49
+ * `timestamp`, `date`) and rewritten to positional `@p1…@pn` by the shared
50
+ * `toPositionalParams`; timestamp params are bound as ISO-8601 `T`-separated
51
+ * naive-UTC strings (language/`DATEFORMAT`-independent) against
52
+ * `datetime2(3)` columns.
53
+ * - Requires SQL Server 2022 (16.x) / Azure SQL: `GREATEST` / `LEAST` (2022),
54
+ * `STRING_AGG` / `STRING_SPLIT` / `JSON_VALUE` / `DATEDIFF_BIG` (2016+).
55
+ */
56
+ import { renderNearestRowJoin } from "./relational.js";
57
+ /**
58
+ * Format an epoch-millisecond timestamp as an ISO-8601 naive-UTC
59
+ * `YYYY-MM-DDTHH:MM:SS.mmm` string for binding to a `datetime2(3)` column /
60
+ * parameter. The `T` separator makes the literal language- and
61
+ * `SET DATEFORMAT`-independent; the wall-clock value is the same one the DuckDB
62
+ * / ClickHouse / Postgres stores bind.
63
+ */
64
+ export function toMssqlTimestamp(epochMs) {
65
+ const d = new Date(epochMs);
66
+ const p = (n, w = 2) => String(n).padStart(w, "0");
67
+ return (`${d.getUTCFullYear()}-${p(d.getUTCMonth() + 1)}-${p(d.getUTCDate())}T` +
68
+ `${p(d.getUTCHours())}:${p(d.getUTCMinutes())}:${p(d.getUTCSeconds())}.${p(d.getUTCMilliseconds(), 3)}`);
69
+ }
70
+ /** Join keywords for the SQL Server flavour of the nearest-row ASOF emulation. */
71
+ export const MSSQL_NEAREST_ROW_JOIN = {
72
+ inner: "CROSS APPLY",
73
+ left: "OUTER APPLY",
74
+ onTrue: "",
75
+ selectPrefix: "SELECT TOP 1",
76
+ selectSuffix: "",
77
+ };
78
+ /**
79
+ * Binary (case-sensitive, code-point ordered) collation applied to every string
80
+ * column and JSON extraction, so string semantics match DuckDB / Postgres
81
+ * instead of SQL Server's case-insensitive default.
82
+ */
83
+ export const MSSQL_BIN_COLLATION = "Latin1_General_100_BIN2";
84
+ /** Schema-qualified name of the quantile helper function (see module doc). */
85
+ export const MSSQL_QUANTILE_FUNCTION = "dbo.uptimizr_quantile";
86
+ /** The Unix epoch as a `datetime2(3)` literal (ISO-8601, language-neutral). */
87
+ const MSSQL_EPOCH = "CAST('1970-01-01T00:00:00' AS datetime2(3))";
88
+ /**
89
+ * The `index0`-th (0-based) element of a JSON number array as `float`, or NULL
90
+ * when the array is shorter / the value is not numeric. The one place that
91
+ * knows vectors are JSON on SQL Server (see module doc).
92
+ */
93
+ export function mssqlVectorElement(expr, index0) {
94
+ return `TRY_CONVERT(float, JSON_VALUE(${expr}, '$[${index0}]'))`;
95
+ }
96
+ /** Render a trusted compile-time key path as a SQL Server JSON path. */
97
+ function jsonPath(path) {
98
+ // Numeric components index a JSON array (0-based); the others are quoted
99
+ // object keys so any key spelling is safe.
100
+ return `$${path.map((k) => (/^\d+$/.test(k) ? `[${k}]` : `."${k}"`)).join("")}`;
101
+ }
102
+ /** `JSON_VALUE(column, '$.path')` — the value at `path` as text (NULL when absent). */
103
+ function jsonAt(column, path) {
104
+ return `JSON_VALUE(${column}, '${jsonPath(path)}')`;
105
+ }
106
+ /**
107
+ * The group's non-NULL values of `expr` packed into one comma-separated
108
+ * `varchar(max)` of round-trippable floats (`CONVERT` style 3 = 17 significant
109
+ * digits), consumed (and sorted) by `dbo.uptimizr_quantile`. Deliberately not
110
+ * `WITHIN GROUP (ORDER BY …)`: T-SQL rejects two ordered aggregates with
111
+ * different orderings in one scope, and the shared SQL routinely computes
112
+ * quantiles of several expressions side by side.
113
+ */
114
+ function packedValues(expr) {
115
+ return `STRING_AGG(CONVERT(varchar(max), CONVERT(varchar(30), CAST((${expr}) AS float), 3)), ',')`;
116
+ }
117
+ export const mssqlDialect = {
118
+ name: "mssql",
119
+ placeholder(name, type) {
120
+ // The TDS driver infers `int`/`float`/`nvarchar` from the JS value; cast in
121
+ // place where the inferred type could differ from the logical one (an
122
+ // integral `f64` would bind as `int`) or where a string must become a date.
123
+ switch (type) {
124
+ case "f64":
125
+ return `CAST($${name} AS float)`;
126
+ case "timestamp":
127
+ return `CAST($${name} AS datetime2(3))`;
128
+ case "date":
129
+ return `CAST($${name} AS date)`;
130
+ default:
131
+ return `$${name}`;
132
+ }
133
+ },
134
+ timestampValue(epochMs) {
135
+ return toMssqlTimestamp(epochMs);
136
+ },
137
+ quantile(expr, q) {
138
+ return `${MSSQL_QUANTILE_FUNCTION}(${packedValues(expr)}, ${q})`;
139
+ },
140
+ vectorNorm(expr) {
141
+ // No arrays: sum the squares of the JSON elements. All callers guard with
142
+ // `arrayLength(expr) = 3`; up to four components are covered (quaternions)
143
+ // and absent components contribute 0.
144
+ const terms = [0, 1, 2, 3].map((i) => `power(coalesce(${mssqlVectorElement(expr, i)}, 0), 2)`);
145
+ return `sqrt(${terms.join(" + ")})`;
146
+ },
147
+ arrayLength(expr) {
148
+ // Vectors are flat JSON number arrays written by the store itself, so the
149
+ // element count is the comma count + 1 (0 for `[]`). Pure scalar text
150
+ // arithmetic — usable in WHERE, SELECT and GROUP BY alike (no subquery).
151
+ return `(CASE WHEN ${expr} IS NULL OR ${expr} = N'[]' THEN 0 ELSE LEN(${expr}) - LEN(REPLACE(${expr}, ',', '')) + 1 END)`;
152
+ },
153
+ avgIf(value, cond) {
154
+ return `avg(CASE WHEN ${cond} THEN CAST((${value}) AS float) END)`;
155
+ },
156
+ anyValue(expr) {
157
+ // `min` is a valid "any" (callers use it on values constant within the
158
+ // group) and ignores NULLs like DuckDB's `any_value`.
159
+ return `min(${expr})`;
160
+ },
161
+ timeBucketMs(tsExpr, intervalPlaceholder) {
162
+ // Float epoch-ms floored to the interval grid (same arithmetic as DuckDB /
163
+ // ClickHouse / Postgres); the interval param is an `int`, the division is
164
+ // float so it can never truncate.
165
+ const bucket = `(${intervalPlaceholder} * 1000)`;
166
+ return `(floor(${this.epochMs(tsExpr)} / ${bucket}) * ${bucket})`;
167
+ },
168
+ epochMs(tsExpr) {
169
+ // `datetime2(3)` holds wall-clock UTC; DATEDIFF_BIG from the epoch is exact
170
+ // milliseconds, returned as float (see module doc).
171
+ return `CAST(DATEDIFF_BIG(millisecond, ${MSSQL_EPOCH}, ${tsExpr}) AS float)`;
172
+ },
173
+ toDate(expr) {
174
+ return `CAST(${expr} AS date)`;
175
+ },
176
+ toText(expr) {
177
+ // Style 121 renders dates as `YYYY-MM-DD` and datetime2 as
178
+ // `YYYY-MM-DD HH:MM:SS.fff` (ISO, language-neutral); the shared SQL only
179
+ // applies it to date-typed rollup columns.
180
+ return `CONVERT(nvarchar(30), ${expr}, 121)`;
181
+ },
182
+ jsonText(column, ...path) {
183
+ return `${jsonAt(column, path)} COLLATE ${MSSQL_BIN_COLLATION}`;
184
+ },
185
+ jsonInt(column, ...path) {
186
+ // `TRY_CONVERT(bigint)` yields NULL for absent / non-integer text exactly
187
+ // like DuckDB's `TRY_CAST(... AS BIGINT)`; widened to float so it never
188
+ // reaches the driver as a bigint string nor truncates under `avg`.
189
+ return `CAST(TRY_CONVERT(bigint, ${jsonAt(column, path)}) AS float)`;
190
+ },
191
+ jsonFloat(column, ...path) {
192
+ return `TRY_CONVERT(float, ${jsonAt(column, path)})`;
193
+ },
194
+ // The daily rollups are query-time views (pre-grouped by day), so each read
195
+ // GROUP BY sees exactly one source row per group and the "merge" of a single
196
+ // precomputed value is a plain pass-through aggregate — as on DuckDB.
197
+ countMerge(stateExpr) {
198
+ return `sum(${stateExpr})`;
199
+ },
200
+ avgMerge(stateExpr) {
201
+ return `avg(CAST((${stateExpr}) AS float))`;
202
+ },
203
+ quantileMerge(stateExpr, q) {
204
+ return `${MSSQL_QUANTILE_FUNCTION}(${packedValues(stateExpr)}, ${q})`;
205
+ },
206
+ asofJoin(spec) {
207
+ return renderNearestRowJoin(spec, MSSQL_NEAREST_ROW_JOIN);
208
+ },
209
+ };
210
+ // ---------------------------------------------------------------------------
211
+ // T-SQL adaptation of the shared SQL (see module doc)
212
+ // ---------------------------------------------------------------------------
213
+ /**
214
+ * Inline 1-based array indexing in the shared SQL: `position[1]`,
215
+ * `m.direction[3]`, … Never matches the dialect's own JSON paths (`'$[0]'`,
216
+ * `'$."uv"[0]'`), because those have no identifier directly before the `[`.
217
+ */
218
+ const VECTOR_INDEX = /(?<![\w.$"])((?:[A-Za-z_]\w*\.)?[A-Za-z_]\w*)\[(\d+)\]/g;
219
+ /** `LIMIT <param|literal>` — always the last clause of a block in the shared SQL. */
220
+ const LIMIT_CLAUSE = /\bLIMIT\s+(\$[A-Za-z_]\w*|\d+)\b/g;
221
+ /**
222
+ * Rewrite a query rendered with {@link mssqlDialect} into T-SQL. Three
223
+ * constructs of the shared SQL are not T-SQL and cannot be expressed through a
224
+ * `Dialect` member without touching every aggregation:
225
+ *
226
+ * - `col[n]` (1-based vector indexing) → {@link mssqlVectorElement}`(col, n-1)`.
227
+ * - `LIMIT n` → `OFFSET 0 ROWS FETCH NEXT n ROWS ONLY` (T-SQL requires an
228
+ * `ORDER BY`, which every `LIMIT` in the shared SQL has).
229
+ * - `GROUP BY <alias>` → the alias's select-list expression (T-SQL: "Invalid
230
+ * column name"). Both DuckDB and Postgres accept output-column aliases in
231
+ * `GROUP BY`; T-SQL only accepts input columns and expressions.
232
+ *
233
+ * Plus the spelling difference `atan2` → `ATN2`. Pure and idempotent; applied
234
+ * once per query by `runMssqlQuery`.
235
+ */
236
+ export function toTsql(sql) {
237
+ return rewriteGroupByAliases(sql)
238
+ .replace(VECTOR_INDEX, (_m, expr, index) => mssqlVectorElement(expr, Number(index) - 1))
239
+ .replace(/\batan2\s*\(/gi, "ATN2(")
240
+ .replace(LIMIT_CLAUSE, "OFFSET 0 ROWS FETCH NEXT $1 ROWS ONLY");
241
+ }
242
+ /** Keywords that end a `GROUP BY` list within the same block. */
243
+ const GROUP_TERMINATORS = /^(HAVING|ORDER\s+BY|LIMIT|OFFSET|UNION|EXCEPT|INTERSECT|WINDOW)\b/i;
244
+ /**
245
+ * One pass over the SQL text: paren depth, string literals skipped, and the
246
+ * clause keywords that matter for block detection. `WITHIN GROUP (` is not a
247
+ * `GROUP BY`; keywords inside `OVER (…)` / subqueries sit at a deeper depth.
248
+ */
249
+ function scanClauses(sql) {
250
+ const clauses = [];
251
+ let depth = 0;
252
+ for (let i = 0; i < sql.length;) {
253
+ const ch = sql[i];
254
+ if (ch === "'") {
255
+ // Skip a string literal ('' is an escaped quote).
256
+ let j = i + 1;
257
+ while (j < sql.length) {
258
+ if (sql[j] === "'") {
259
+ if (sql[j + 1] === "'")
260
+ j += 2;
261
+ else
262
+ break;
263
+ }
264
+ else
265
+ j++;
266
+ }
267
+ i = j + 1;
268
+ continue;
269
+ }
270
+ if (ch === "(") {
271
+ depth++;
272
+ i++;
273
+ continue;
274
+ }
275
+ if (ch === ")") {
276
+ clauses.push({ kind: "close", depth, start: i, end: i + 1 });
277
+ depth--;
278
+ i++;
279
+ continue;
280
+ }
281
+ if (/[A-Za-z_]/.test(ch) && !/[\w.$]/.test(sql[i - 1] ?? " ")) {
282
+ const rest = sql.slice(i);
283
+ const word = /^[A-Za-z_]\w*/.exec(rest)[0];
284
+ const upper = word.toUpperCase();
285
+ let match;
286
+ if (upper === "SELECT")
287
+ match = { kind: "select", length: word.length };
288
+ else if (upper === "FROM")
289
+ match = { kind: "from", length: word.length };
290
+ else if (upper === "GROUP") {
291
+ const by = /^GROUP\s+BY\b/i.exec(rest);
292
+ if (by)
293
+ match = { kind: "groupBy", length: by[0].length };
294
+ }
295
+ else {
296
+ const term = GROUP_TERMINATORS.exec(rest);
297
+ if (term)
298
+ match = { kind: "terminator", length: term[0].length };
299
+ }
300
+ if (match)
301
+ clauses.push({ kind: match.kind, depth, start: i, end: i + match.length });
302
+ i += word.length;
303
+ continue;
304
+ }
305
+ i++;
306
+ }
307
+ return clauses;
308
+ }
309
+ /** Split on commas that sit outside parentheses and string literals. */
310
+ function splitTopLevel(text) {
311
+ const parts = [];
312
+ let depth = 0;
313
+ let inString = false;
314
+ let start = 0;
315
+ for (let i = 0; i < text.length; i++) {
316
+ const ch = text[i];
317
+ if (inString) {
318
+ if (ch === "'") {
319
+ if (text[i + 1] === "'")
320
+ i++;
321
+ else
322
+ inString = false;
323
+ }
324
+ continue;
325
+ }
326
+ if (ch === "'")
327
+ inString = true;
328
+ else if (ch === "(")
329
+ depth++;
330
+ else if (ch === ")")
331
+ depth--;
332
+ else if (ch === "," && depth === 0) {
333
+ parts.push(text.slice(start, i));
334
+ start = i + 1;
335
+ }
336
+ }
337
+ parts.push(text.slice(start));
338
+ return parts;
339
+ }
340
+ const ALIASED_ITEM = /^([\s\S]*\S)\s+AS\s+([A-Za-z_]\w*)\s*$/i;
341
+ const BARE_IDENTIFIER = /^\s*([A-Za-z_]\w*)\s*$/;
342
+ /**
343
+ * Replace output-column aliases in every `GROUP BY` list with the expression
344
+ * that defines them in the owning `SELECT` list (nearest preceding `SELECT` at
345
+ * the same paren depth; its list ends at the first `FROM` at that depth).
346
+ */
347
+ function rewriteGroupByAliases(sql) {
348
+ const clauses = scanClauses(sql);
349
+ let out = sql;
350
+ // Walk backwards so earlier offsets stay valid after each replacement.
351
+ for (let g = clauses.length - 1; g >= 0; g--) {
352
+ const group = clauses[g];
353
+ if (group.kind !== "groupBy")
354
+ continue;
355
+ let selectIdx = -1;
356
+ for (let s = g - 1; s >= 0; s--) {
357
+ const c = clauses[s];
358
+ if (c.kind === "select" && c.depth === group.depth) {
359
+ selectIdx = s;
360
+ break;
361
+ }
362
+ }
363
+ if (selectIdx < 0)
364
+ continue;
365
+ const from = clauses
366
+ .slice(selectIdx + 1, g)
367
+ .find((c) => c.kind === "from" && c.depth === group.depth);
368
+ if (!from)
369
+ continue;
370
+ const selectList = out
371
+ .slice(clauses[selectIdx].end, from.start)
372
+ .replace(/^\s*(DISTINCT|TOP\s+\d+)\b/i, "");
373
+ const aliases = new Map();
374
+ for (const item of splitTopLevel(selectList)) {
375
+ const m = ALIASED_ITEM.exec(item.trim());
376
+ if (m && m[1].trim() !== m[2])
377
+ aliases.set(m[2], m[1].trim());
378
+ }
379
+ if (aliases.size === 0)
380
+ continue;
381
+ const terminator = clauses
382
+ .slice(g + 1)
383
+ .find((c) => (c.kind === "terminator" && c.depth === group.depth) ||
384
+ (c.kind === "close" && c.depth === group.depth));
385
+ const listEnd = terminator ? terminator.start : out.length;
386
+ const list = out.slice(group.end, listEnd);
387
+ const rewritten = splitTopLevel(list)
388
+ .map((item) => {
389
+ const id = BARE_IDENTIFIER.exec(item);
390
+ const expr = id ? aliases.get(id[1]) : undefined;
391
+ return expr ? item.replace(id[1], expr) : item;
392
+ })
393
+ .join(",");
394
+ out = out.slice(0, group.end) + rewritten + out.slice(listEnd);
395
+ }
396
+ return out;
397
+ }
398
+ //# sourceMappingURL=mssqlDialect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mssqlDialect.js","sourceRoot":"","sources":["../../src/query/mssqlDialect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * PostgreSQL dialect for the query layer (ADR 0020, #84).
3
+ *
4
+ * Postgres is the optional single-tenant *relational* store: a multi-writer
5
+ * server many self-hosters already operate, for shops that "only want Postgres"
6
+ * and have outgrown DuckDB's single read-write process. This dialect renders the
7
+ * engine-specific fragments of each shared aggregation (`aggregations.ts`) to
8
+ * Postgres SQL, so the bulk of every query stays shared with DuckDB/ClickHouse.
9
+ *
10
+ * Like the other dialects it is *single-tenant* — no `org_id`, no tenant
11
+ * isolation — which keeps it relocatable across the open-core boundary.
12
+ *
13
+ * Binding model: parameters are emitted as named `$name::type` placeholders
14
+ * (every logical {@link ParamType} carries an explicit cast so Postgres never has
15
+ * to infer a parameter type from context) and rewritten to positional `$1…$n`
16
+ * by `toPositionalParams` in the `@uptimizr/db-postgres` runner. Timestamp params
17
+ * are bound as naive-UTC strings (see {@link toPostgresTimestamp}) against
18
+ * `timestamp` (without time zone) columns, so ordering, bucketing, and `::date`
19
+ * truncation are wall-clock-UTC exactly as on DuckDB and ClickHouse, independent
20
+ * of the session `TimeZone`.
21
+ *
22
+ * Row-store fit gaps (issue #84) and how they are closed here:
23
+ * - **No `ASOF JOIN`.** `asofJoin` renders the shared nearest-row emulation
24
+ * (`renderNearestRowJoin`, `relational.ts`) as `JOIN LATERAL (… ORDER BY ts
25
+ * DESC LIMIT 1) ON TRUE`.
26
+ * - **No MergeTree rollups.** The daily rollups are plain views that recompute
27
+ * at query time (see the `@uptimizr/db-postgres` migrations), so the `-Merge`
28
+ * combinators reduce to pass-through aggregates, as on DuckDB.
29
+ * - **Arrays are 1-indexed** — the same convention DuckDB and ClickHouse use, so
30
+ * `position[1]` in the shared SQL needs no translation; `arrayLength` maps to
31
+ * `cardinality` and `vectorNorm` unnests the array.
32
+ * - **JSON** lives in a `jsonb` column; extraction is `#>>` with a text[] path,
33
+ * and the numeric extractors regex-guard the cast (Postgres has no `TRY_CAST`)
34
+ * so an absent / non-numeric key yields NULL exactly like DuckDB's `TRY_CAST`.
35
+ */
36
+ import type { Dialect } from "./dialect.js";
37
+ import { type NearestRowJoinTokens } from "./relational.js";
38
+ /**
39
+ * Format an epoch-millisecond timestamp as a naive-UTC `YYYY-MM-DD HH:MM:SS.mmm`
40
+ * string for binding to a Postgres `timestamp` (without time zone) column/param.
41
+ * Mirrors the DuckDB / ClickHouse literal format so all stores order and bucket
42
+ * time identically.
43
+ */
44
+ export declare function toPostgresTimestamp(epochMs: number): string;
45
+ /** Join keywords for the Postgres flavour of the nearest-row ASOF emulation. */
46
+ export declare const POSTGRES_NEAREST_ROW_JOIN: NearestRowJoinTokens;
47
+ export declare const postgresDialect: Dialect;
48
+ //# sourceMappingURL=postgresDialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresDialect.d.ts","sourceRoot":"","sources":["../../src/query/postgresDialect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAa,MAAM,cAAc,CAAC;AACvD,OAAO,EAAwB,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAkBlF;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO3D;AAED,gFAAgF;AAChF,eAAO,MAAM,yBAAyB,EAAE,oBAMvC,CAAC;AAsBF,eAAO,MAAM,eAAe,EAAE,OAiF7B,CAAC"}