@hypequery/datasets 0.20.0 → 0.22.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 (172) hide show
  1. package/README.md +6 -0
  2. package/dist/api.type-test.js +7 -1
  3. package/dist/catalog.d.ts +7 -3
  4. package/dist/catalog.d.ts.map +1 -1
  5. package/dist/catalog.js +15 -3
  6. package/dist/contract.d.ts +1 -1
  7. package/dist/contract.d.ts.map +1 -1
  8. package/dist/dataset-compilation.d.ts +45 -0
  9. package/dist/dataset-compilation.d.ts.map +1 -0
  10. package/dist/dataset-compilation.js +1 -0
  11. package/dist/dataset-query.d.ts +3 -3
  12. package/dist/dataset-query.d.ts.map +1 -1
  13. package/dist/dataset-query.js +7 -71
  14. package/dist/dataset-sql-dialect.d.ts +11 -0
  15. package/dist/dataset-sql-dialect.d.ts.map +1 -0
  16. package/dist/dataset-sql-dialect.js +1 -0
  17. package/dist/dataset.d.ts +3 -2
  18. package/dist/dataset.d.ts.map +1 -1
  19. package/dist/execution-capabilities.d.ts +28 -0
  20. package/dist/execution-capabilities.d.ts.map +1 -0
  21. package/dist/execution-capabilities.js +17 -0
  22. package/dist/execution-capabilities.json +13 -0
  23. package/dist/executor.d.ts +17 -38
  24. package/dist/executor.d.ts.map +1 -1
  25. package/dist/executor.js +37 -400
  26. package/dist/index.d.ts +9 -4
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +3 -2
  29. package/dist/internal.d.ts +3 -0
  30. package/dist/internal.d.ts.map +1 -1
  31. package/dist/internal.js +2 -0
  32. package/dist/measure.d.ts +56 -19
  33. package/dist/measure.d.ts.map +1 -1
  34. package/dist/measure.js +5 -65
  35. package/dist/metric-query-engine.d.ts +29 -0
  36. package/dist/metric-query-engine.d.ts.map +1 -0
  37. package/dist/metric-query-engine.js +106 -0
  38. package/dist/portable-executor.d.ts +20 -6
  39. package/dist/portable-executor.d.ts.map +1 -1
  40. package/dist/portable-executor.js +29 -9
  41. package/dist/query-builder-protocol.d.ts +4 -0
  42. package/dist/query-builder-protocol.d.ts.map +1 -1
  43. package/dist/query-helpers.d.ts +10 -10
  44. package/dist/query-helpers.d.ts.map +1 -1
  45. package/dist/query-helpers.js +1 -3
  46. package/dist/query-planner.d.ts +7 -3
  47. package/dist/query-planner.d.ts.map +1 -1
  48. package/dist/query-planner.js +10 -12
  49. package/dist/semantic-planner.d.ts.map +1 -1
  50. package/dist/semantic-planner.js +2 -0
  51. package/dist/semantic-query-schema.d.ts.map +1 -1
  52. package/dist/semantic-query-schema.js +3 -3
  53. package/dist/types.d.ts +22 -16
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/utils/agent-catalog-projection.d.ts +1 -0
  56. package/dist/utils/agent-catalog-projection.d.ts.map +1 -1
  57. package/dist/utils/agent-catalog-projection.js +2 -1
  58. package/dist/utils/approximate-measures.d.ts +1 -0
  59. package/dist/utils/approximate-measures.d.ts.map +1 -1
  60. package/dist/utils/approximate-measures.js +10 -4
  61. package/dist/utils/base-measure-definition.d.ts +23 -0
  62. package/dist/utils/base-measure-definition.d.ts.map +1 -0
  63. package/dist/utils/base-measure-definition.js +65 -0
  64. package/dist/utils/build-dataset-query-builder.d.ts +5 -0
  65. package/dist/utils/build-dataset-query-builder.d.ts.map +1 -0
  66. package/dist/utils/build-dataset-query-builder.js +62 -0
  67. package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +4 -0
  68. package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -0
  69. package/dist/utils/clickhouse-dataset-sql-dialect.js +6 -0
  70. package/dist/utils/compile-dataset-query.d.ts +10 -0
  71. package/dist/utils/compile-dataset-query.d.ts.map +1 -0
  72. package/dist/utils/compile-dataset-query.js +71 -0
  73. package/dist/utils/composite-time-measure-sql.d.ts +10 -0
  74. package/dist/utils/composite-time-measure-sql.d.ts.map +1 -0
  75. package/dist/utils/composite-time-measure-sql.js +61 -0
  76. package/dist/utils/dataset-derived-query.d.ts +1 -2
  77. package/dist/utils/dataset-derived-query.d.ts.map +1 -1
  78. package/dist/utils/dataset-derived-query.js +16 -30
  79. package/dist/utils/dataset-filter-types.d.ts +28 -0
  80. package/dist/utils/dataset-filter-types.d.ts.map +1 -0
  81. package/dist/utils/dataset-filter-types.js +1 -0
  82. package/dist/utils/dataset-query-validation.d.ts.map +1 -1
  83. package/dist/utils/dataset-query-validation.js +15 -3
  84. package/dist/utils/dataset-sql-dialect.d.ts +5 -0
  85. package/dist/utils/dataset-sql-dialect.d.ts.map +1 -0
  86. package/dist/utils/dataset-sql-dialect.js +5 -0
  87. package/dist/utils/dataset-target.d.ts +3 -0
  88. package/dist/utils/dataset-target.d.ts.map +1 -0
  89. package/dist/utils/dataset-target.js +3 -0
  90. package/dist/utils/derived-measure-sql.d.ts +1 -0
  91. package/dist/utils/derived-measure-sql.d.ts.map +1 -1
  92. package/dist/utils/derived-measure-sql.js +9 -6
  93. package/dist/utils/derived-measure-validation.d.ts.map +1 -1
  94. package/dist/utils/derived-measure-validation.js +4 -25
  95. package/dist/utils/filter-definition.d.ts +3 -0
  96. package/dist/utils/filter-definition.d.ts.map +1 -0
  97. package/dist/utils/filter-definition.js +3 -0
  98. package/dist/utils/measure-dependencies.d.ts +8 -0
  99. package/dist/utils/measure-dependencies.d.ts.map +1 -0
  100. package/dist/utils/measure-dependencies.js +44 -0
  101. package/dist/utils/measure-evaluation-graph.d.ts +23 -0
  102. package/dist/utils/measure-evaluation-graph.d.ts.map +1 -0
  103. package/dist/utils/measure-evaluation-graph.js +38 -0
  104. package/dist/utils/measure-time-grains.d.ts.map +1 -1
  105. package/dist/utils/measure-time-grains.js +4 -2
  106. package/dist/utils/metric-derived-query.d.ts +8 -0
  107. package/dist/utils/metric-derived-query.d.ts.map +1 -0
  108. package/dist/utils/metric-derived-query.js +106 -0
  109. package/dist/utils/metric-query-builder.d.ts +5 -0
  110. package/dist/utils/metric-query-builder.d.ts.map +1 -0
  111. package/dist/utils/metric-query-builder.js +50 -0
  112. package/dist/utils/metric-query-result.d.ts +10 -0
  113. package/dist/utils/metric-query-result.d.ts.map +1 -0
  114. package/dist/utils/metric-query-result.js +18 -0
  115. package/dist/utils/metric-query-validation.d.ts +6 -0
  116. package/dist/utils/metric-query-validation.d.ts.map +1 -0
  117. package/dist/utils/metric-query-validation.js +116 -0
  118. package/dist/utils/relationship-builder-plan.d.ts +4 -2
  119. package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
  120. package/dist/utils/relationship-builder-plan.js +10 -13
  121. package/dist/utils/relationship-measure-sql.d.ts +14 -0
  122. package/dist/utils/relationship-measure-sql.d.ts.map +1 -0
  123. package/dist/utils/relationship-measure-sql.js +44 -0
  124. package/dist/utils/relationship-measure-types.d.ts +36 -0
  125. package/dist/utils/relationship-measure-types.d.ts.map +1 -0
  126. package/dist/utils/relationship-measure-types.js +1 -0
  127. package/dist/utils/relationship-measures.d.ts +23 -0
  128. package/dist/utils/relationship-measures.d.ts.map +1 -0
  129. package/dist/utils/relationship-measures.js +62 -0
  130. package/dist/utils/relationship-validation.d.ts +1 -1
  131. package/dist/utils/relationship-validation.d.ts.map +1 -1
  132. package/dist/utils/relationship-validation.js +3 -5
  133. package/dist/utils/semantic-query-syntax.d.ts +6 -0
  134. package/dist/utils/semantic-query-syntax.d.ts.map +1 -0
  135. package/dist/utils/semantic-query-syntax.js +25 -0
  136. package/dist/utils/shift-measure-grains.d.ts +5 -0
  137. package/dist/utils/shift-measure-grains.d.ts.map +1 -0
  138. package/dist/utils/shift-measure-grains.js +10 -0
  139. package/dist/utils/shift-measure-sql.d.ts +5 -2
  140. package/dist/utils/shift-measure-sql.d.ts.map +1 -1
  141. package/dist/utils/shift-measure-sql.js +25 -8
  142. package/dist/utils/shift-measure-validation.js +3 -3
  143. package/dist/utils/shift-wall-time-sql.d.ts +4 -0
  144. package/dist/utils/shift-wall-time-sql.d.ts.map +1 -0
  145. package/dist/utils/shift-wall-time-sql.js +17 -0
  146. package/dist/utils/snapshot-execution-context.d.ts +4 -0
  147. package/dist/utils/snapshot-execution-context.d.ts.map +1 -0
  148. package/dist/utils/snapshot-execution-context.js +16 -0
  149. package/dist/utils/time-measure-axis-sql.d.ts +1 -0
  150. package/dist/utils/time-measure-axis-sql.d.ts.map +1 -1
  151. package/dist/utils/time-measure-axis-sql.js +5 -4
  152. package/dist/utils/time-measure-axis.d.ts.map +1 -1
  153. package/dist/utils/time-measure-axis.js +3 -2
  154. package/dist/utils/time-measure-context-sql.d.ts +6 -0
  155. package/dist/utils/time-measure-context-sql.d.ts.map +1 -0
  156. package/dist/utils/time-measure-context-sql.js +21 -0
  157. package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
  158. package/dist/utils/time-measure-dataset-sql.js +27 -9
  159. package/dist/utils/time-measure-leaf-sql.d.ts +14 -0
  160. package/dist/utils/time-measure-leaf-sql.d.ts.map +1 -0
  161. package/dist/utils/time-measure-leaf-sql.js +52 -0
  162. package/dist/utils/time-measure-result-sql.d.ts +1 -1
  163. package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
  164. package/dist/utils/time-measure-result-sql.js +4 -3
  165. package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
  166. package/dist/utils/time-measure-source-sql.js +8 -4
  167. package/dist/utils/time-query-measures.d.ts.map +1 -1
  168. package/dist/utils/time-query-measures.js +4 -8
  169. package/dist/utils/window-query-signature.d.ts.map +1 -1
  170. package/dist/utils/window-query-signature.js +12 -2
  171. package/dist/validation.js +1 -1
  172. package/package.json +8 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickhouse-dataset-sql-dialect.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-dataset-sql-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAGnE,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,EAAE,iBAGxC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { quoteSQLIdentifier } from '../sql-utils.js';
