@hypequery/datasets 0.23.1 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (223) hide show
  1. package/README.md +14 -0
  2. package/dist/aggregations.d.ts +3 -0
  3. package/dist/aggregations.d.ts.map +1 -1
  4. package/dist/aggregations.js +6 -0
  5. package/dist/cache/definition-identity.js +2 -2
  6. package/dist/cache/query-signature.d.ts.map +1 -1
  7. package/dist/cache/query-signature.js +3 -0
  8. package/dist/catalog.d.ts +3 -0
  9. package/dist/catalog.d.ts.map +1 -1
  10. package/dist/catalog.js +3 -2
  11. package/dist/client-contract/query-schema.d.ts +19 -0
  12. package/dist/client-contract/query-schema.d.ts.map +1 -0
  13. package/dist/client-contract/query-schema.js +85 -0
  14. package/dist/constants.d.ts +5 -4
  15. package/dist/constants.d.ts.map +1 -1
  16. package/dist/constants.js +7 -6
  17. package/dist/contract.d.ts +2 -0
  18. package/dist/contract.d.ts.map +1 -1
  19. package/dist/contract.js +4 -3
  20. package/dist/dataset-sql-dialect.d.ts +117 -0
  21. package/dist/dataset-sql-dialect.d.ts.map +1 -1
  22. package/dist/dataset.d.ts +1 -1
  23. package/dist/dataset.d.ts.map +1 -1
  24. package/dist/dataset.js +2 -1
  25. package/dist/in-memory-backend.d.ts.map +1 -1
  26. package/dist/in-memory-backend.js +2 -0
  27. package/dist/index.d.ts +48 -5
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +34 -6
  30. package/dist/internal.d.ts +5 -1
  31. package/dist/internal.d.ts.map +1 -1
  32. package/dist/internal.js +5 -1
  33. package/dist/measure.d.ts +9 -1
  34. package/dist/measure.d.ts.map +1 -1
  35. package/dist/measure.js +9 -1
  36. package/dist/metric-query-engine.d.ts.map +1 -1
  37. package/dist/metric-query-engine.js +4 -3
  38. package/dist/protocol-adapter.d.ts.map +1 -1
  39. package/dist/protocol-adapter.js +5 -3
  40. package/dist/protocol-deployment-adapter.d.ts.map +1 -1
  41. package/dist/protocol-deployment-adapter.js +7 -1
  42. package/dist/protocol-schema-adapter.d.ts +7 -7
  43. package/dist/protocol-schema-adapter.d.ts.map +1 -1
  44. package/dist/protocol-schema-adapter.js +12 -245
  45. package/dist/query-builder-protocol.d.ts +32 -1
  46. package/dist/query-builder-protocol.d.ts.map +1 -1
  47. package/dist/query-planner.d.ts +14 -5
  48. package/dist/query-planner.d.ts.map +1 -1
  49. package/dist/query-planner.js +53 -34
  50. package/dist/relationship-check.d.ts.map +1 -1
  51. package/dist/relationship-check.js +5 -3
  52. package/dist/schemas.d.ts +17 -0
  53. package/dist/schemas.d.ts.map +1 -0
  54. package/dist/schemas.js +16 -0
  55. package/dist/semantic-planner.d.ts.map +1 -1
  56. package/dist/semantic-planner.js +16 -0
  57. package/dist/semantic-query-schema.d.ts +2 -1
  58. package/dist/semantic-query-schema.d.ts.map +1 -1
  59. package/dist/semantic-query-schema.js +12 -12
  60. package/dist/types.d.ts +22 -1
  61. package/dist/types.d.ts.map +1 -1
  62. package/dist/utils/aggregate-state-functions.d.ts +24 -0
  63. package/dist/utils/aggregate-state-functions.d.ts.map +1 -0
  64. package/dist/utils/aggregate-state-functions.js +60 -0
  65. package/dist/utils/approximate-measures.d.ts +3 -1
  66. package/dist/utils/approximate-measures.d.ts.map +1 -1
  67. package/dist/utils/approximate-measures.js +8 -2
  68. package/dist/utils/base-measure-definition.d.ts +16 -0
  69. package/dist/utils/base-measure-definition.d.ts.map +1 -1
  70. package/dist/utils/base-measure-definition.js +31 -0
  71. package/dist/utils/build-dataset-query-builder.d.ts.map +1 -1
  72. package/dist/utils/build-dataset-query-builder.js +16 -8
  73. package/dist/utils/by-name.d.ts +8 -0
  74. package/dist/utils/by-name.d.ts.map +1 -0
  75. package/dist/utils/by-name.js +44 -0
  76. package/dist/utils/calendar-shift-sql.d.ts +5 -4
  77. package/dist/utils/calendar-shift-sql.d.ts.map +1 -1
  78. package/dist/utils/calendar-shift-sql.js +8 -8
  79. package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +2 -2
  80. package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -1
  81. package/dist/utils/clickhouse-dataset-sql-dialect.js +140 -1
  82. package/dist/utils/compile-dataset-query.d.ts.map +1 -1
  83. package/dist/utils/compile-dataset-query.js +9 -6
  84. package/dist/utils/composite-time-measure-sql.d.ts +3 -5
  85. package/dist/utils/composite-time-measure-sql.d.ts.map +1 -1
  86. package/dist/utils/composite-time-measure-sql.js +15 -16
  87. package/dist/utils/count-aggregations.d.ts +4 -0
  88. package/dist/utils/count-aggregations.d.ts.map +1 -0
  89. package/dist/utils/count-aggregations.js +6 -0
  90. package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
  91. package/dist/utils/dataset-definition-validation.js +36 -26
  92. package/dist/utils/dataset-derived-query.d.ts +2 -4
  93. package/dist/utils/dataset-derived-query.d.ts.map +1 -1
  94. package/dist/utils/dataset-derived-query.js +11 -14
  95. package/dist/utils/dataset-having.d.ts +5 -7
  96. package/dist/utils/dataset-having.d.ts.map +1 -1
  97. package/dist/utils/dataset-having.js +9 -12
  98. package/dist/utils/dataset-normalization.d.ts.map +1 -1
  99. package/dist/utils/dataset-normalization.js +1 -0
  100. package/dist/utils/dataset-query-validation.d.ts.map +1 -1
  101. package/dist/utils/dataset-query-validation.js +7 -0
  102. package/dist/utils/dataset-source.d.ts +9 -0
  103. package/dist/utils/dataset-source.d.ts.map +1 -0
  104. package/dist/utils/dataset-source.js +16 -0
  105. package/dist/utils/dataset-sql-dialect.d.ts +13 -2
  106. package/dist/utils/dataset-sql-dialect.d.ts.map +1 -1
  107. package/dist/utils/dataset-sql-dialect.js +74 -1
  108. package/dist/utils/dataset-validation.d.ts.map +1 -1
  109. package/dist/utils/dataset-validation.js +7 -0
  110. package/dist/utils/deployable-formulas.d.ts +33 -0
  111. package/dist/utils/deployable-formulas.d.ts.map +1 -0
  112. package/dist/utils/deployable-formulas.js +102 -0
  113. package/dist/utils/derived-cte-validation.d.ts +5 -0
  114. package/dist/utils/derived-cte-validation.d.ts.map +1 -1
  115. package/dist/utils/derived-cte-validation.js +77 -8
  116. package/dist/utils/derived-measure-sql.d.ts +10 -2
  117. package/dist/utils/derived-measure-sql.d.ts.map +1 -1
  118. package/dist/utils/derived-measure-sql.js +23 -43
  119. package/dist/utils/filter-field-type.d.ts +14 -0
  120. package/dist/utils/filter-field-type.d.ts.map +1 -0
  121. package/dist/utils/filter-field-type.js +21 -0
  122. package/dist/utils/filtered-aggregation-sql.d.ts +2 -1
  123. package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
  124. package/dist/utils/filtered-aggregation-sql.js +18 -35
  125. package/dist/utils/groupable.d.ts +7 -0
  126. package/dist/utils/groupable.d.ts.map +1 -0
  127. package/dist/utils/groupable.js +8 -0
  128. package/dist/utils/metric-derived-query.d.ts +2 -4
  129. package/dist/utils/metric-derived-query.d.ts.map +1 -1
  130. package/dist/utils/metric-derived-query.js +54 -41
  131. package/dist/utils/metric-query-builder.d.ts.map +1 -1
  132. package/dist/utils/metric-query-builder.js +17 -9
  133. package/dist/utils/metric-query-validation.d.ts.map +1 -1
  134. package/dist/utils/metric-query-validation.js +7 -0
  135. package/dist/utils/portable-aggregations.d.ts +1 -0
  136. package/dist/utils/portable-aggregations.d.ts.map +1 -1
  137. package/dist/utils/portable-aggregations.js +5 -0
  138. package/dist/utils/protocol-schema-conversion.d.ts +12 -0
  139. package/dist/utils/protocol-schema-conversion.d.ts.map +1 -0
  140. package/dist/utils/protocol-schema-conversion.js +61 -0
  141. package/dist/utils/query-field-references.d.ts +10 -0
  142. package/dist/utils/query-field-references.d.ts.map +1 -0
  143. package/dist/utils/query-field-references.js +38 -0
  144. package/dist/utils/query-timezone.d.ts +41 -3
  145. package/dist/utils/query-timezone.d.ts.map +1 -1
  146. package/dist/utils/query-timezone.js +75 -8
  147. package/dist/utils/raw-sql-dialect-check.d.ts +12 -0
  148. package/dist/utils/raw-sql-dialect-check.d.ts.map +1 -0
  149. package/dist/utils/raw-sql-dialect-check.js +37 -0
  150. package/dist/utils/relationship-builder-plan.d.ts +25 -8
  151. package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
  152. package/dist/utils/relationship-builder-plan.js +23 -9
  153. package/dist/utils/relationship-measure-sql.d.ts +14 -4
  154. package/dist/utils/relationship-measure-sql.d.ts.map +1 -1
  155. package/dist/utils/relationship-measure-sql.js +33 -10
  156. package/dist/utils/relationship-measures.d.ts.map +1 -1
  157. package/dist/utils/relationship-measures.js +3 -0
  158. package/dist/utils/semantic-tool.d.ts +2 -2
  159. package/dist/utils/semantic-tool.d.ts.map +1 -1
  160. package/dist/utils/semantic-tool.js +1 -1
  161. package/dist/utils/shift-measure-sql.d.ts +5 -4
  162. package/dist/utils/shift-measure-sql.d.ts.map +1 -1
  163. package/dist/utils/shift-measure-sql.js +25 -26
  164. package/dist/utils/shift-wall-time-sql.d.ts +2 -1
  165. package/dist/utils/shift-wall-time-sql.d.ts.map +1 -1
  166. package/dist/utils/shift-wall-time-sql.js +7 -7
  167. package/dist/utils/statement-parameters.d.ts +58 -0
  168. package/dist/utils/statement-parameters.d.ts.map +1 -0
  169. package/dist/utils/statement-parameters.js +101 -0
  170. package/dist/utils/time-axis-calendar-guard.d.ts +2 -1
  171. package/dist/utils/time-axis-calendar-guard.d.ts.map +1 -1
  172. package/dist/utils/time-axis-calendar-guard.js +5 -7
  173. package/dist/utils/time-measure-axis-sql.d.ts +2 -1
  174. package/dist/utils/time-measure-axis-sql.d.ts.map +1 -1
  175. package/dist/utils/time-measure-axis-sql.js +19 -22
  176. package/dist/utils/time-measure-context-sql.d.ts +3 -2
  177. package/dist/utils/time-measure-context-sql.d.ts.map +1 -1
  178. package/dist/utils/time-measure-context-sql.js +11 -11
  179. package/dist/utils/time-measure-dataset-sql.d.ts +7 -5
  180. package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
  181. package/dist/utils/time-measure-dataset-sql.js +17 -12
  182. package/dist/utils/time-measure-leaf-sql.d.ts +2 -1
  183. package/dist/utils/time-measure-leaf-sql.d.ts.map +1 -1
  184. package/dist/utils/time-measure-leaf-sql.js +17 -18
  185. package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
  186. package/dist/utils/time-measure-result-sql.js +10 -11
  187. package/dist/utils/time-measure-source-sql.d.ts +2 -2
  188. package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
  189. package/dist/utils/time-measure-source-sql.js +25 -16
  190. package/dist/utils/time-measure-values-sql.d.ts +4 -3
  191. package/dist/utils/time-measure-values-sql.d.ts.map +1 -1
  192. package/dist/utils/time-measure-values-sql.js +26 -29
  193. package/dist/utils/timestamp-instants.d.ts +17 -0
  194. package/dist/utils/timestamp-instants.d.ts.map +1 -0
  195. package/dist/utils/timestamp-instants.js +34 -0
  196. package/dist/utils/timestamp-text.d.ts +12 -0
  197. package/dist/utils/timestamp-text.d.ts.map +1 -0
  198. package/dist/utils/timestamp-text.js +18 -0
  199. package/dist/utils/zod-json-schema.d.ts +8 -0
  200. package/dist/utils/zod-json-schema.d.ts.map +1 -0
  201. package/dist/utils/zod-json-schema.js +18 -0
  202. package/dist/utils/zod-schema-version.d.ts +19 -0
  203. package/dist/utils/zod-schema-version.d.ts.map +1 -0
  204. package/dist/utils/zod-schema-version.js +6 -0
  205. package/dist/utils/zod-v3-protocol-schema.d.ts +10 -0
  206. package/dist/utils/zod-v3-protocol-schema.d.ts.map +1 -0
  207. package/dist/utils/zod-v3-protocol-schema.js +164 -0
  208. package/dist/utils/zod-v4-protocol-schema.d.ts +12 -0
  209. package/dist/utils/zod-v4-protocol-schema.d.ts.map +1 -0
  210. package/dist/utils/zod-v4-protocol-schema.js +171 -0
  211. package/dist/validation.d.ts +7 -0
  212. package/dist/validation.d.ts.map +1 -1
  213. package/dist/validation.js +42 -8
  214. package/package.json +21 -12
  215. package/dist/api.type-test.d.ts +0 -2
  216. package/dist/api.type-test.d.ts.map +0 -1
  217. package/dist/api.type-test.js +0 -251
  218. package/dist/utils/time-arithmetic-sql.d.ts +0 -4
  219. package/dist/utils/time-arithmetic-sql.d.ts.map +0 -1
  220. package/dist/utils/time-arithmetic-sql.js +0 -9
  221. package/dist/utils/window-aggregation-sql.d.ts +0 -5
  222. package/dist/utils/window-aggregation-sql.d.ts.map +0 -1
  223. package/dist/utils/window-aggregation-sql.js +0 -17