2
+ /** Preserve the existing ClickHouse spelling and escaping byte for byte. */
3
+ export const clickhouseDatasetSqlDialect = Object.freeze({
4
+ name: 'clickhouse',
5
+ quoteIdentifier: quoteSQLIdentifier,
6
+ });
@@ -0,0 +1,10 @@
1
+ import type { DatasetCompilation } from '../dataset-compilation.js';
2
+ import type { DatasetQueryExecutionOptions } from '../dataset-query.js';
3
+ import type { AnyDatasetInstance, DatasetQuery } from '../types.js';
4
+ export interface PreparedDatasetQuery {
5
+ readonly compilation: DatasetCompilation;
6
+ execute(): Promise<Record<string, unknown>[]>;
7
+ }
8
+ /** The one dispatch point for dataset preview and query-builder execution. */
9
+ export declare function prepareDatasetQuery(dataset: AnyDatasetInstance, input: DatasetQuery, options: DatasetQueryExecutionOptions, mode?: 'execution' | 'logical'): PreparedDatasetQuery;
10
+ //# sourceMappingURL=compile-dataset-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compile-dataset-query.d.ts","sourceRoot":"","sources":["../../src/utils/compile-dataset-query.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAA2D,MAAM,2BAA2B,CAAC;AAC7H,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AASpE,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,OAAO,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CAC/C;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,IAAI,GAAE,WAAW,GAAG,SAAuB,GAC1C,oBAAoB,CA4DtB"}
@@ -0,0 +1,71 @@
1
+ import { queryMeasureDefinitions } from './relationship-measures.js';
2
+ import { buildDatasetQueryBuilder } from './build-dataset-query-builder.js';
3
+ import { baseMeasureNames } from './dataset-measures.js';
4
+ import { buildDerivedDatasetSql, hasSelectedDerivedMeasure } from './dataset-derived-query.js';
5
+ import { measureDependencyNames } from './measure-dependencies.js';
6
+ import { overfetchLimit } from './pagination.js';
7
+ import { resolveResultLimit } from './result-limits.js';
8
+ import { buildTimeMeasureDatasetSql } from './time-measure-dataset-sql.js';
9
+ import { selectedTimeMeasures } from './time-query-measures.js';
10
+ /** The one dispatch point for dataset preview and query-builder execution. */
11
+ export function prepareDatasetQuery(dataset, input, options, mode = 'execution') {
12
+ const query = structuredClone({
13
+ ...input,
14
+ limit: mode === 'execution' ? resolveResultLimit(input.limit, dataset.limits).limit : input.limit,
15
+ });
16
+ // Preserve omission while validating/planning: maxMeasures constrains an
17
+ // explicit selection, not the dataset's default set of base measures.
18
+ const measures = query.measures ?? baseMeasureNames(dataset.measures);
19
+ const executionLimit = mode === 'execution' ? overfetchLimit(query.limit) : query.limit;
20
+ const executionOptions = { ...options, executionLimit };
21
+ const preflightStatements = [];
22
+ let statement;
23
+ let execute;
24
+ let plan;
25
+ const abortSignal = options.context?.abortSignal;
26
+ if (selectedTimeMeasures(dataset, query).size) {
27
+ plan = 'time';
28
+ const compiled = buildTimeMeasureDatasetSql(dataset, query, executionOptions);
29
+ statement = compiled;
30
+ preflightStatements.push({ sql: compiled.timeAxisSql, parameters: compiled.parameters });
31
+ execute = async () => {
32
+ await options.builderFactory.rawQuery(compiled.timeAxisSql, compiled.parameters, { abortSignal });
33
+ return options.builderFactory.rawQuery(compiled.sql, compiled.parameters, { abortSignal });
34
+ };
35
+ }
36
+ else if (hasSelectedDerivedMeasure(dataset, query)) {
37
+ plan = 'derived';
38
+ statement = buildDerivedDatasetSql(dataset, query, executionOptions, buildDatasetQueryBuilder);
39
+ execute = () => options.builderFactory.rawQuery(statement.sql, statement.parameters, { abortSignal });
40
+ }
41
+ else {
42
+ plan = 'aggregate';
43
+ const builder = buildDatasetQueryBuilder(dataset, query, executionOptions);
44
+ statement = builder.toSQLWithParams();
45
+ // Retain builder execution: custom builders may carry adapter settings.
46
+ execute = () => builder.execute({ abortSignal });
47
+ }
48
+ const description = Object.freeze({
49
+ kind: 'dataset-compilation', version: 1, dataset: dataset.name, plan,
50
+ dimensions: Object.freeze([...(query.dimensions ?? [])]),
51
+ measures: Object.freeze([...measures]),
52
+ measureDependencies: Object.freeze(measureDependencyNames(queryMeasureDefinitions(dataset, measures), measures)),
53
+ filters: Object.freeze((query.filters ?? []).map(({ field, operator }) => Object.freeze({ field, operator }))),
54
+ segments: Object.freeze([...(query.segments ?? [])]),
55
+ by: query.by, timezone: query.timezone,
56
+ effectiveLimit: query.limit, executionLimit, offset: query.offset ?? 0,
57
+ parameterCount: statement.parameters.length,
58
+ preflightStatementCount: preflightStatements.length,
59
+ });
60
+ const compilation = Object.freeze({
61
+ sql: statement.sql,
62
+ parameters: Object.freeze(structuredClone(statement.parameters)),
63
+ query: Object.freeze(structuredClone({ ...query, measures })),
64
+ preflightStatements: Object.freeze(preflightStatements.map(preflight => Object.freeze({
65
+ sql: preflight.sql, parameters: Object.freeze(structuredClone(preflight.parameters)),
66
+ }))),
67
+ describe: () => description,
68
+ toJSON: () => description,
69
+ });
70
+ return { compilation, execute };
71
+ }
@@ -0,0 +1,10 @@
1
+ import type { AnyDatasetInstance, DatasetQuery } from '../types.js';
2
+ import type { DatasetQueryExecutionOptions } from '../dataset-query.js';
3
+ import type { TimeMeasureAxis } from './time-measure-axis.js';
4
+ /** Aggregate each dependency in its evaluation context, then evaluate formulas. */
5
+ export declare function buildCompositeTimeMeasureSql(ds: AnyDatasetInstance, query: DatasetQuery, options: DatasetQueryExecutionOptions, axis: TimeMeasureAxis): {
6
+ sql: string;
7
+ parameters: unknown[];
8
+ timeAxisSql: string;
9
+ };
10
+ //# sourceMappingURL=composite-time-measure-sql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"composite-time-measure-sql.d.ts","sourceRoot":"","sources":["../../src/utils/composite-time-measure-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AACxE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAW9D,mFAAmF;AACnF,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,4BAA4B,EAAE,IAAI,EAAE,eAAe;;;;EAgDrJ"}
@@ -0,0 +1,61 @@
1
+ import { queryMeasureDefinitions } from './relationship-measures.js';
2
+ import { quoteSQLIdentifier } from '../sql-utils.js';
3
+ import { isDerivedMeasure, isWindowMeasure } from './dataset-measures.js';
4
+ import { buildMeasureEvaluationGraph, evaluationFormula } from './measure-evaluation-graph.js';
5
+ import { buildTimeMeasureSourceSql } from './time-measure-source-sql.js';
6
+ import { buildTimeMeasureAxisSql } from './time-measure-axis-sql.js';
7
+ import { timeMeasureContextCtes, timeMeasureContextGuard } from './time-measure-context-sql.js';
8
+ import { timeMeasureLeafSql } from './time-measure-leaf-sql.js';
9
+ import { derivedProjection } from './derived-measure-sql.js';
10
+ import { buildTimeMeasureResultSql } from './time-measure-result-sql.js';
11
+ /** Aggregate each dependency in its evaluation context, then evaluate formulas. */
12
+ export function buildCompositeTimeMeasureSql(ds, query, options, axis) {
13
+ const graph = buildMeasureEvaluationGraph(queryMeasureDefinitions(ds, query.measures ?? []), query.measures ?? []);
14
+ const leaves = graph.nodes.filter(node => !isDerivedMeasure(node.definition));
15
+ const baseNames = new Set(leaves.map(node => isWindowMeasure(node.definition) ? node.definition.measure : node.name));
16
+ const source = buildTimeMeasureSourceSql(ds, query, baseNames, axis.filters, options);
17
+ const timeAxis = buildTimeMeasureAxisSql(source, axis);
18
+ const contexts = timeMeasureContextCtes(graph.contexts, axis);
19
+ const inputs = leaves.map(node => {
20
+ const window = isWindowMeasure(node.definition) ? node.definition : undefined;
21
+ const base = source.bases.find(base => base.name === (window?.measure ?? node.name));
22
+ return { node, ...timeMeasureLeafSql(node.id, node.context, base, window, axis, source.dimensions) };
23
+ });
24
+ const keys = source.dimensions.map(dim => dim.alias);
25
+ const combinations = keys.length ? ` CROSS JOIN (SELECT DISTINCT ${keys.join(', ')} FROM (${inputs.map(input => `SELECT ${keys.join(', ')} FROM ${input.population}`).join(' UNION ALL ')})) AS _hq_combinations` : '';
26
+ const guardCtes = [...source.ctes, ...timeAxis.ctes, ...contexts, ...inputs.flatMap(input => input.ctes)];
27
+ const ctes = [...guardCtes,
28
+ `_hq_skeleton AS (SELECT * FROM _hq_series${combinations})`];
29
+ let previous = '_hq_skeleton';
30
+ for (const input of inputs) {
31
+ const { node } = input;
32
+ const output = `_hq_value${node.id}`;
33
+ const next = `_hq_evaluated${node.id}`;
34
+ const value = input.count ? `coalesce(m.${output}, 0)` : `m.${output}`;
35
+ if (input.cumulative) {
36
+ const partition = keys.length ? `tuple(${keys.map(key => `s.${key}`).join(', ')})` : 'tuple(0)';
37
+ const lookup = `_hq_lookup${node.id}`;
38
+ ctes.push(`${lookup} AS (SELECT s.*, c._hq_eval_end, ${partition} AS _hq_partition FROM ${previous} AS s INNER JOIN _hq_context${input.context} AS c ON s._hq_period = c._hq_period)`);
39
+ ctes.push(`${next} AS (SELECT s.* EXCEPT (_hq_eval_end, _hq_partition), ${value} AS ${output} FROM ${lookup} AS s LEFT ASOF JOIN ${input.aggregate} AS m ON s._hq_partition = m._hq_partition AND s._hq_eval_end > m._hq_time)`);
40
+ }
41
+ else {
42
+ const condition = ['s._hq_period = m._hq_period', ...keys.map(key => `isNotDistinctFrom(s.${key}, m.${key})`)];
43
+ ctes.push(`${next} AS (SELECT s.*, ${value} AS ${output} FROM ${previous} AS s LEFT JOIN ${input.aggregate} AS m ON ${condition.join(' AND ')})`);
44
+ }
45
+ previous = next;
46
+ }
47
+ for (const node of graph.nodes.filter(node => isDerivedMeasure(node.definition))) {
48
+ const next = `_hq_formula${node.id}`;
49
+ ctes.push(`${next} AS (SELECT *, ${derivedProjection(`_hq_value${node.id}`, evaluationFormula(node))} FROM ${previous})`);
50
+ previous = next;
51
+ }
52
+ const projections = ['v._hq_period AS period', ...source.dimensions.map(dim => `v.${dim.alias} AS ${quoteSQLIdentifier(dim.name)}`),
53
+ ...Object.entries(graph.outputs).map(([name, id]) => `v._hq_value${id} AS ${quoteSQLIdentifier(name)}`)];
54
+ ctes.push(`_hq_values AS (SELECT ${projections.join(', ')} FROM ${previous} AS v)`);
55
+ const guard = [timeAxis.guard, timeAxis.rangeGuard, ...graph.contexts.map(context => timeMeasureContextGuard(context, axis)), ...inputs.map(input => input.guard)].join(' + ');
56
+ return {
57
+ sql: buildTimeMeasureResultSql(ds, query, options, source.dimensions, ctes, guard, true),
58
+ parameters: source.parameters,
59
+ timeAxisSql: `WITH ${guardCtes.join(',\n')} SELECT ${guard} AS _hq_validated FROM _hq_bounds`,
60
+ };
61
+ }
@@ -1,4 +1,4 @@
1
- import type { AnyDatasetInstance, DatasetQuery, DatasetQueryResult } from '../types.js';
1
+ import type { AnyDatasetInstance, DatasetQuery } from '../types.js';
2
2
  import type { QueryBuilderLike } from '../query-builder-protocol.js';
3
3
  import type { DatasetQueryExecutionOptions } from '../dataset-query.js';
4
4
  type BuildBaseQuery = (dataset: AnyDatasetInstance, query: DatasetQuery, options: DatasetQueryExecutionOptions) => QueryBuilderLike;
@@ -7,6 +7,5 @@ export declare function buildDerivedDatasetSql(ds: AnyDatasetInstance, query: Da
7
7
  sql: string;
8
8
  parameters: unknown[];
9
9
  };
10
- export declare function runDerivedDatasetQuery(ds: AnyDatasetInstance, query: DatasetQuery, options: DatasetQueryExecutionOptions, buildBaseQuery: BuildBaseQuery): Promise<DatasetQueryResult>;
11
10
  export {};
12
11
  //# sourceMappingURL=dataset-derived-query.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-derived-query.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-derived-query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACxF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAKrE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,KAAK,cAAc,GAAG,CACpB,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,KAClC,gBAAgB,CAAC;AAEtB,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAE9F;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,CA2CxC;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,kBAAkB,CAAC,CAgB7B"}
1
+ {"version":3,"file":"dataset-derived-query.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-derived-query.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAGrE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qBAAqB,CAAC;AAExE,KAAK,cAAc,GAAG,CACpB,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,KAClC,gBAAgB,CAAC;AAEtB,wBAAgB,yBAAyB,CAAC,EAAE,EAAE,kBAAkB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAE9F;AAED,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,cAAc,EAAE,cAAc,GAC7B;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,EAAE,CAAA;CAAE,CA4CxC"}
@@ -1,9 +1,10 @@
1
- import { derivedProjection } from './derived-measure-sql.js';
1
+ import { queryMeasureDefinitions } from './relationship-measures.js';
2
+ import { semanticOrderDirection } from './semantic-query-syntax.js';
3
+ import { measureDependencyNames } from './measure-dependencies.js';
4
+ import { derivedExpressionSql } from './derived-measure-sql.js';
2
5
  import { baseMeasureNames, getBaseMeasure, getDerivedMeasure } from './dataset-measures.js';