@@ -1,50 +1,49 @@
1
- import { GRAIN_FUNCTIONS } from '../constants.js';
2
- import { addTimeSql as add, subtractTimeSql as subtract } from './time-arithmetic-sql.js';
3
1
  import { calendarBucketGuardSql } from './time-axis-calendar-guard.js';
4
2
  import { shiftRangePredicateSql } from './shift-measure-sql.js';
5
- import { isCountAggregation, windowAggregateSql } from './window-aggregation-sql.js';
3
+ import { isCountAggregation } from './count-aggregations.js';
6
4
  /** Keyed source membership retains distinct/average semantics and row multiplicity. */
7
- export function timeMeasureLeafSql(id, context, base, window, axis, dims) {
5
+ export function timeMeasureLeafSql(dialect, id, context, base, window, axis, dims) {
8
6
  const keys = dims.map(dim => dim.alias);
9
7
  const ranges = `_hq_ranges${id}`;
10
8
  const rows = `_hq_rows${id}`;
11
9
  const aggregate = `_hq_leaf${id}`;
12
10
  const value = `_hq_value${id}`;
13
11
  const group = `_hq_period${keys.length ? `, ${keys.join(', ')}` : ''}`;
14
- const expression = windowAggregateSql(base.definition, base.value, base.arg);
12
+ const expression = dialect.windowAggregate(base.definition, base.value, base.arg);
15
13
  if (window?.cumulative) {
16
14
  const population = `_hq_history${id}`;
17
15
  const partial = `_hq_partial${id}`;
18
16
  const operation = base.definition.aggregation === 'count' ? 'sum' : base.definition.aggregation;
19
- const partition = keys.length ? `tuple(${keys.join(', ')})` : 'tuple(0)';
17
+ const partition = dialect.packRow(keys.length ? keys : ['0']);
20
18
  const historyGroup = `_hq_time${keys.length ? `, ${keys.join(', ')}` : ''}`;
21
19
  return {
22
20
  ctes: [
23
21
  `${population} AS (SELECT * FROM _hq_source WHERE _hq_time < (SELECT max(_hq_eval_end) FROM _hq_context${context}))`,
24
- `${partial} AS (SELECT ${historyGroup}, ${partition} AS _hq_partition, toNullable(${expression}) AS _hq_partial FROM ${population} GROUP BY ${historyGroup})`,
25
- `${aggregate} AS (SELECT _hq_time, _hq_partition, ${operation}OrNull(_hq_partial) OVER (PARTITION BY _hq_partition ORDER BY _hq_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS ${value} FROM ${partial})`,
22
+ `${partial} AS (SELECT ${historyGroup}, ${partition} AS _hq_partition, ${dialect.toNullable(expression)} AS _hq_partial FROM ${population} GROUP BY ${historyGroup})`,
23
+ `${aggregate} AS (SELECT _hq_time, _hq_partition, ${dialect.reaggregate(operation, '_hq_partial')} OVER (PARTITION BY _hq_partition ORDER BY _hq_time ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS ${value} FROM ${partial})`,
26
24
  ],
27
25
  population, aggregate, context, cumulative: true, guard: '0',
28
26
  count: isCountAggregation(base.definition),
29
27
  };
30
28
  }
31
- const lower = window?.trailing ? subtract('_hq_eval_end', window.trailing.amount, window.trailing.unit)
32
- : window?.toDate ? `${GRAIN_FUNCTIONS[window.toDate]}(_hq_eval_period)` : '_hq_eval_lower';
29
+ const lower = window?.trailing ? dialect.subtractTime('_hq_eval_end', window.trailing.amount, window.trailing.unit)
30
+ : window?.toDate ? dialect.truncateTime('_hq_eval_period', window.toDate) : '_hq_eval_lower';
33
31
  const upper = window ? '_hq_eval_end' : '_hq_eval_upper';
34
32
  const lowerInclusive = window ? '1' : '_hq_lower_inclusive';
35
33
  const upperInclusive = window ? '0' : '_hq_upper_inclusive';
36
- const first = `${GRAIN_FUNCTIONS[axis.grain]}(_hq_scan_lower)`;
37
- const last = `${GRAIN_FUNCTIONS[axis.grain]}(_hq_scan_upper)`;
38
- const count = `greatest(0, dateDiff('${axis.grain}', ${first}, ${last}) + if(_hq_scan_upper > ${last} OR _hq_scan_upper_inclusive, 1, 0))`;
39
- const fanout = window ? `throwIf(${count} > 1000, 'A window row contributes to more than 1000 buckets.')` : '0';
40
- const guard = `${fanout} + ${calendarBucketGuardSql(first, `${count} + ${fanout}`, axis.grain)}`;
34
+ const first = dialect.truncateTime('_hq_scan_lower', axis.grain);
35
+ const last = dialect.truncateTime('_hq_scan_upper', axis.grain);
36
+ const partialUpper = dialect.ifElse(`_hq_scan_upper > ${last} OR _hq_scan_upper_inclusive`, '1', '0');
37
+ const count = dialect.greatest('0', `${dialect.timeDiff(axis.grain, first, last)} + ${partialUpper}`);
38
+ const fanout = window ? dialect.guard(`${count} > 1000`, 'A window row contributes to more than 1000 buckets.') : '0';
39
+ const guard = `${fanout} + ${calendarBucketGuardSql(dialect, first, `${count} + ${fanout}`, axis.grain)}`;
41
40
  const predicate = shiftRangePredicateSql('r._hq_time', { lower: 'b._hq_scan_lower', upper: 'b._hq_scan_upper', lowerInclusive: 'b._hq_scan_lower_inclusive', upperInclusive: 'b._hq_scan_upper_inclusive' });
42
41
  return {
43
42
  ctes: [
44
43
  `${ranges} AS (SELECT _hq_period, ${lower} AS _hq_scan_lower, ${upper} AS _hq_scan_upper, ${lowerInclusive} AS _hq_scan_lower_inclusive, ${upperInclusive} AS _hq_scan_upper_inclusive FROM _hq_context${context})`,
45
- `_hq_keys${id} AS (SELECT *, ${add(first, `arrayJoin(range(toUInt64(${count} + ${guard})))`, axis.grain)} AS _hq_source_period FROM ${ranges})`,
46
- `${rows} AS (SELECT r.*, b._hq_period FROM _hq_source AS r INNER JOIN _hq_keys${id} AS b ON ${GRAIN_FUNCTIONS[axis.grain]}(r._hq_time) = b._hq_source_period WHERE ${predicate})`,
47
- `${aggregate} AS (SELECT ${group}, toNullable(${expression}) AS ${value} FROM ${rows} GROUP BY ${group})`,
44
+ `_hq_keys${id} AS (SELECT *, ${dialect.addTime(first, dialect.seriesIndex(`${count} + ${guard}`), axis.grain)} AS _hq_source_period FROM ${ranges})`,
45
+ `${rows} AS (SELECT r.*, b._hq_period FROM _hq_source AS r INNER JOIN _hq_keys${id} AS b ON ${dialect.truncateTime('r._hq_time', axis.grain)} = b._hq_source_period WHERE ${predicate})`,
46
+ `${aggregate} AS (SELECT ${group}, ${dialect.toNullable(expression)} AS ${value} FROM ${rows} GROUP BY ${group})`,
48
47
  ],
49
48
  population: rows, aggregate, context, cumulative: false,
50
49
  guard: `(SELECT coalesce(max(${guard}), 0) FROM ${ranges})`, count: isCountAggregation(base.definition),
@@ -1 +1 @@
1
- {"version":3,"file":"time-measure-result-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-result-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAIxE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,IAAI,EAAE,SAAS,uBAAuB,EAAE,EACxC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,eAAe,EAAE,MAAM,EACvB,iBAAiB,UAAQ,GACxB,MAAM,CAuBR"}
1
+ {"version":3,"file":"time-measure-result-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-result-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAIxE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAE5E,sFAAsF;AACtF,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,IAAI,EAAE,SAAS,uBAAuB,EAAE,EACxC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,eAAe,EAAE,MAAM,EACvB,iBAAiB,UAAQ,GACxB,MAAM,CAwBR"}
@@ -1,31 +1,30 @@
1
1
  import { semanticOrderDirection } from './semantic-query-syntax.js';
2
- import { quoteSQLIdentifier } from '../sql-utils.js';
2
+ import { resolveDatasetSqlDialect } from './dataset-sql-dialect.js';
3
3
  import { getDerivedMeasure } from './dataset-measures.js';
4
4
  import { derivedProjection } from './derived-measure-sql.js';
5
5
  /** Evaluate formulas after aggregation, then apply output ordering and pagination. */
6
6
  export function buildTimeMeasureResultSql(ds, query, options, dims, ctes, validationGuard, formulasEvaluated = false) {
7
+ const dialect = resolveDatasetSqlDialect(options.builderFactory);
8
+ const quote = dialect.quoteIdentifier;
7
9
  const selected = query.measures ?? [];
8
10
  const projections = [
9
- quoteSQLIdentifier('period'),
10
- ...dims.map(dimension => quoteSQLIdentifier(dimension.name)),
11
+ quote('period'),
12
+ ...dims.map(dimension => quote(dimension.name)),
11
13
  ...selected.map(name => {
12
14
  const derived = getDerivedMeasure(ds.measures, name);
13
- return derived && !formulasEvaluated ? derivedProjection(name, derived) : quoteSQLIdentifier(name);
15
+ return derived && !formulasEvaluated ? derivedProjection(dialect, name, derived) : quote(name);
14
16
  }),
15
17
  ];
16
18
  // Keep toSQL independently guarded too. This branch produces no valid
17
19
  // result rows, and forces axis validation even when _hq_values is empty.
18
20
  const outputNames = ['period', ...dims.map(dimension => dimension.name), ...selected];
19
- const nullProjections = outputNames.map(name => `NULL AS ${quoteSQLIdentifier(name)}`).join(', ');
21
+ const nullProjections = outputNames.map(name => `NULL AS ${quote(name)}`).join(', ');
20
22
  const resultCte = `_hq_result AS (SELECT ${projections.join(', ')} FROM _hq_values`
21
23
  + ` UNION ALL SELECT ${nullProjections} FROM _hq_bounds WHERE ${validationGuard} = 1)`;
22
- let sql = `WITH ${[...ctes, resultCte].join(',\n')} SELECT ${outputNames.map(quoteSQLIdentifier).join(', ')} FROM _hq_result`;
24
+ let sql = `WITH ${[...ctes, resultCte].join(',\n')} SELECT ${outputNames.map(name => quote(name)).join(', ')} FROM _hq_result`;
23
25
  const order = query.orderBy?.length ? query.orderBy : [{ field: 'period', direction: 'asc' }];
24
- sql += ` ORDER BY ${order.map(item => `${quoteSQLIdentifier(item.field)} ${semanticOrderDirection(item.direction)}`).join(', ')}`;
26
+ sql += ` ORDER BY ${order.map(item => dialect.orderTerm(quote(item.field), semanticOrderDirection(item.direction))).join(', ')}`;
25
27
  const limit = options.executionLimit ?? query.limit;
26
- if (limit !== undefined)
27
- sql += ` LIMIT ${limit}`;
28
- if (query.offset !== undefined)
29
- sql += ` OFFSET ${query.offset}`;
28
+ sql += dialect.paginate(limit, query.offset);
30
29
  return sql;
31
30
  }
@@ -1,5 +1,6 @@
1
1
  import type { AnyDatasetInstance, DatasetQuery, MeasureDefinition, MetricFilter } from '../types.js';
2
2
  import type { DatasetQueryExecutionOptions } from '../dataset-query.js';
3
+ import type { StatementParameters } from './statement-parameters.js';
3
4
  export interface TimeMeasureSqlDimension {
4
5
  name: string;
5
6
  alias: string;
@@ -12,11 +13,10 @@ export interface TimeMeasureSqlBase {
12
13
  }
13
14
  export interface TimeMeasureSqlSource {
14
15
  ctes: string[];
15
- parameters: unknown[];
16
16
  dimensions: TimeMeasureSqlDimension[];
17
17
  bases: TimeMeasureSqlBase[];
18
18
  rowAlias: string;
19
19
  }
20
20
  /** Isolate authored expressions, joins, tenancy and filters from internal SQL aliases. */
21
- export declare function buildTimeMeasureSourceSql(ds: AnyDatasetInstance, query: DatasetQuery, baseNames: Iterable<string>, filters: readonly MetricFilter[], options: DatasetQueryExecutionOptions): TimeMeasureSqlSource;
21
+ export declare function buildTimeMeasureSourceSql(ds: AnyDatasetInstance, query: DatasetQuery, baseNames: Iterable<string>, filters: readonly MetricFilter[], options: DatasetQueryExecutionOptions, statement: StatementParameters): TimeMeasureSqlSource;
22
22
  //# sourceMappingURL=time-measure-source-sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"time-measure-source-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-source-sql.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAUxE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,iBAAiB,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,OAAO,EAAE,CAAC;IACtB,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0FAA0F;AAC1F,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,4BAA4B,GACpC,oBAAoB,CA6DtB"}
1
+ {"version":3,"file":"time-measure-source-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-source-sql.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrG,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AASxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAErE,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,iBAAiB,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,uBAAuB,EAAE,CAAC;IACtC,KAAK,EAAE,kBAAkB,EAAE,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,0FAA0F;AAC1F,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,SAAS,YAAY,EAAE,EAChC,OAAO,EAAE,4BAA4B,EACrC,SAAS,EAAE,mBAAmB,GAC7B,oBAAoB,CAqEtB"}
@@ -1,6 +1,7 @@
1
+ import { datasetSourceBuilder } from './dataset-source.js';
1
2
  import { resolveRelationshipMeasure } from './relationship-measures.js';
2
3
  import { relationshipMeasureExpressions } from './relationship-measure-sql.js';
3
- import { queryTimeSql } from './query-timezone.js';
4
+ import { queryTimeFilter, queryTimeSql } from './query-timezone.js';
4
5
  import { resolveDimensionExpression, resolveFilterField, resolveTenantFilterColumn } from '../query-planner.js';
5
6
  import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn } from './relationship-builder-plan.js';
6
7
  import { getRuntimeTenantPredicate } from './tenant-runtime.js';
@@ -10,12 +11,13 @@ import { measureToAggregationSpec } from './dataset-normalization.js';
10
11
  import { applyFilteredAggregationExpression } from './filtered-aggregation-sql.js';
11
12
  import { assertNoRawSqlUnderJoins } from './sql-under-joins.js';
12
13
  /** Isolate authored expressions, joins, tenancy and filters from internal SQL aliases. */
13
- export function buildTimeMeasureSourceSql(ds, query, baseNames, filters, options) {
14
+ export function buildTimeMeasureSourceSql(ds, query, baseNames, filters, options, statement) {
15
+ const { dialect } = statement;
14
16
  const bases = [];
15
- const joinCtx = buildRelationshipBuilderContext(ds, query, options.context);
17
+ const joinCtx = buildRelationshipBuilderContext(ds, query, options.context, dialect);
16
18
  // Keep source expressions in their own SELECT scope: internal aliases cannot
17
19
  // capture authored SQL identifiers, even if a physical column shares a name.
18
- let raw = applyRelationshipJoins(options.builderFactory.table(ds.source), joinCtx);
20
+ let raw = applyRelationshipJoins(datasetSourceBuilder(options.builderFactory, ds), joinCtx);
19
21
  const dims = (query.dimensions ?? []).map((name, i) => ({ name, alias: `_hq_d${i}` }));
20
22
  const sourceParts = [resolveDimensionExpression(ds, ds.timeKey, joinCtx)];
21
23
  for (const dimension of dims)
@@ -31,9 +33,9 @@ export function buildTimeMeasureSourceSql(ds, query, baseNames, filters, options
31
33
  const value = `_hq_v${bases.length}`;
32
34
  const arg = `_hq_a${bases.length}`;
33
35
  bases.push({ name, definition, value, arg });
34
- const expressions = related ? relationshipMeasureExpressions(ds, name, joinCtx) : undefined;
36
+ const expressions = related ? relationshipMeasureExpressions(ds, name, joinCtx, dialect) : undefined;
35
37
  const expression = expressions?.field ?? spec.sql ?? resolveDimensionExpression(ds, spec.field, joinCtx);
36
- sourceParts.push(expressions ? expression : applyFilteredAggregationExpression(ds, spec, expression, joinCtx));
38
+ sourceParts.push(expressions ? expression : applyFilteredAggregationExpression(ds, spec, expression, joinCtx, dialect));
37
39
  sourceParts.push(expressions?.arg ?? (spec.argField ? resolveDimensionExpression(ds, spec.argField, joinCtx) : 'NULL'));
38
40
  }
39
41
  // A positional tuple isolates internal names from the source table's names.
@@ -47,31 +49,38 @@ export function buildTimeMeasureSourceSql(ds, query, baseNames, filters, options
47
49
  let rowAlias = '_hq_row';
48
50
  while (sourceIdentifiers.some(text => text.includes(rowAlias)))
49
51
  rowAlias += '_';
50
- raw = raw.select([`tuple(${sourceParts.join(', ')}) AS ${rowAlias}`]);
52
+ raw = raw.select([`${dialect.packRow(sourceParts)} AS ${rowAlias}`]);
51
53
  const tenant = getRuntimeTenantPredicate(options.context);
52
54
  const tenantColumn = resolveTenantFilterColumn(ds, options.context);
53
55
  if (tenant && tenantColumn)
54
56
  raw = raw.where(qualifyBaseColumn(joinCtx, tenantColumn), tenant.operator, tenant.value);
57
+ // The time axis already consumed every time-key bound, so these are the
58
+ // remaining caller filters.
55
59
  for (const filter of filters) {
56
- raw = raw.where(resolveFilterField(ds, filter.field, joinCtx), filter.operator, filter.value);
60
+ const predicates = queryTimeFilter(dialect, ds, filter, resolveFilterField(ds, filter.field, joinCtx), query.timezone);
61
+ for (const predicate of predicates) {
62
+ raw = raw.where(predicate.expression, predicate.operator, predicate.value);
63
+ }
57
64
  }
58
65
  for (const filter of segmentFilters(ds, query.segments)) {
59
- raw = raw.where(resolveDimensionExpression(ds, filter.field, joinCtx), filter.operator, filter.value);
66
+ const predicates = queryTimeFilter(dialect, ds, filter, resolveDimensionExpression(ds, filter.field, joinCtx), query.timezone, { field: 'dimension' });
67
+ for (const predicate of predicates) {
68
+ raw = raw.where(predicate.expression, predicate.operator, predicate.value);
69
+ }
60
70
  }
61
- const { sql: rawSql, parameters } = raw.toSQLWithParams();
62
- const sourceColumns = [`${queryTimeSql(`assumeNotNull(tupleElement(${rowAlias}, 1))`, query.timezone)} AS _hq_time`];
63
- dims.forEach((dimension, i) => sourceColumns.push(`tupleElement(${rowAlias}, ${i + 2}) AS ${dimension.alias}`));
71
+ const rawSql = statement.embedBuilder(raw);
72
+ const sourceColumns = [`${queryTimeSql(dialect, dialect.assumeNotNull(dialect.rowElement(rowAlias, 1)), query.timezone)} AS _hq_time`];
73
+ dims.forEach((dimension, i) => sourceColumns.push(`${dialect.rowElement(rowAlias, i + 2)} AS ${dimension.alias}`));
64
74
  bases.forEach((base, i) => {
65
75
  const valuePosition = dims.length + 2 + i * 2;
66
- sourceColumns.push(`tupleElement(${rowAlias}, ${valuePosition}) AS ${base.value}`);
67
- sourceColumns.push(`tupleElement(${rowAlias}, ${valuePosition + 1}) AS ${base.arg}`);
76
+ sourceColumns.push(`${dialect.rowElement(rowAlias, valuePosition)} AS ${base.value}`);
77
+ sourceColumns.push(`${dialect.rowElement(rowAlias, valuePosition + 1)} AS ${base.arg}`);
68
78
  });
69
79
  return {
70
80
  ctes: [
71
81
  `_hq_raw AS (${rawSql})`,
72
- `_hq_source AS (SELECT ${sourceColumns.join(', ')} FROM _hq_raw WHERE isNotNull(tupleElement(${rowAlias}, 1)))`,
82
+ `_hq_source AS (SELECT ${sourceColumns.join(', ')} FROM _hq_raw WHERE ${dialect.isNotNull(dialect.rowElement(rowAlias, 1))})`,
73
83
  ],
74
- parameters,
75
84
  dimensions: dims,
76
85
  bases,
77
86
  rowAlias,
@@ -1,3 +1,4 @@
1
+ import type { DatasetSqlDialectRules } from '../dataset-sql-dialect.js';
1
2
  import type { TimeGrain, WindowMeasureDefinition } from '../types.js';
2
3
  import type { TimeMeasureAxis } from './time-measure-axis.js';
3
4
  import type { TimeMeasureSqlBase, TimeMeasureSqlDimension } from './time-measure-source-sql.js';
@@ -12,9 +13,9 @@ export interface TimeMeasureSqlInput {
12
13
  /** Shift populations already map exclusively to the requested output series. */
13
14
  restrictToAxis?: boolean;
14
15
  }
15
- export declare function windowScanStartSql(window: WindowMeasureDefinition, grain: TimeGrain): string;
16
+ export declare function windowScanStartSql(dialect: DatasetSqlDialectRules, window: WindowMeasureDefinition, grain: TimeGrain): string;
16
17
  /** Base measures respect partial endpoints; windows use entire contributing buckets. */
17
- export declare function windowMeasureRowsSql(window: WindowMeasureDefinition | undefined, axis: TimeMeasureAxis): string;
18
+ export declare function windowMeasureRowsSql(dialect: DatasetSqlDialectRules, window: WindowMeasureDefinition | undefined, axis: TimeMeasureAxis): string;
18
19
  /** Fill dimension combinations, aggregate populations, and join measure values onto the series. */
19
- export declare function buildTimeMeasureValuesSql(dims: readonly TimeMeasureSqlDimension[], inputs: readonly TimeMeasureSqlInput[], axis: TimeMeasureAxis): string[];
20
+ export declare function buildTimeMeasureValuesSql(dialect: DatasetSqlDialectRules, dims: readonly TimeMeasureSqlDimension[], inputs: readonly TimeMeasureSqlInput[], axis: TimeMeasureAxis): string[];
20
21
  //# sourceMappingURL=time-measure-values-sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"time-measure-values-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-values-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAGtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAKhG,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAwBD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAI5F;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,uBAAuB,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAG/G;AAED,mGAAmG;AACnG,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,SAAS,uBAAuB,EAAE,EACxC,MAAM,EAAE,SAAS,mBAAmB,EAAE,EACtC,IAAI,EAAE,eAAe,GACpB,MAAM,EAAE,CA0CV"}
1
+ {"version":3,"file":"time-measure-values-sql.d.ts","sourceRoot":"","sources":["../../src/utils/time-measure-values-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAIhG,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC;IAChB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,gFAAgF;IAChF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAwBD,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,uBAAuB,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CAI7H;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,uBAAuB,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAGhJ;AAED,mGAAmG;AACnG,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,sBAAsB,EAC/B,IAAI,EAAE,SAAS,uBAAuB,EAAE,EACxC,MAAM,EAAE,SAAS,mBAAmB,EAAE,EACtC,IAAI,EAAE,eAAe,GACpB,MAAM,EAAE,CA0CV"}
@@ -1,54 +1,51 @@
1
- import { GRAIN_FUNCTIONS } from '../constants.js';
2
- import { quoteSQLIdentifier } from '../sql-utils.js';
3
- import { addTimeSql as add, subtractTimeSql as subtract } from './time-arithmetic-sql.js';
4
- import { isCountAggregation, windowAggregateSql } from './window-aggregation-sql.js';
1
+ import { isCountAggregation } from './count-aggregations.js';
5
2
  import { calendarBucketGuardSql } from './time-axis-calendar-guard.js';
6
3
  /** One row is emitted into each bounded window it contributes to. */
7
- function contributionSql(window, grain) {
8
- const bucket = `${GRAIN_FUNCTIONS[grain]}(_hq_time)`;
4
+ function contributionSql(dialect, window, grain) {
5
+ const bucket = dialect.truncateTime('_hq_time', grain);
9
6
  if (!window)
10
7
  return bucket;
11
8
  if (window.cumulative)
12
- return `greatest(${bucket}, _hq_first)`;
9
+ return dialect.greatest(bucket, '_hq_first');
13
10
  let count;
14
11
  if (window.trailing) {
15
- const end = add(bucket, window.trailing.amount, window.trailing.unit);
16
- count = `dateDiff('${grain}', ${bucket}, ${end})`;
12
+ const end = dialect.addTime(bucket, window.trailing.amount, window.trailing.unit);
13
+ count = dialect.timeDiff(grain, bucket, end);
17
14
  }
18
15
  else if (window.toDate) {
19
- const calendarStart = `${GRAIN_FUNCTIONS[window.toDate]}(_hq_time)`;
20
- const end = add(calendarStart, 1, window.toDate);
21
- count = `dateDiff('${grain}', ${bucket}, ${end})`;
16
+ const calendarStart = dialect.truncateTime('_hq_time', window.toDate);
17
+ const end = dialect.addTime(calendarStart, 1, window.toDate);
18
+ count = dialect.timeDiff(grain, bucket, end);
22
19
  }
23
20
  else {
24
21
  throw new Error('Window measure has no supported mode.');
25
22
  }
26
- const fanoutGuard = `throwIf(${count} > 1000, 'A window row contributes to more than 1000 buckets.')`;
27
- const calendarGuard = calendarBucketGuardSql(bucket, `${count} + ${fanoutGuard}`, grain);
28
- const bucketIndices = `arrayJoin(range(toUInt64(${count} + ${fanoutGuard} + ${calendarGuard})))`;
29
- return add(bucket, bucketIndices, grain);
23
+ const fanoutGuard = dialect.guard(`${count} > 1000`, 'A window row contributes to more than 1000 buckets.');
24
+ const calendarGuard = calendarBucketGuardSql(dialect, bucket, `${count} + ${fanoutGuard}`, grain);
25
+ const bucketIndices = dialect.seriesIndex(`${count} + ${fanoutGuard} + ${calendarGuard}`);
26
+ return dialect.addTime(bucket, bucketIndices, grain);
30
27
  }
31
- export function windowScanStartSql(window, grain) {
28
+ export function windowScanStartSql(dialect, window, grain) {
32
29
  if (window.trailing)
33
- return subtract(add('_hq_first', 1, grain), window.trailing.amount, window.trailing.unit);
30
+ return dialect.subtractTime(dialect.addTime('_hq_first', 1, grain), window.trailing.amount, window.trailing.unit);
34
31
  if (window.toDate)
35
- return `${GRAIN_FUNCTIONS[window.toDate]}(_hq_first)`;
32
+ return dialect.truncateTime('_hq_first', window.toDate);
36
33
  throw new Error('Cumulative windows have unbounded history.');
37
34
  }
38
35
  /** Base measures respect partial endpoints; windows use entire contributing buckets. */
39
- export function windowMeasureRowsSql(window, axis) {
36
+ export function windowMeasureRowsSql(dialect, window, axis) {
40
37
  const range = window ? '' : ` WHERE _hq_time ${axis.lowerInclusive ? '>=' : '>'} _hq_lower AND _hq_time ${axis.upperInclusive ? '<=' : '<'} _hq_upper`;
41
- return `SELECT *, ${contributionSql(window, axis.grain)} AS _hq_period FROM _hq_scanned${range}`;
38
+ return `SELECT *, ${contributionSql(dialect, window, axis.grain)} AS _hq_period FROM _hq_scanned${range}`;
42
39
  }
43
40
  /** Fill dimension combinations, aggregate populations, and join measure values onto the series. */
44
- export function buildTimeMeasureValuesSql(dims, inputs, axis) {
41
+ export function buildTimeMeasureValuesSql(dialect, dims, inputs, axis) {
45
42
  const ctes = [];
46
43
  const keys = dims.map(dimension => dimension.alias);
47
44
  const combinations = keys.length
48
45
  ? ` CROSS JOIN (SELECT DISTINCT ${keys.join(', ')} FROM _hq_scanned) AS _hq_combinations`
49
46
  : '';
50
47
  ctes.push(`_hq_skeleton AS (SELECT * FROM _hq_series${combinations})`);
51
- const projections = ['s._hq_period AS period', ...dims.map(dimension => `s.${dimension.alias} AS ${quoteSQLIdentifier(dimension.name)}`)];
48
+ const projections = ['s._hq_period AS period', ...dims.map(dimension => `s.${dimension.alias} AS ${dialect.quoteIdentifier(dimension.name)}`)];
52
49
  const joins = [];
53
50
  let index = 0;
54
51
  for (const input of inputs) {
@@ -58,28 +55,28 @@ export function buildTimeMeasureValuesSql(dims, inputs, axis) {
58
55
  const aggregate = `_hq_agg${index}`;
59
56
  ctes.push(...rowCtes, `${fan} AS (${rowsSql})`);
60
57
  // toNullable makes unmatched LEFT JOIN values NULL regardless of join_use_nulls.
61
- const expression = windowAggregateSql(base.definition, base.value, base.arg);
58
+ const expression = dialect.windowAggregate(base.definition, base.value, base.arg);
62
59
  const groupColumns = `_hq_period${keys.length ? `, ${keys.join(', ')}` : ''}`;
63
60
  const outputRange = restrictToAxis ? ' WHERE _hq_period BETWEEN _hq_first AND _hq_last' : '';
64
61
  const trailingRange = window?.trailing
65
- ? ` AND _hq_time >= ${subtract(add('_hq_period', 1, axis.grain), window.trailing.amount, window.trailing.unit)}`
62
+ ? ` AND _hq_time >= ${dialect.subtractTime(dialect.addTime('_hq_period', 1, axis.grain), window.trailing.amount, window.trailing.unit)}`
66
63
  : '';
67
- const aggregationSql = `SELECT ${groupColumns}, toNullable(${expression}) AS ${output}`
64
+ const aggregationSql = `SELECT ${groupColumns}, ${dialect.toNullable(expression)} AS ${output}`
68
65
  + ` FROM ${fan}${outputRange}${trailingRange} GROUP BY ${groupColumns}`;
69
66
  ctes.push(`${aggregate} AS (${aggregationSql})`);
70
67
  const alias = `m${index}`;
71
- const condition = [`s._hq_period = ${alias}._hq_period`, ...keys.map(key => `isNotDistinctFrom(s.${key}, ${alias}.${key})`)];
68
+ const condition = [`s._hq_period = ${alias}._hq_period`, ...keys.map(key => dialect.isNotDistinctFrom(`s.${key}`, `${alias}.${key}`))];
72
69
  joins.push(`LEFT JOIN ${aggregate} AS ${alias} ON ${condition.join(' AND ')}`);
73
70
  let value = `${alias}.${output}`;
74
71
  if (window?.cumulative) {
75
72
  const operation = base.definition.aggregation === 'count' ? 'sum' : base.definition.aggregation;
76
73
  const partition = keys.length ? `PARTITION BY ${keys.map(key => `s.${key}`).join(', ')} ` : '';
77
74
  const frame = `${partition}ORDER BY s._hq_period ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW`;
78
- value = `${operation}OrNull(${value}) OVER (${frame})`;
75
+ value = `${dialect.reaggregate(operation, value)} OVER (${frame})`;
79
76
  }
80
77
  if (isCountAggregation(base.definition))
81
78
  value = `coalesce(${value}, 0)`;
82
- projections.push(`${value} AS ${quoteSQLIdentifier(name)}`);
79
+ projections.push(`${value} AS ${dialect.quoteIdentifier(name)}`);
83
80
  index++;
84
81
  }
85
82
  ctes.push(`_hq_values AS (SELECT ${projections.join(', ')} FROM _hq_skeleton AS s ${joins.join(' ')})`);
@@ -0,0 +1,17 @@
1
+ import { type ParsedTimeBound } from './time-axis-bounds.js';
2
+ /**
3
+ * Parse an RFC 3339 instant: ISO 8601 text carrying `Z` or a numeric offset,
4
+ * the form `Date.prototype.toISOString` produces. Offset-free text is a local
5
+ * reading rather than an instant and yields undefined, as does anything that
6
+ * is not a timestamp.
7
+ */
8
+ export declare function parseTimestampInstant(value: unknown): ParsedTimeBound | undefined;
9
+ /**
10
+ * The wall clock an instant shows in `timezone`, spelled
11
+ * `YYYY-MM-DD hh:mm:ss[.fraction]`: the form ClickHouse reads as local time in
12
+ * the zone of the DateTime64 it is compared against. Offsets are whole
13
+ * minutes, so the authored sub-second digits survive unchanged; a zero
14
+ * fraction is dropped.
15
+ */
16
+ export declare function instantWallClock(instant: ParsedTimeBound, timezone: string): string;
17
+ //# sourceMappingURL=timestamp-instants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-instants.d.ts","sourceRoot":"","sources":["../../src/utils/timestamp-instants.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7E;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,GAAG,SAAS,CAGjF;AAUD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CASnF"}
@@ -0,0 +1,34 @@
1
+ import { parseTimeBound } from './time-axis-bounds.js';
2
+ /**
3
+ * Parse an RFC 3339 instant: ISO 8601 text carrying `Z` or a numeric offset,
4
+ * the form `Date.prototype.toISOString` produces. Offset-free text is a local
5
+ * reading rather than an instant and yields undefined, as does anything that
6
+ * is not a timestamp.
7
+ */
8
+ export function parseTimestampInstant(value) {
9
+ const instant = parseTimeBound(value);
10
+ return instant?.hasOffset ? instant : undefined;
11
+ }
12
+ const NANOSECONDS_PER_SECOND = 1000000000n;
13
+ const CLOCK_PARTS = {
14
+ hourCycle: 'h23',
15
+ year: 'numeric', month: '2-digit', day: '2-digit',
16
+ hour: '2-digit', minute: '2-digit', second: '2-digit',
17
+ };
18
+ /**
19
+ * The wall clock an instant shows in `timezone`, spelled
20
+ * `YYYY-MM-DD hh:mm:ss[.fraction]`: the form ClickHouse reads as local time in
21
+ * the zone of the DateTime64 it is compared against. Offsets are whole
22
+ * minutes, so the authored sub-second digits survive unchanged; a zero
23
+ * fraction is dropped.
24
+ */
25
+ export function instantWallClock(instant, timezone) {
26
+ const parts = new Intl.DateTimeFormat('en-US', { ...CLOCK_PARTS, timeZone: timezone })
27
+ .formatToParts(new Date(instant.milliseconds));
28
+ const part = (type) => parts.find(entry => entry.type === type)?.value ?? '';
29
+ const date = `${part('year').padStart(4, '0')}-${part('month')}-${part('day')}`;
30
+ const clock = `${part('hour')}:${part('minute')}:${part('second')}`;
31
+ const remainder = ((instant.nanoseconds % NANOSECONDS_PER_SECOND) + NANOSECONDS_PER_SECOND) % NANOSECONDS_PER_SECOND;
32
+ const fraction = remainder === 0n ? '' : `.${remainder.toString().padStart(9, '0').replace(/0+$/, '')}`;
33
+ return `${date} ${clock}${fraction}`;
34
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * True for the timestamp text a filter may bind: a real calendar date, or a
3
+ * date and time with seconds, optionally with a fraction and `Z`/offset.
4
+ *
5
+ * ClickHouse reads an invalid `DateTime64` parameter as some other instant
6
+ * instead of refusing it (`2026-13-45` becomes 1900-01-01, `2026-02-30`
7
+ * rolls into March, digits are read as Unix seconds), and refuses a time
8
+ * without seconds, so anything else is rejected before planning. This is
9
+ * the same rule as the Python planner's.
10
+ */
11
+ export declare function isTimestampText(value: unknown): value is string;
12
+ //# sourceMappingURL=timestamp-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timestamp-text.d.ts","sourceRoot":"","sources":["../../src/utils/timestamp-text.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAK/D"}
@@ -0,0 +1,18 @@
1
+ import { parseTimeBound } from './time-axis-bounds.js';
2
+ /**
3
+ * True for the timestamp text a filter may bind: a real calendar date, or a
4
+ * date and time with seconds, optionally with a fraction and `Z`/offset.
5
+ *
6
+ * ClickHouse reads an invalid `DateTime64` parameter as some other instant
7
+ * instead of refusing it (`2026-13-45` becomes 1900-01-01, `2026-02-30`
8
+ * rolls into March, digits are read as Unix seconds), and refuses a time
9
+ * without seconds, so anything else is rejected before planning. This is
10
+ * the same rule as the Python planner's.
11
+ */
12
+ export function isTimestampText(value) {
13
+ const parsed = parseTimeBound(value);
14
+ if (!parsed)
15
+ return false;
16
+ const time = parsed.text.slice(10);
17
+ return time === '' || /^[T ]\d{2}:\d{2}:\d{2}/.test(time);
18
+ }
@@ -0,0 +1,8 @@
1
+ import { type AnyZodSchema } from './zod-schema-version.js';
2
+ /**
3
+ * Draft-07 JSON Schema for a Zod 3 or Zod 4 schema, describing what a caller
4
+ * may send. `zod-to-json-schema` only reads Zod 3 (it returns an empty schema
5
+ * for Zod 4), and Zod 4 ships its own generator.
6
+ */
7
+ export declare function zodToDraft7JsonSchema(schema: AnyZodSchema): Record<string, unknown>;
8
+ //# sourceMappingURL=zod-json-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-json-schema.d.ts","sourceRoot":"","sources":["../../src/utils/zod-json-schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,KAAK,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE1E;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASnF"}
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod';
2
+ import { zodToJsonSchema } from 'zod-to-json-schema';
3
+ import { isZod4Schema } from './zod-schema-version.js';
4
+ /**
5
+ * Draft-07 JSON Schema for a Zod 3 or Zod 4 schema, describing what a caller
6
+ * may send. `zod-to-json-schema` only reads Zod 3 (it returns an empty schema
7
+ * for Zod 4), and Zod 4 ships its own generator.
8
+ */
9
+ export function zodToDraft7JsonSchema(schema) {
10
+ if (isZod4Schema(schema)) {
11
+ return z.toJSONSchema(schema, {
12
+ target: 'draft-7',
13
+ io: 'input',
14
+ unrepresentable: 'any',
15
+ });
16
+ }
17
+ return zodToJsonSchema(schema, { target: 'jsonSchema7', $refStrategy: 'none' });
18
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Any Zod schema, from either major version or another installed copy of Zod.
3
+ *
4
+ * Structural on purpose: a caller's schema built with their own `zod@3` is not
5
+ * assignable to this package's `zod@4` types, but both carry their definition
6
+ * on `_def` (v3) or `_zod` (v4).
7
+ */
8
+ export type AnyZodSchema = {
9
+ readonly _def: unknown;
10
+ } | {
11
+ readonly _zod: unknown;
12
+ };
13
+ /** Zod 4 (classic or mini) keeps its internals on `_zod`; Zod 3 has none. */
14
+ export declare function isZod4Schema(schema: unknown): schema is {
15
+ readonly _zod: {
16
+ readonly def: Record<string, any>;
17
+ };
18
+ };
19
+ //# sourceMappingURL=zod-schema-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-schema-version.d.ts","sourceRoot":"","sources":["../../src/utils/zod-schema-version.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEnF,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI;IAAE,QAAQ,CAAC,IAAI,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,CAAA;CAAE,CAIhH"}
@@ -0,0 +1,6 @@
1
+ /** Zod 4 (classic or mini) keeps its internals on `_zod`; Zod 3 has none. */
2
+ export function isZod4Schema(schema) {
3
+ return typeof schema === 'object'
4
+ && schema !== null
5
+ && typeof schema._zod === 'object';
6
+ }
@@ -0,0 +1,10 @@
1
+ import { type ProtocolSchemaDraft } from './protocol-schema-conversion.js';
2
+ /** A Zod 3 schema, read through its `_def`. Callers may hold any 3.x copy of Zod. */
3
+ type ZodV3Schema = {
4
+ readonly _def: Record<string, any>;
5
+ readonly description?: string;
6
+ };
7
+ /** Converts the portable subset of a Zod 3 schema into an RFC 0004 draft. */
8
+ export declare function convertZodV3Schema(schema: ZodV3Schema, path: string): ProtocolSchemaDraft;
9
+ export {};
10
+ //# sourceMappingURL=zod-v3-protocol-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-v3-protocol-schema.d.ts","sourceRoot":"","sources":["../../src/utils/zod-v3-protocol-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,mBAAmB,EACzB,MAAM,iCAAiC,CAAC;AAEzC,qFAAqF;AACrF,KAAK,WAAW,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AA8DzF,6EAA6E;AAC7E,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,mBAAmB,CAyGzF"}