3
- import { quoteSQLIdentifier } from '../sql-utils.js';
6
+ import { resolveDatasetSqlDialect } from './dataset-sql-dialect.js';
4
7
  import { validateDatasetQueryInput } from './dataset-query-validation.js';
5
- import { overfetchLimit } from './pagination.js';
6
- import { toDatasetQueryResult } from './dataset-query-result.js';
7
8
  export function hasSelectedDerivedMeasure(ds, query) {
8
9
  return (query.measures ?? []).some(name => getDerivedMeasure(ds.measures, name) !== undefined);
9
10
  }
@@ -12,13 +13,13 @@ export function buildDerivedDatasetSql(ds, query, options, buildBaseQuery) {
12
13
  if (!validation.valid) {
13
14
  throw new Error(`Invalid dataset query: ${validation.errors.join('; ')}`);
14
15
  }
16
+ const dialect = resolveDatasetSqlDialect(options.builderFactory);
15
17
  const selected = query.measures ?? baseMeasureNames(ds.measures);
16
- const baseMeasures = new Set(selected.filter(name => getBaseMeasure(ds.measures, name) !== undefined));
17
- for (const name of selected) {
18
+ const baseMeasures = measureDependencyNames(queryMeasureDefinitions(ds, selected), selected).filter(name => name.includes('.') || getBaseMeasure(ds.measures, name));
19
+ const resolve = (name) => {
18
20
  const derived = getDerivedMeasure(ds.measures, name);
19
- if (derived)
20
- Object.values(derived.uses).forEach(base => baseMeasures.add(base));
21
- }
21
+ return derived ? `(${derivedExpressionSql(derived, resolve)})` : dialect.quoteIdentifier(name);
22
+ };
22
23
  const inner = buildBaseQuery(ds, {
23
24
  ...query,
24
25
  measures: [...baseMeasures],
@@ -29,21 +30,21 @@ export function buildDerivedDatasetSql(ds, query, options, buildBaseQuery) {
29
30
  const { sql: innerSql, parameters } = inner.toSQLWithParams();
30
31
  const projections = [];
31
32
  if (query.by)
32
- projections.push(quoteSQLIdentifier('period'));
33
+ projections.push(dialect.quoteIdentifier('period'));
33
34
  for (const dimension of query.dimensions ?? [])
34
- projections.push(quoteSQLIdentifier(dimension));
35
+ projections.push(dialect.quoteIdentifier(dimension));
35
36
  for (const name of selected) {
36
37
  const derived = getDerivedMeasure(ds.measures, name);
37
38
  projections.push(derived
38
- ? derivedProjection(name, derived)
39
- : quoteSQLIdentifier(name));
39
+ ? `${derivedExpressionSql(derived, resolve)} AS ${dialect.quoteIdentifier(name)}`
40
+ : dialect.quoteIdentifier(name));
40
41
  }
41
42
  let sql = `WITH base AS (${innerSql}) SELECT ${projections.join(', ')} FROM base`;
42
43
  if (query.orderBy?.length) {
43
- sql += ` ORDER BY ${query.orderBy.map(order => (`${quoteSQLIdentifier(order.field)} ${order.direction === 'asc' ? 'ASC' : 'DESC'}`)).join(', ')}`;
44
+ sql += ` ORDER BY ${query.orderBy.map(order => (`${dialect.quoteIdentifier(order.field)} ${semanticOrderDirection(order.direction)}`)).join(', ')}`;
44
45
  }
45
46
  else if (query.by) {
46
- sql += ` ORDER BY ${quoteSQLIdentifier('period')} ASC`;
47
+ sql += ` ORDER BY ${dialect.quoteIdentifier('period')} ASC`;
47
48
  }
48
49
  const limit = options.executionLimit ?? query.limit;
49
50
  if (limit !== undefined)
@@ -52,18 +53,3 @@ export function buildDerivedDatasetSql(ds, query, options, buildBaseQuery) {
52
53
  sql += ` OFFSET ${query.offset}`;
53
54
  return { sql, parameters };
54
55
  }
55
- export async function runDerivedDatasetQuery(ds, query, options, buildBaseQuery) {
56
- const start = Date.now();
57
- const { sql, parameters } = buildDerivedDatasetSql(ds, query, {
58
- ...options,
59
- executionLimit: overfetchLimit(query.limit),
60
- }, buildBaseQuery);
61
- const rows = await options.builderFactory.rawQuery(sql, parameters, { abortSignal: options.context?.abortSignal });
62
- return toDatasetQueryResult(rows, {
63
- dataset: ds,
64
- query,
65
- sql,
66
- timingMs: Date.now() - start,
67
- context: options.context,
68
- });
69
- }
@@ -0,0 +1,28 @@
1
+ import type { DatasetInstance, DimensionDefinition, MetricFilter, MetricFilterOperator, RelationshipDefinition, SemanticFilterDefinition, SemanticFiltersDefinition } from '../types.js';
2
+ /** The generated allowlist mirrors normalizeFilters; explicit maps replace it. */
3
+ export type NormalizedDatasetFilters<TDimensions extends Record<string, DimensionDefinition>, TFilters extends SemanticFiltersDefinition | undefined> = TFilters extends SemanticFiltersDefinition ? TFilters : {
4
+ [K in keyof TDimensions as TDimensions[K] extends {
5
+ filterable: false;
6
+ } ? never : K]: SemanticFilterDefinition & {
7
+ field: K & string;
8
+ };
9
+ };
10
+ type FilterOperators<TDefinition> = TDefinition extends {
11
+ operators: readonly (infer TOperator extends MetricFilterOperator)[];
12
+ } ? TOperator : MetricFilterOperator;
13
+ type LocalFilterFor<TFilters extends SemanticFiltersDefinition> = {
14
+ [K in keyof TFilters & string]: K extends `${string}.${string}` ? never : MetricFilter<K, unknown, FilterOperators<TFilters[K]>>;
15
+ }[keyof TFilters & string];
16
+ type RelationshipFilterFor<TRelationships extends Record<string, RelationshipDefinition>> = {
17
+ [R in keyof TRelationships & string]: TRelationships[R] extends RelationshipDefinition<infer TTarget, infer TKind> ? TKind extends 'hasMany' ? never : TTarget extends DatasetInstance<any, any, any, any> ? {
18
+ [K in keyof TTarget['filters'] & keyof TTarget['dimensions'] & string]: TTarget['dimensions'][K] extends {
19
+ sql: string;
20
+ } ? never : K extends TTarget['filters'][K]['field'] ? MetricFilter<`${R}.${K}`, unknown, FilterOperators<TTarget['filters'][K]>> : never;
21
+ }[keyof TTarget['filters'] & keyof TTarget['dimensions'] & string] : never : never;
22
+ }[keyof TRelationships & string];
23
+ /** Filter names and operator policies exposed by a dataset and its to-one targets. */
24
+ export type DatasetFilterFor<TDataset extends DatasetInstance<any, any, any, any>> = LocalFilterFor<TDataset['filters']> | RelationshipFilterFor<TDataset['relationships']>;
25
+ /** Names in the normalized filter allowlist, including one-hop relationship filters. */
26
+ export type DatasetFilterNames<TDataset extends DatasetInstance<any, any, any, any>> = DatasetFilterFor<TDataset>['field'];
27
+ export {};
28
+ //# sourceMappingURL=dataset-filter-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-filter-types.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-filter-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,oBAAoB,EACpB,sBAAsB,EACtB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,aAAa,CAAC;AAErB,kFAAkF;AAClF,MAAM,MAAM,wBAAwB,CAClC,WAAW,SAAS,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,EACvD,QAAQ,SAAS,yBAAyB,GAAG,SAAS,IACpD,QAAQ,SAAS,yBAAyB,GAAG,QAAQ,GAAG;KACzD,CAAC,IAAI,MAAM,WAAW,IAAI,WAAW,CAAC,CAAC,CAAC,SAAS;QAAE,UAAU,EAAE,KAAK,CAAA;KAAE,GAAG,KAAK,GAAG,CAAC,GAAG,wBAAwB,GAAG;QAC/G,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC;KACnB;CACF,CAAC;AAEF,KAAK,eAAe,CAAC,WAAW,IAAI,WAAW,SAAS;IACtD,SAAS,EAAE,SAAS,CAAC,MAAM,SAAS,SAAS,oBAAoB,CAAC,EAAE,CAAC;CACtE,GAAG,SAAS,GAAG,oBAAoB,CAAC;AAErC,KAAK,cAAc,CAAC,QAAQ,SAAS,yBAAyB,IAAI;KAC/D,CAAC,IAAI,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,SAAS,GAAG,MAAM,IAAI,MAAM,EAAE,GAC3D,KAAK,GACL,YAAY,CAAC,CAAC,EAAE,OAAO,EAAE,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;CAC3D,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC;AAE3B,KAAK,qBAAqB,CAAC,cAAc,SAAS,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,IAAI;KACzF,CAAC,IAAI,MAAM,cAAc,GAAG,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,SAAS,sBAAsB,CAAC,MAAM,OAAO,EAAE,MAAM,KAAK,CAAC,GAC9G,KAAK,SAAS,SAAS,GAAG,KAAK,GAC7B,OAAO,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GACjD;SACC,CAAC,IAAI,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,GACnE,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;YAAE,GAAG,EAAE,MAAM,CAAA;SAAE,GAAG,KAAK,GACpD,CAAC,SAAS,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GACtC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAC1E,KAAK;KACd,CAAC,MAAM,OAAO,CAAC,SAAS,CAAC,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,GAChE,KAAK,GACT,KAAK;CACV,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,CAAC;AAEjC,sFAAsF;AACtF,MAAM,MAAM,gBAAgB,CAAC,QAAQ,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IAC7E,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,GACnC,qBAAqB,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,CAAC;AAErD,wFAAwF;AACxF,MAAM,MAAM,kBAAkB,CAAC,QAAQ,SAAS,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,IACjF,gBAAgB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1 +1 @@
1
- {"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAiB9E,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAmKlB"}
1
+ {"version":3,"file":"dataset-query-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-query-validation.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAiB9E,wBAAgB,yBAAyB,CACvC,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,CAAC,EAAE,gBAAgB,GACzB,gBAAgB,CAuKlB"}
@@ -1,3 +1,5 @@
1
+ import { resolveRelationshipMeasure } from './relationship-measures.js';
2
+ import { semanticQuerySyntaxErrors } from './semantic-query-syntax.js';
1
3
  import { queryTimezoneErrors } from './query-timezone.js';
2
4
  import { baseMeasureNames } from './dataset-measures.js';
3
5
  import { validateFilterValue } from '../validation.js';
@@ -8,11 +10,16 @@ import { validateQualifiedFilter, validateRelationshipTenantRuntime, } from './r
8
10
  import { segmentSelectionErrors } from './segments.js';
9
11
  import { analyzeTimeMeasureAxis } from './time-measure-axis.js';
10
12
  export function validateDatasetQueryInput(ds, query, context) {
11
- const errors = queryTimezoneErrors(query.timezone);
13
+ const errors = [...semanticQuerySyntaxErrors(query), ...queryTimezoneErrors(query.timezone)];
12
14
  const dimensionNames = Object.keys(ds.dimensions);
13
15
  const measureNames = Object.keys(ds.measures);
14
16
  const selectedDimensions = query.dimensions ?? [];
15
17
  const selectedMeasures = query.measures ?? baseMeasureNames(ds.measures);
18
+ for (const name of selectedDimensions) {
19
+ if (selectedMeasures.includes(name)) {
20
+ errors.push(`Output "${name}" cannot be selected as both a dimension and a measure. Select one or give the definitions distinct names.`);
21
+ }
22
+ }
16
23
  const filterNames = Object.keys(ds.filters);
17
24
  const orderableFields = new Set([
18
25
  ...selectedDimensions,
@@ -47,7 +54,12 @@ export function validateDatasetQueryInput(ds, query, context) {
47
54
  if (query.measures) {
48
55
  for (const measure of query.measures) {
49
56
  if (isQualifiedField(measure)) {
50
- errors.push(`Measure "${measure}" is relationship-qualified. Measures can only be defined on the base dataset "${ds.name}", not traversed through relationships.`);
57
+ try {
58
+ resolveRelationshipMeasure(ds, measure);
59
+ }
60
+ catch (error) {
61
+ errors.push(error.message);
62
+ }
51
63
  continue;
52
64
  }
53
65
  if (!measureNames.includes(measure)) {
@@ -95,7 +107,7 @@ export function validateDatasetQueryInput(ds, query, context) {
95
107
  if (query.orderBy) {
96
108
  const invalid = [];
97
109
  for (const order of query.orderBy) {
98
- if (isQualifiedField(order.field)) {
110
+ if (isQualifiedField(order.field) && !selectedMeasures.includes(order.field)) {
99
111
  const resolution = resolveQualifiedField(ds, order.field);
100
112
  if (resolution?.error) {
101
113
  errors.push(resolution.error);
@@ -0,0 +1,5 @@
1
+ import type { DatasetSqlDialect } from '../dataset-sql-dialect.js';
2
+ import type { QueryBuilderFactoryInput } from '../query-builder-protocol.js';
3
+ /** Resolve from the active factory, so a runtime builder override carries its dialect. */
4
+ export declare function resolveDatasetSqlDialect(factory: QueryBuilderFactoryInput): DatasetSqlDialect;
5
+ //# sourceMappingURL=dataset-sql-dialect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-sql-dialect.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-sql-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AAG7E,0FAA0F;AAC1F,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAE7F"}
@@ -0,0 +1,5 @@
1
+ import { clickhouseDatasetSqlDialect } from './clickhouse-dataset-sql-dialect.js';
2
+ /** Resolve from the active factory, so a runtime builder override carries its dialect. */
3
+ export function resolveDatasetSqlDialect(factory) {
4
+ return factory.datasetSqlDialect ?? clickhouseDatasetSqlDialect;
5
+ }
@@ -0,0 +1,3 @@
1
+ import type { AnyDatasetInstance } from '../types.js';
2
+ export declare function isDatasetInstance(target: unknown): target is AnyDatasetInstance;
3
+ //# sourceMappingURL=dataset-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-target.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-target.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,IAAI,kBAAkB,CAE/E"}
@@ -0,0 +1,3 @@
1
+ export function isDatasetInstance(target) {
2
+ return !!target && typeof target === 'object' && '__type' in target && target.__type === 'dataset';
3
+ }
@@ -1,3 +1,4 @@
1
1
  import type { DerivedMeasureDefinition } from '../types.js';
2
+ export declare function derivedExpressionSql(definition: DerivedMeasureDefinition, resolve?: (name: string) => string): string;
2
3
  export declare function derivedProjection(name: string, definition: DerivedMeasureDefinition): string;
3
4
  //# sourceMappingURL=derived-measure-sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"derived-measure-sql.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAyC5D,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,wBAAwB,GAAG,MAAM,CAG5F"}
1
+ {"version":3,"file":"derived-measure-sql.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAyC5D,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,wBAAwB,EAAE,OAAO,GAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAA2B,GAAG,MAAM,CAGzI;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,wBAAwB,GAAG,MAAM,CAE5F"}
@@ -1,12 +1,12 @@
1
1
  import { quoteSQLIdentifier } from '../sql-utils.js';
2
- function expressionSql(expression, uses) {
2
+ function expressionSql(expression, uses, resolve) {
3
3
  switch (expression.kind) {
4
4
  case 'ref': {
5
5
  if (!Object.hasOwn(uses, expression.name)) {
6
6
  throw new Error(`Formula references undeclared input "${expression.name}".`);
7
7
  }
8
8
  const measureName = uses[expression.name];
9
- return quoteSQLIdentifier(measureName);
9
+ return resolve(measureName);
10
10
  }
11
11
  case 'literal': {
12
12
  if (expression.value === null)
@@ -25,10 +25,10 @@ function expressionSql(expression, uses) {
25
25
  const operator = operators[expression.operator];
26
26
  if (!operator)
27
27
  throw new Error('Formula contains an unsupported binary operator.');
28
- return `(${expressionSql(expression.left, uses)} ${operator} ${expressionSql(expression.right, uses)})`;
28
+ return `(${expressionSql(expression.left, uses, resolve)} ${operator} ${expressionSql(expression.right, uses, resolve)})`;
29
29
  }
30
30
  case 'function': {
31
- const args = expression.args.map(arg => expressionSql(arg, uses));
31
+ const args = expression.args.map(arg => expressionSql(arg, uses, resolve));
32
32
  switch (expression.name) {
33
33
  case 'nullIfZero': return `NULLIF(${args[0]}, 0)`;
34
34
  case 'coalesce': return `COALESCE(${args.join(', ')})`;
@@ -40,7 +40,10 @@ function expressionSql(expression, uses) {
40
40
  }
41
41
  }
42
42
  }
43
- export function derivedProjection(name, definition) {
43
+ export function derivedExpressionSql(definition, resolve = quoteSQLIdentifier) {
44
44
  const aliases = Object.fromEntries(Object.keys(definition.uses).map(alias => [alias, alias]));
45
- return `${expressionSql(definition.formula(aliases).expression, definition.uses)} AS ${quoteSQLIdentifier(name)}`;
45
+ return expressionSql(definition.formula(aliases).expression, definition.uses, resolve);
46
+ }
47
+ export function derivedProjection(name, definition) {
48
+ return `${derivedExpressionSql(definition)} AS ${quoteSQLIdentifier(name)}`;
46
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"derived-measure-validation.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-validation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,aAAa,CAAC;AAmHrB,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GACjD,IAAI,CAmDN"}
1
+ {"version":3,"file":"derived-measure-validation.d.ts","sourceRoot":"","sources":["../../src/utils/derived-measure-validation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,wBAAwB,EAGzB,MAAM,aAAa,CAAC;AA8FrB,wBAAgB,uBAAuB,CACrC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,GACjD,IAAI,CAgDN"}
@@ -1,3 +1,4 @@
1
+ import { assertMeasureDependenciesAcyclic } from './measure-dependencies.js';
1
2
  import { isSafeSQLIdentifier } from '../sql-utils.js';
2
3
  import { splitDatasetMeasures } from './dataset-measures.js';
3
4
  function fail(datasetName, measureName, detail) {
@@ -71,27 +72,8 @@ function evaluateFormula(datasetName, measureName, definition) {
71
72
  }
72
73
  return formula;
73
74
  }
74
- function assertAcyclic(datasetName, derivedMeasures) {
75
- const visiting = new Set();
76
- const visited = new Set();
77
- const visit = (name) => {
78
- if (visiting.has(name))
79
- fail(datasetName, name, 'contains a dependency cycle.');
80
- if (visited.has(name))
81
- return;
82
- if (!Object.hasOwn(derivedMeasures, name))
83
- return;
84
- const definition = derivedMeasures[name];
85
- visiting.add(name);
86
- Object.values(definition.uses).forEach(dependency => visit(dependency));
87
- visiting.delete(name);
88
- visited.add(name);
89
- };
90
- Object.keys(derivedMeasures).forEach(visit);
91
- }
92
75
  export function validateDerivedMeasures(datasetName, measures) {
93
- const { base: baseMeasures, derived: derivedMeasures, windows, shifts } = splitDatasetMeasures(measures);
94
- const timeMeasureNames = new Set([...Object.keys(windows), ...Object.keys(shifts)]);
76
+ const { derived: derivedMeasures } = splitDatasetMeasures(measures);
95
77
  for (const [measureName, definition] of Object.entries(derivedMeasures)) {
96
78
  if (!isSafeSQLIdentifier(measureName)) {
97
79
  fail(datasetName, measureName, 'name is not a safe identifier.');
@@ -103,7 +85,7 @@ export function validateDerivedMeasures(datasetName, measures) {
103
85
  fail(datasetName, measureName, 'must declare an input map.');
104
86
  }
105
87
  }
106
- assertAcyclic(datasetName, derivedMeasures);
88
+ assertMeasureDependenciesAcyclic(datasetName, measures);
107
89
  for (const [measureName, definition] of Object.entries(derivedMeasures)) {
108
90
  const uses = Object.entries(definition.uses);
109
91
  if (uses.length === 0)
@@ -116,10 +98,7 @@ export function validateDerivedMeasures(datasetName, measures) {
116
98
  if (typeof dependencyName !== 'string' || dependencyName.includes('.')) {
117
99
  fail(datasetName, measureName, `references cross-dataset measure "${String(dependencyName)}".`);
118
100
  }
119
- if (Object.hasOwn(derivedMeasures, dependencyName)) {
120
- fail(datasetName, measureName, `references derived measure "${dependencyName}"; v1 inputs must be base measures.`);
121
- }
122
- if (!Object.hasOwn(baseMeasures, dependencyName) && !timeMeasureNames.has(dependencyName)) {
101
+ if (!Object.hasOwn(measures, dependencyName)) {
123
102
  fail(datasetName, measureName, `references missing measure "${dependencyName}".`);
124
103
  }
125
104
  }
@@ -0,0 +1,3 @@
1
+ import type { MetricFilter, MetricFilterOperator } from '../types.js';
2
+ export declare function createFilter<const TField extends string, const TValue, TOperator extends MetricFilterOperator>(field: TField, operator: TOperator, value: TValue): MetricFilter<TField, TValue, TOperator>;
3
+ //# sourceMappingURL=filter-definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"filter-definition.d.ts","sourceRoot":"","sources":["../../src/utils/filter-definition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAEtE,wBAAgB,YAAY,CAC1B,KAAK,CAAC,MAAM,SAAS,MAAM,EAC3B,KAAK,CAAC,MAAM,EACZ,SAAS,SAAS,oBAAoB,EACtC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,CAE5F"}
@@ -0,0 +1,3 @@
1
+ export function createFilter(field, operator, value) {
2
+ return { field, operator, value };
3
+ }
@@ -0,0 +1,8 @@
1
+ import type { DatasetMeasureDefinition, MeasureDefinition } from '../types.js';
2
+ export declare function measureDependencies(definition: DatasetMeasureDefinition): string[];
3
+ /** Dependency order, with each shared input visited once. */
4
+ export declare function measureDependencyNames(measures: Readonly<Record<string, DatasetMeasureDefinition>>, selected: readonly string[]): string[];
5
+ export declare function assertMeasureDependenciesAcyclic(dataset: string, measures: Readonly<Record<string, DatasetMeasureDefinition>>): void;
6
+ /** Wrappers inherit aggregation metadata only when their input has one. */
7
+ export declare function inheritedBaseMeasure(measures: Readonly<Record<string, DatasetMeasureDefinition>>, name: string): MeasureDefinition | undefined;
8
+ //# sourceMappingURL=measure-dependencies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure-dependencies.d.ts","sourceRoot":"","sources":["../../src/utils/measure-dependencies.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAG/E,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,wBAAwB,GAAG,MAAM,EAAE,CAIlF;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAe1I;AAED,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,GAAG,IAAI,CAGpI;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAK9I"}
@@ -0,0 +1,44 @@
1
+ import { isBaseMeasure, isDerivedMeasure, isShiftMeasure, isWindowMeasure } from './dataset-measures.js';
2
+ export function measureDependencies(definition) {
3
+ if (isDerivedMeasure(definition))
4
+ return Object.values(definition.uses);
5
+ if (isShiftMeasure(definition) || isWindowMeasure(definition))
6
+ return [definition.measure];
7
+ return [];
8
+ }
9
+ /** Dependency order, with each shared input visited once. */
10
+ export function measureDependencyNames(measures, selected) {
11
+ const visited = new Set();
12
+ const visiting = new Set();
13
+ const names = [];
14
+ const visit = (name) => {
15
+ if (visiting.has(name))
16
+ throw new Error(`Measure "${name}" contains a dependency cycle.`);
17
+ if (visited.has(name) || !Object.hasOwn(measures, name))
18
+ return;
19
+ visiting.add(name);
20
+ measureDependencies(measures[name]).forEach(visit);
21
+ visiting.delete(name);
22
+ visited.add(name);
23
+ names.push(name);
24
+ };
25
+ selected.forEach(visit);
26
+ return names;
27
+ }
28
+ export function assertMeasureDependenciesAcyclic(dataset, measures) {
29
+ try {
30
+ measureDependencyNames(measures, Object.keys(measures));
31
+ }
32
+ catch (error) {
33
+ throw new Error(`Invalid dataset "${dataset}": ${String(error)}`);
34
+ }
35
+ }
36
+ /** Wrappers inherit aggregation metadata only when their input has one. */
37
+ export function inheritedBaseMeasure(measures, name) {
38
+ const definition = measures[name];
39
+ if (isBaseMeasure(definition))
40
+ return definition;
41
+ if (isWindowMeasure(definition) || isShiftMeasure(definition))
42
+ return inheritedBaseMeasure(measures, definition.measure);
43
+ return undefined;
44
+ }
@@ -0,0 +1,23 @@
1
+ import type { DatasetMeasureDefinition, DerivedMeasureDefinition, MeasureTimeInterval } from '../types.js';
2
+ export interface MeasureEvaluationContext {
3
+ id: number;
4
+ parent?: number;
5
+ interval?: MeasureTimeInterval;
6
+ }
7
+ export interface MeasureEvaluationNode {
8
+ id: number;
9
+ name: string;
10
+ context: number;
11
+ definition: DatasetMeasureDefinition;
12
+ inputs?: Record<string, number>;
13
+ }
14
+ /** A shift changes evaluation context; formulas combine values in that context. */
15
+ export declare function buildMeasureEvaluationGraph(measures: Readonly<Record<string, DatasetMeasureDefinition>>, selected: readonly string[]): {
16
+ contexts: MeasureEvaluationContext[];
17
+ nodes: MeasureEvaluationNode[];
18
+ outputs: {
19
+ [k: string]: number;
20
+ };
21
+ };
22
+ export declare function evaluationFormula(node: MeasureEvaluationNode): DerivedMeasureDefinition;
23
+ //# sourceMappingURL=measure-evaluation-graph.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measure-evaluation-graph.d.ts","sourceRoot":"","sources":["../../src/utils/measure-evaluation-graph.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAG3G,MAAM,WAAW,wBAAwB;IAAG,EAAE,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,mBAAmB,CAAA;CAAE;AACzG,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,wBAAwB,CAAC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,mFAAmF;AACnF,wBAAgB,2BAA2B,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE;;;;;;EA+BpI;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,qBAAqB,GAAG,wBAAwB,CAIvF"}