@hypequery/datasets 0.23.1 → 0.25.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.
- package/README.md +14 -0
- package/dist/aggregations.d.ts +3 -0
- package/dist/aggregations.d.ts.map +1 -1
- package/dist/aggregations.js +6 -0
- package/dist/cache/definition-identity.d.ts +9 -6
- package/dist/cache/definition-identity.d.ts.map +1 -1
- package/dist/cache/definition-identity.js +14 -8
- package/dist/cache/protocol-cache-keys.d.ts.map +1 -1
- package/dist/cache/protocol-cache-keys.js +29 -6
- package/dist/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +12 -1
- package/dist/catalog.d.ts +7 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +5 -2
- package/dist/client-contract/query-schema.d.ts +19 -0
- package/dist/client-contract/query-schema.d.ts.map +1 -0
- package/dist/client-contract/query-schema.js +85 -0
- package/dist/constants.d.ts +5 -4
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +7 -6
- package/dist/contract.d.ts +5 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +6 -3
- package/dist/dataset-compilation.d.ts +2 -0
- package/dist/dataset-compilation.d.ts.map +1 -1
- package/dist/dataset-sql-dialect.d.ts +119 -0
- package/dist/dataset-sql-dialect.d.ts.map +1 -1
- package/dist/dataset.d.ts +4 -2
- package/dist/dataset.d.ts.map +1 -1
- package/dist/dataset.js +23 -31
- package/dist/in-memory-backend.d.ts.map +1 -1
- package/dist/in-memory-backend.js +2 -0
- package/dist/index.d.ts +49 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -6
- package/dist/internal.d.ts +5 -1
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +5 -1
- package/dist/measure.d.ts +9 -1
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +9 -1
- package/dist/metric-query-engine.d.ts +0 -1
- package/dist/metric-query-engine.d.ts.map +1 -1
- package/dist/metric-query-engine.js +11 -3
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +2 -3
- package/dist/protocol-deployment-adapter.d.ts.map +1 -1
- package/dist/protocol-deployment-adapter.js +7 -1
- package/dist/protocol-rehydrate.d.ts.map +1 -1
- package/dist/protocol-rehydrate.js +1 -0
- package/dist/protocol-schema-adapter.d.ts +7 -7
- package/dist/protocol-schema-adapter.d.ts.map +1 -1
- package/dist/protocol-schema-adapter.js +12 -245
- package/dist/query-builder-protocol.d.ts +34 -1
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/query-planner.d.ts +14 -5
- package/dist/query-planner.d.ts.map +1 -1
- package/dist/query-planner.js +53 -34
- package/dist/relationship-check.d.ts.map +1 -1
- package/dist/relationship-check.js +8 -8
- package/dist/schemas.d.ts +17 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +16 -0
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +49 -13
- package/dist/semantic-query-schema.d.ts +8 -1
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +18 -12
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +4 -3
- package/dist/types.d.ts +105 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/aggregate-state-functions.d.ts +24 -0
- package/dist/utils/aggregate-state-functions.d.ts.map +1 -0
- package/dist/utils/aggregate-state-functions.js +60 -0
- package/dist/utils/approximate-measures.d.ts +3 -1
- package/dist/utils/approximate-measures.d.ts.map +1 -1
- package/dist/utils/approximate-measures.js +8 -2
- package/dist/utils/base-measure-definition.d.ts +16 -0
- package/dist/utils/base-measure-definition.d.ts.map +1 -1
- package/dist/utils/base-measure-definition.js +31 -0
- package/dist/utils/build-dataset-query-builder.d.ts +1 -1
- package/dist/utils/build-dataset-query-builder.d.ts.map +1 -1
- package/dist/utils/build-dataset-query-builder.js +26 -21
- package/dist/utils/by-name.d.ts +8 -0
- package/dist/utils/by-name.d.ts.map +1 -0
- package/dist/utils/by-name.js +44 -0
- package/dist/utils/calendar-shift-sql.d.ts +5 -4
- package/dist/utils/calendar-shift-sql.d.ts.map +1 -1
- package/dist/utils/calendar-shift-sql.js +8 -8
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +2 -2
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -1
- package/dist/utils/clickhouse-dataset-sql-dialect.js +143 -1
- package/dist/utils/compile-dataset-query.d.ts +1 -1
- package/dist/utils/compile-dataset-query.d.ts.map +1 -1
- package/dist/utils/compile-dataset-query.js +22 -8
- package/dist/utils/composite-time-measure-sql.d.ts +3 -5
- package/dist/utils/composite-time-measure-sql.d.ts.map +1 -1
- package/dist/utils/composite-time-measure-sql.js +15 -16
- package/dist/utils/count-aggregations.d.ts +4 -0
- package/dist/utils/count-aggregations.d.ts.map +1 -0
- package/dist/utils/count-aggregations.js +6 -0
- package/dist/utils/dataset-definition-validation.d.ts +1 -1
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
- package/dist/utils/dataset-definition-validation.js +55 -26
- package/dist/utils/dataset-derived-query.d.ts +2 -4
- package/dist/utils/dataset-derived-query.d.ts.map +1 -1
- package/dist/utils/dataset-derived-query.js +11 -14
- package/dist/utils/dataset-having.d.ts +5 -7
- package/dist/utils/dataset-having.d.ts.map +1 -1
- package/dist/utils/dataset-having.js +9 -12
- package/dist/utils/dataset-normalization.d.ts.map +1 -1
- package/dist/utils/dataset-normalization.js +1 -0
- package/dist/utils/dataset-query-validation.d.ts +1 -1
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +19 -2
- package/dist/utils/dataset-source.d.ts +42 -0
- package/dist/utils/dataset-source.d.ts.map +1 -0
- package/dist/utils/dataset-source.js +59 -0
- package/dist/utils/dataset-sql-dialect.d.ts +13 -2
- package/dist/utils/dataset-sql-dialect.d.ts.map +1 -1
- package/dist/utils/dataset-sql-dialect.js +74 -1
- package/dist/utils/dataset-time-keys.d.ts +32 -0
- package/dist/utils/dataset-time-keys.d.ts.map +1 -0
- package/dist/utils/dataset-time-keys.js +55 -0
- package/dist/utils/dataset-validation.d.ts.map +1 -1
- package/dist/utils/dataset-validation.js +7 -0
- package/dist/utils/deployable-formulas.d.ts +33 -0
- package/dist/utils/deployable-formulas.d.ts.map +1 -0
- package/dist/utils/deployable-formulas.js +102 -0
- package/dist/utils/derived-cte-validation.d.ts +5 -0
- package/dist/utils/derived-cte-validation.d.ts.map +1 -1
- package/dist/utils/derived-cte-validation.js +77 -8
- package/dist/utils/derived-measure-sql.d.ts +10 -2
- package/dist/utils/derived-measure-sql.d.ts.map +1 -1
- package/dist/utils/derived-measure-sql.js +23 -43
- package/dist/utils/enforced-constraints.d.ts +14 -0
- package/dist/utils/enforced-constraints.d.ts.map +1 -0
- package/dist/utils/enforced-constraints.js +57 -0
- package/dist/utils/filter-field-type.d.ts +14 -0
- package/dist/utils/filter-field-type.d.ts.map +1 -0
- package/dist/utils/filter-field-type.js +21 -0
- package/dist/utils/filtered-aggregation-sql.d.ts +2 -1
- package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
- package/dist/utils/filtered-aggregation-sql.js +18 -35
- package/dist/utils/groupable.d.ts +7 -0
- package/dist/utils/groupable.d.ts.map +1 -0
- package/dist/utils/groupable.js +8 -0
- package/dist/utils/metric-derived-query.d.ts +2 -4
- package/dist/utils/metric-derived-query.d.ts.map +1 -1
- package/dist/utils/metric-derived-query.js +58 -51
- package/dist/utils/metric-query-builder.d.ts +1 -1
- package/dist/utils/metric-query-builder.d.ts.map +1 -1
- package/dist/utils/metric-query-builder.js +22 -20
- package/dist/utils/metric-query-validation.d.ts.map +1 -1
- package/dist/utils/metric-query-validation.js +14 -1
- package/dist/utils/portable-aggregations.d.ts +1 -0
- package/dist/utils/portable-aggregations.d.ts.map +1 -1
- package/dist/utils/portable-aggregations.js +5 -0
- package/dist/utils/protocol-schema-conversion.d.ts +12 -0
- package/dist/utils/protocol-schema-conversion.d.ts.map +1 -0
- package/dist/utils/protocol-schema-conversion.js +61 -0
- package/dist/utils/query-field-references.d.ts +10 -0
- package/dist/utils/query-field-references.d.ts.map +1 -0
- package/dist/utils/query-field-references.js +38 -0
- package/dist/utils/query-timezone.d.ts +41 -3
- package/dist/utils/query-timezone.d.ts.map +1 -1
- package/dist/utils/query-timezone.js +76 -9
- package/dist/utils/raw-sql-dialect-check.d.ts +12 -0
- package/dist/utils/raw-sql-dialect-check.d.ts.map +1 -0
- package/dist/utils/raw-sql-dialect-check.js +37 -0
- package/dist/utils/relationship-builder-plan.d.ts +37 -18
- package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
- package/dist/utils/relationship-builder-plan.js +43 -28
- package/dist/utils/relationship-measure-sql.d.ts +14 -4
- package/dist/utils/relationship-measure-sql.d.ts.map +1 -1
- package/dist/utils/relationship-measure-sql.js +36 -10
- package/dist/utils/relationship-measures.d.ts.map +1 -1
- package/dist/utils/relationship-measures.js +3 -0
- package/dist/utils/relationship-validation.d.ts.map +1 -1
- package/dist/utils/relationship-validation.js +2 -0
- package/dist/utils/row-scope.d.ts +23 -0
- package/dist/utils/row-scope.d.ts.map +1 -0
- package/dist/utils/row-scope.js +26 -0
- package/dist/utils/scoped-dataset-source.d.ts +23 -0
- package/dist/utils/scoped-dataset-source.d.ts.map +1 -0
- package/dist/utils/scoped-dataset-source.js +34 -0
- package/dist/utils/semantic-tool.d.ts +2 -2
- package/dist/utils/semantic-tool.d.ts.map +1 -1
- package/dist/utils/semantic-tool.js +1 -1
- package/dist/utils/shift-measure-sql.d.ts +5 -4
- package/dist/utils/shift-measure-sql.d.ts.map +1 -1
- package/dist/utils/shift-measure-sql.js +25 -26
- package/dist/utils/shift-wall-time-sql.d.ts +2 -1
- package/dist/utils/shift-wall-time-sql.d.ts.map +1 -1
- package/dist/utils/shift-wall-time-sql.js +7 -7
- package/dist/utils/snapshot-execution-context.d.ts.map +1 -1
- package/dist/utils/snapshot-execution-context.js +3 -0
- package/dist/utils/statement-parameters.d.ts +58 -0
- package/dist/utils/statement-parameters.d.ts.map +1 -0
- package/dist/utils/statement-parameters.js +101 -0
- package/dist/utils/time-axis-calendar-guard.d.ts +2 -1
- package/dist/utils/time-axis-calendar-guard.d.ts.map +1 -1
- package/dist/utils/time-axis-calendar-guard.js +5 -7
- package/dist/utils/time-measure-axis-sql.d.ts +3 -2
- package/dist/utils/time-measure-axis-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-axis-sql.js +20 -22
- package/dist/utils/time-measure-context-sql.d.ts +3 -2
- package/dist/utils/time-measure-context-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-context-sql.js +11 -11
- package/dist/utils/time-measure-dataset-sql.d.ts +7 -5
- package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-dataset-sql.js +17 -12
- package/dist/utils/time-measure-leaf-sql.d.ts +3 -2
- package/dist/utils/time-measure-leaf-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-leaf-sql.js +19 -18
- package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-result-sql.js +10 -11
- package/dist/utils/time-measure-source-sql.d.ts +5 -2
- package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-source-sql.js +33 -23
- package/dist/utils/time-measure-values-sql.d.ts +4 -3
- package/dist/utils/time-measure-values-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-values-sql.js +26 -29
- package/dist/utils/timestamp-instants.d.ts +17 -0
- package/dist/utils/timestamp-instants.d.ts.map +1 -0
- package/dist/utils/timestamp-instants.js +34 -0
- package/dist/utils/timestamp-text.d.ts +12 -0
- package/dist/utils/timestamp-text.d.ts.map +1 -0
- package/dist/utils/timestamp-text.js +18 -0
- package/dist/utils/zod-json-schema.d.ts +8 -0
- package/dist/utils/zod-json-schema.d.ts.map +1 -0
- package/dist/utils/zod-json-schema.js +18 -0
- package/dist/utils/zod-schema-version.d.ts +19 -0
- package/dist/utils/zod-schema-version.d.ts.map +1 -0
- package/dist/utils/zod-schema-version.js +6 -0
- package/dist/utils/zod-v3-protocol-schema.d.ts +10 -0
- package/dist/utils/zod-v3-protocol-schema.d.ts.map +1 -0
- package/dist/utils/zod-v3-protocol-schema.js +164 -0
- package/dist/utils/zod-v4-protocol-schema.d.ts +12 -0
- package/dist/utils/zod-v4-protocol-schema.d.ts.map +1 -0
- package/dist/utils/zod-v4-protocol-schema.js +171 -0
- package/dist/validation.d.ts +7 -0
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +42 -8
- package/package.json +21 -12
- package/dist/api.type-test.d.ts +0 -2
- package/dist/api.type-test.d.ts.map +0 -1
- package/dist/api.type-test.js +0 -251
- package/dist/utils/time-arithmetic-sql.d.ts +0 -4
- package/dist/utils/time-arithmetic-sql.d.ts.map +0 -1
- package/dist/utils/time-arithmetic-sql.js +0 -9
- package/dist/utils/window-aggregation-sql.d.ts +0 -5
- package/dist/utils/window-aggregation-sql.d.ts.map +0 -1
- package/dist/utils/window-aggregation-sql.js +0 -17
|
@@ -1,34 +1,39 @@
|
|
|
1
|
+
import { scopedDatasetSource } from './scoped-dataset-source.js';
|
|
1
2
|
import { relationshipMeasureExpressions } from './relationship-measure-sql.js';
|
|
2
3
|
import { measureToAggregationSpec } from './dataset-normalization.js';
|
|
3
4
|
import { applyAggregationSpec } from '../query-planner.js';
|
|
4
5
|
import { resolveDatasetSqlDialect } from './dataset-sql-dialect.js';
|
|
5
|
-
import {
|
|
6
|
+
import { assertRawSqlForDialect } from './raw-sql-dialect-check.js';
|
|
7
|
+
import { queryTimeFilter } from './query-timezone.js';
|
|
6
8
|
import { selectedTimeMeasures } from './time-query-measures.js';
|
|
7
9
|
import { baseMeasureNames, getBaseMeasure } from './dataset-measures.js';
|
|
8
|
-
import { appendOrderLimitOffset, applyMeasureDefinition, buildDimensionSelectionPlan, resolveDimensionExpression, resolveFilterField,
|
|
10
|
+
import { appendOrderLimitOffset, applyMeasureDefinition, buildDimensionSelectionPlan, resolveDimensionExpression, resolveFilterField, } from '../query-planner.js';
|
|
9
11
|
import { validateDatasetQueryInput } from './dataset-query-validation.js';
|
|
10
|
-
import {
|
|
11
|
-
import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn, } from './relationship-builder-plan.js';
|
|
12
|
+
import { buildRelationshipBuilderContext } from './relationship-builder-plan.js';
|
|
12
13
|
import { hasSelectedDerivedMeasure, } from './dataset-derived-query.js';
|
|
13
14
|
import { segmentFilters } from './segments.js';
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
import { datasetOnTimeKey } from './dataset-time-keys.js';
|
|
16
|
+
export function buildDatasetQueryBuilder(dataset, query, options) {
|
|
17
|
+
if (selectedTimeMeasures(dataset, query).size) {
|
|
16
18
|
throw new Error('A time-based dataset query needs the time-measure SQL planner; use createDatasetClient().toSQL().');
|
|
17
19
|
}
|
|
18
|
-
if (hasSelectedDerivedMeasure(
|
|
20
|
+
if (hasSelectedDerivedMeasure(dataset, query)) {
|
|
19
21
|
throw new Error('A derived dataset query needs the outer SQL projection; use createDatasetClient().toSQL().');
|
|
20
22
|
}
|
|
21
23
|
if (query.having?.length) {
|
|
22
24
|
throw new Error('A dataset query with having needs the outer SQL projection; use createDatasetClient().toSQL().');
|
|
23
25
|
}
|
|
24
|
-
const validation = validateDatasetQueryInput(
|
|
26
|
+
const validation = validateDatasetQueryInput(dataset, query, options.context);
|
|
25
27
|
if (!validation.valid) {
|
|
26
28
|
throw new Error(`Invalid dataset query: ${validation.errors.join('; ')}`);
|
|
27
29
|
}
|
|
30
|
+
const ds = datasetOnTimeKey(dataset, query.timeKey);
|
|
28
31
|
const dialect = resolveDatasetSqlDialect(options.builderFactory);
|
|
32
|
+
assertRawSqlForDialect(ds, dialect);
|
|
29
33
|
const joinCtx = buildRelationshipBuilderContext(ds, query, options.context, dialect);
|
|
30
|
-
let qb =
|
|
31
|
-
|
|
34
|
+
let qb = scopedDatasetSource({
|
|
35
|
+
builderFactory: options.builderFactory, ds, context: options.context, joinCtx, dialect, timezone: query.timezone,
|
|
36
|
+
});
|
|
32
37
|
const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], query.by, joinCtx, query.timezone, dialect);
|
|
33
38
|
const measureNames = query.measures ?? baseMeasureNames(ds.measures);
|
|
34
39
|
if (selectParts.length > 0) {
|
|
@@ -39,27 +44,27 @@ export function buildDatasetQueryBuilder(ds, query, options) {
|
|
|
39
44
|
qb = qb.groupBy(groupByParts);
|
|
40
45
|
for (const measureName of measureNames) {
|
|
41
46
|
if (measureName.includes('.')) {
|
|
42
|
-
const expressions = relationshipMeasureExpressions(ds, measureName, joinCtx);
|
|
43
|
-
qb = applyAggregationSpec(qb, ds, measureToAggregationSpec(measureName, expressions.definition),
|
|
47
|
+
const expressions = relationshipMeasureExpressions(ds, measureName, joinCtx, dialect);
|
|
48
|
+
qb = applyAggregationSpec(qb, ds, measureToAggregationSpec(measureName, expressions.definition), measureName, joinCtx, dialect, expressions);
|
|
44
49
|
continue;
|
|
45
50
|
}
|
|
46
51
|
const definition = getBaseMeasure(ds.measures, measureName);
|
|
47
52
|
if (!definition)
|
|
48
53
|
throw new Error(`Measure "${measureName}" is not a base measure.`);
|
|
49
|
-
qb = applyMeasureDefinition(qb, ds, measureName, definition, joinCtx);
|
|
50
|
-
}
|
|
51
|
-
const tenantColumn = resolveTenantFilterColumn(ds, options.context);
|
|
52
|
-
const tenantPredicate = getRuntimeTenantPredicate(options.context);
|
|
53
|
-
if (tenantPredicate && tenantColumn) {
|
|
54
|
-
qb = qb.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
|
|
54
|
+
qb = applyMeasureDefinition(qb, ds, measureName, definition, joinCtx, dialect);
|
|
55
55
|
}
|
|
56
56
|
for (const filter of query.filters ?? []) {
|
|
57
57
|
const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
|
|
58
|
-
|
|
58
|
+
for (const predicate of queryTimeFilter(dialect, ds, filter, resolvedField, query.timezone)) {
|
|
59
|
+
qb = qb.where(predicate.expression, predicate.operator, predicate.value);
|
|
60
|
+
}
|
|
59
61
|
}
|
|
60
62
|
// Segments are author-defined, so they bypass the caller filter allow-list.
|
|
61
63
|
for (const filter of segmentFilters(ds, query.segments)) {
|
|
62
|
-
|
|
64
|
+
const predicates = queryTimeFilter(dialect, ds, filter, resolveDimensionExpression(ds, filter.field, joinCtx), query.timezone, { field: 'dimension' });
|
|
65
|
+
for (const predicate of predicates) {
|
|
66
|
+
qb = qb.where(predicate.expression, predicate.operator, predicate.value);
|
|
67
|
+
}
|
|
63
68
|
}
|
|
64
|
-
return appendOrderLimitOffset(qb, query.orderBy, options.skipDefaultOrderBy ? undefined : query.by, options.executionLimit ?? query.limit, query.offset, joinCtx, dialect);
|
|
69
|
+
return appendOrderLimitOffset(qb, query.orderBy, options.skipDefaultOrderBy ? undefined : query.by, options.executionLimit ?? query.limit, query.offset, joinCtx, dialect, { ds, dimensions: query.dimensions ?? [] });
|
|
65
70
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The order protocol artifacts list named things in, so the same definitions
|
|
3
|
+
* produce the same bytes whatever order they were supplied in, on any host.
|
|
4
|
+
*/
|
|
5
|
+
export declare function byName<T extends {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
}>(left: T, right: T): number;
|
|
8
|
+
//# sourceMappingURL=by-name.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"by-name.d.ts","sourceRoot":"","sources":["../../src/utils/by-name.ts"],"names":[],"mappings":"AAkCA;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAIrF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The weights a name sorts by: underscore, then digits, then letters
|
|
3
|
+
* compared without case; ties then go lowercase first. This is what
|
|
4
|
+
* `localeCompare` gives RFC 0002 names (ASCII letters, digits, underscores)
|
|
5
|
+
* under the root collation, fixed so it cannot change with the host locale.
|
|
6
|
+
* Python's `portable_name_key` is the same collation.
|
|
7
|
+
*/
|
|
8
|
+
function nameWeights(value) {
|
|
9
|
+
const base = [];
|
|
10
|
+
const caseWeights = [];
|
|
11
|
+
for (const char of value) {
|
|
12
|
+
if (char === '_') {
|
|
13
|
+
base.push(0);
|
|
14
|
+
caseWeights.push(0);
|
|
15
|
+
}
|
|
16
|
+
else if (char >= '0' && char <= '9') {
|
|
17
|
+
base.push(char.charCodeAt(0) - 48 + 1);
|
|
18
|
+
caseWeights.push(0);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const lower = char.toLowerCase();
|
|
22
|
+
base.push(lower.charCodeAt(0) - 97 + 11);
|
|
23
|
+
caseWeights.push(char === lower ? 0 : 1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return { base, caseWeights };
|
|
27
|
+
}
|
|
28
|
+
function compareWeights(left, right) {
|
|
29
|
+
const length = Math.min(left.length, right.length);
|
|
30
|
+
for (let index = 0; index < length; index += 1) {
|
|
31
|
+
if (left[index] !== right[index])
|
|
32
|
+
return left[index] - right[index];
|
|
33
|
+
}
|
|
34
|
+
return left.length - right.length;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The order protocol artifacts list named things in, so the same definitions
|
|
38
|
+
* produce the same bytes whatever order they were supplied in, on any host.
|
|
39
|
+
*/
|
|
40
|
+
export function byName(left, right) {
|
|
41
|
+
const a = nameWeights(left.name);
|
|
42
|
+
const b = nameWeights(right.name);
|
|
43
|
+
return compareWeights(a.base, b.base) || compareWeights(a.caseWeights, b.caseWeights);
|
|
44
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DatasetSqlDialectRules } from '../dataset-sql-dialect.js';
|
|
1
2
|
import type { MeasureTimeInterval } from '../types.js';
|
|
2
3
|
/**
|
|
3
4
|
* Shift a point inside a bucket by a calendar interval, relative to the
|
|
@@ -10,9 +11,9 @@ import type { MeasureTimeInterval } from '../types.js';
|
|
|
10
11
|
* the 2024-01-26 week, so a partial bucket's range came out empty or
|
|
11
12
|
* overlapping the next week.
|
|
12
13
|
*
|
|
13
|
-
* The point keeps its own wall-clock time (
|
|
14
|
-
* preserve it) and moves by whole calendar days to the anchor's
|
|
15
|
-
* point's day offset within its bucket.
|
|
14
|
+
* The point keeps its own wall-clock time (calendar subtraction and day
|
|
15
|
+
* addition both preserve it) and moves by whole calendar days to the anchor's
|
|
16
|
+
* day plus the point's day offset within its bucket.
|
|
16
17
|
*/
|
|
17
|
-
export declare function anchoredCalendarShiftSql(point: string, anchor: string, interval: MeasureTimeInterval): string;
|
|
18
|
+
export declare function anchoredCalendarShiftSql(dialect: DatasetSqlDialectRules, point: string, anchor: string, interval: MeasureTimeInterval): string;
|
|
18
19
|
//# sourceMappingURL=calendar-shift-sql.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"calendar-shift-sql.d.ts","sourceRoot":"","sources":["../../src/utils/calendar-shift-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"calendar-shift-sql.d.ts","sourceRoot":"","sources":["../../src/utils/calendar-shift-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,sBAAsB,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,mBAAmB,GAAG,MAAM,CAK9I"}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { subtractTimeSql } from './time-arithmetic-sql.js';
|
|
2
1
|
/**
|
|
3
2
|
* Shift a point inside a bucket by a calendar interval, relative to the
|
|
4
3
|
* bucket's shifted anchor rather than on its own.
|
|
@@ -10,12 +9,13 @@ import { subtractTimeSql } from './time-arithmetic-sql.js';
|
|
|
10
9
|
* the 2024-01-26 week, so a partial bucket's range came out empty or
|
|
11
10
|
* overlapping the next week.
|
|
12
11
|
*
|
|
13
|
-
* The point keeps its own wall-clock time (
|
|
14
|
-
* preserve it) and moves by whole calendar days to the anchor's
|
|
15
|
-
* point's day offset within its bucket.
|
|
12
|
+
* The point keeps its own wall-clock time (calendar subtraction and day
|
|
13
|
+
* addition both preserve it) and moves by whole calendar days to the anchor's
|
|
14
|
+
* day plus the point's day offset within its bucket.
|
|
16
15
|
*/
|
|
17
|
-
export function anchoredCalendarShiftSql(point, anchor, interval) {
|
|
18
|
-
const shiftedPoint =
|
|
19
|
-
const shiftedAnchor =
|
|
20
|
-
|
|
16
|
+
export function anchoredCalendarShiftSql(dialect, point, anchor, interval) {
|
|
17
|
+
const shiftedPoint = dialect.subtractTime(point, interval.amount, interval.unit);
|
|
18
|
+
const shiftedAnchor = dialect.subtractTime(anchor, interval.amount, interval.unit);
|
|
19
|
+
const days = `${dialect.timeDiff('day', shiftedPoint, shiftedAnchor)} + ${dialect.timeDiff('day', anchor, point)}`;
|
|
20
|
+
return dialect.addTime(shiftedPoint, days, 'day');
|
|
21
21
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DatasetSqlDialectRules } from '../dataset-sql-dialect.js';
|
|
2
2
|
/** Preserve the existing ClickHouse spelling and escaping byte for byte. */
|
|
3
|
-
export declare const clickhouseDatasetSqlDialect:
|
|
3
|
+
export declare const clickhouseDatasetSqlDialect: DatasetSqlDialectRules;
|
|
4
4
|
//# sourceMappingURL=clickhouse-dataset-sql-dialect.d.ts.map
|
|
@@ -1 +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,
|
|
1
|
+
{"version":3,"file":"clickhouse-dataset-sql-dialect.d.ts","sourceRoot":"","sources":["../../src/utils/clickhouse-dataset-sql-dialect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA4B,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAwClG,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,EAAE,sBAiHP,CAAC"}
|
|
@@ -1,6 +1,148 @@
|
|
|
1
|
-
import { quoteSQLIdentifier } from '../sql-utils.js';
|
|
1
|
+
import { quoteSQLIdentifier, stripSqlLiterals } from '../sql-utils.js';
|
|
2
|
+
import { assertAggregateStateFunction, mergeStateExpression } from './aggregate-state-functions.js';
|
|
3
|
+
const TRUNCATE_FUNCTIONS = {
|
|
4
|
+
minute: 'toStartOfMinute',
|
|
5
|
+
hour: 'toStartOfHour',
|
|
6
|
+
day: 'toStartOfDay',
|
|
7
|
+
// Mode 0 (Sunday) is the shipped behaviour; Monday weeks wait for a major.
|
|
8
|
+
week: 'toStartOfWeek',
|
|
9
|
+
month: 'toStartOfMonth',
|
|
10
|
+
quarter: 'toStartOfQuarter',
|
|
11
|
+
year: 'toStartOfYear',
|
|
12
|
+
};
|
|
13
|
+
const INTERVAL_UNITS = {
|
|
14
|
+
minute: 'Minutes', hour: 'Hours', day: 'Days', week: 'Weeks', month: 'Months', quarter: 'Quarters', year: 'Years',
|
|
15
|
+
};
|
|
16
|
+
const BINARY_OPERATORS = {
|
|
17
|
+
add: '+', subtract: '-', multiply: '*', divide: '/',
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Statement terminators and comment openers. A raw `sql` value is spliced into
|
|
21
|
+
* a larger expression, so any of these turns one expression into something
|
|
22
|
+
* else: `;` starts a second statement, `--` and `/*` comment out whatever the
|
|
23
|
+
* builder appends after it.
|
|
24
|
+
*/
|
|
25
|
+
const STATEMENT_BREAKERS = /;|--|\/\*/;
|
|
26
|
+
// ClickHouse also treats backslash as a string escape. Doubling quotes without
|
|
27
|
+
// escaping backslashes lets a value ending in \\ before a quote break out of
|
|
28
|
+
// the literal and change the predicate.
|
|
29
|
+
function stringLiteral(value) {
|
|
30
|
+
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
|
|
31
|
+
}
|
|
2
32
|
/** Preserve the existing ClickHouse spelling and escaping byte for byte. */
|
|
3
33
|
export const clickhouseDatasetSqlDialect = Object.freeze({
|
|
4
34
|
name: 'clickhouse',
|
|
5
35
|
quoteIdentifier: quoteSQLIdentifier,
|
|
36
|
+
// The adapter substitutes positional `?` on the client, one value per marker.
|
|
37
|
+
placeholder: () => '?',
|
|
38
|
+
reusesPlaceholders: false,
|
|
39
|
+
embedsPositionalPieces: true,
|
|
40
|
+
orderTerm: (expression, direction) => direction ? `${expression} ${direction}` : expression,
|
|
41
|
+
paginate: (limit, offset) => `${limit === undefined ? '' : ` LIMIT ${limit}`}${offset === undefined ? '' : ` OFFSET ${offset}`}`,
|
|
42
|
+
quoteQualifiedName: (name) => name.split('.').map(quoteSQLIdentifier).join('.'),
|
|
43
|
+
finalTable: (quotedSource) => `${quotedSource} FINAL`,
|
|
44
|
+
rawSqlExpressionError(sql) {
|
|
45
|
+
let code;
|
|
46
|
+
try {
|
|
47
|
+
// Quoted spans are blanked first, so a terminator inside a literal is data.
|
|
48
|
+
code = stripSqlLiterals(sql);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return 'unterminated-literal';
|
|
52
|
+
}
|
|
53
|
+
return STATEMENT_BREAKERS.test(code) ? 'statement-break' : undefined;
|
|
54
|
+
},
|
|
55
|
+
stringLiteral,
|
|
56
|
+
measureFilterLiteral(value) {
|
|
57
|
+
if (value === null)
|
|
58
|
+
return 'NULL';
|
|
59
|
+
if (typeof value === 'string')
|
|
60
|
+
return stringLiteral(value);
|
|
61
|
+
if (typeof value === 'number') {
|
|
62
|
+
if (!Number.isFinite(value)) {
|
|
63
|
+
throw new Error(`Invalid non-finite numeric literal in measure filter: ${value}`);
|
|
64
|
+
}
|
|
65
|
+
return String(value);
|
|
66
|
+
}
|
|
67
|
+
if (typeof value === 'boolean')
|
|
68
|
+
return value ? '1' : '0';
|
|
69
|
+
throw new Error(`Unsupported literal type in measure filter: ${typeof value}`);
|
|
70
|
+
},
|
|
71
|
+
truncateTime: (time, grain) => `${TRUNCATE_FUNCTIONS[grain]}(${time})`,
|
|
72
|
+
// Calendar truncation returns a zone-less Date; later day arithmetic on it
|
|
73
|
+
// would run in the server zone, not the query zone.
|
|
74
|
+
truncateTimeInZone: (time, grain, timezone) => `toDateTime64(${TRUNCATE_FUNCTIONS[grain]}(${time}), 9, ${timezone})`,
|
|
75
|
+
addTime: (time, amount, grain) => `add${INTERVAL_UNITS[grain]}(${time}, ${amount})`,
|
|
76
|
+
subtractTime: (time, amount, grain) => `subtract${INTERVAL_UNITS[grain]}(${time}, ${amount})`,
|
|
77
|
+
timeDiff: (grain, start, end) => `dateDiff('${grain}', ${start}, ${end})`,
|
|
78
|
+
toQueryTimezone: (time, timezone) => `toDateTime64(${time}, 9, '${timezone}')`,
|
|
79
|
+
parseTimestamp: (value, timezone) => `parseDateTime64BestEffort(${value}, 9, ${timezone})`,
|
|
80
|
+
timezoneOf: (time) => `timezoneOf(${time})`,
|
|
81
|
+
clockPart: (part, time) => `to${part}(${time})`,
|
|
82
|
+
isDateOnlyType: (value) => `position(toTypeName(${value}), 'Date') > 0 AND position(toTypeName(${value}), 'DateTime') = 0`,
|
|
83
|
+
guard: (condition, message) => `throwIf(${condition}, ${stringLiteral(message)})`,
|
|
84
|
+
ifElse: (condition, then, otherwise) => `if(${condition}, ${then}, ${otherwise})`,
|
|
85
|
+
greatest: (...values) => `greatest(${values.join(', ')})`,
|
|
86
|
+
least: (...values) => `least(${values.join(', ')})`,
|
|
87
|
+
isNotDistinctFrom: (left, right) => `isNotDistinctFrom(${left}, ${right})`,
|
|
88
|
+
isNotNull: (value) => `isNotNull(${value})`,
|
|
89
|
+
assumeNotNull: (value) => `assumeNotNull(${value})`,
|
|
90
|
+
toNullable: (value) => `toNullable(${value})`,
|
|
91
|
+
countIf: (condition) => `countIf(${condition})`,
|
|
92
|
+
seriesIndex: (count) => `arrayJoin(range(toUInt64(${count})))`,
|
|
93
|
+
anyIndex: (index, count, predicate) => `arrayExists(${index} -> ${predicate}, range(toUInt64(${count})))`,
|
|
94
|
+
packRow: (values) => `tuple(${values.join(', ')})`,
|
|
95
|
+
rowElement: (row, position) => `tupleElement(${row}, ${position})`,
|
|
96
|
+
selectAllExcept: (relation, columns) => `${relation}.* EXCEPT (${columns.join(', ')})`,
|
|
97
|
+
asofLeftJoin: 'LEFT ASOF JOIN',
|
|
98
|
+
filteredAggregateInput: (condition, value, fallback) => `if(${condition}, ${value}, ${fallback})`,
|
|
99
|
+
windowAggregate(definition, value, arg) {
|
|
100
|
+
switch (definition.aggregation) {
|
|
101
|
+
case 'count': return `count(${value})`;
|
|
102
|
+
case 'countDistinct': return `uniqExact(${value})`;
|
|
103
|
+
case 'approxCountDistinct': return `uniq(${value})`;
|
|
104
|
+
// -Merge cannot take -OrNull (the state type names the bare function); a
|
|
105
|
+
// window group always has rows, and counts coalesce to 0 downstream.
|
|
106
|
+
case 'merge': return mergeStateExpression(requireStateFunction(definition), value);
|
|
107
|
+
case 'percentile': return `quantileOrNull(${definition.level})(${value})`;
|
|
108
|
+
case 'argMax': return `argMaxOrNull(${value}, ${arg})`;
|
|
109
|
+
case 'argMin': return `argMinOrNull(${value}, ${arg})`;
|
|
110
|
+
case 'stddev': return `stddevSampOrNull(${value})`;
|
|
111
|
+
case 'variance': return `varSampOrNull(${value})`;
|
|
112
|
+
default: return `${definition.aggregation}OrNull(${value})`;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
reaggregate: (aggregation, value) => `${aggregation}OrNull(${value})`,
|
|
116
|
+
formulaLiteral(value) {
|
|
117
|
+
if (value === null)
|
|
118
|
+
return 'NULL';
|
|
119
|
+
if (typeof value === 'number') {
|
|
120
|
+
if (!Number.isFinite(value))
|
|
121
|
+
throw new Error('Formula contains a non-finite number.');
|
|
122
|
+
return String(value);
|
|
123
|
+
}
|
|
124
|
+
if (typeof value === 'boolean')
|
|
125
|
+
return value ? 'true' : 'false';
|
|
126
|
+
return stringLiteral(value);
|
|
127
|
+
},
|
|
128
|
+
formulaBinary(operator, left, right) {
|
|
129
|
+
const symbol = BINARY_OPERATORS[operator];
|
|
130
|
+
if (!symbol)
|
|
131
|
+
throw new Error('Formula contains an unsupported binary operator.');
|
|
132
|
+
return `(${left} ${symbol} ${right})`;
|
|
133
|
+
},
|
|
134
|
+
formulaFunction(name, args) {
|
|
135
|
+
switch (name) {
|
|
136
|
+
case 'nullIfZero': return `NULLIF(${args[0]}, 0)`;
|
|
137
|
+
case 'coalesce': return `COALESCE(${args.join(', ')})`;
|
|
138
|
+
case 'round': return `ROUND(${args.join(', ')})`;
|
|
139
|
+
case 'floor': return `FLOOR(${args[0]})`;
|
|
140
|
+
case 'ceil': return `CEIL(${args[0]})`;
|
|
141
|
+
default: throw new Error('Formula contains an unsupported function.');
|
|
142
|
+
}
|
|
143
|
+
},
|
|
6
144
|
});
|
|
145
|
+
function requireStateFunction(definition) {
|
|
146
|
+
assertAggregateStateFunction(definition.stateFunction, 'A merge measure');
|
|
147
|
+
return definition.stateFunction;
|
|
148
|
+
}
|
|
@@ -6,5 +6,5 @@ export interface PreparedDatasetQuery {
|
|
|
6
6
|
execute(): Promise<Record<string, unknown>[]>;
|
|
7
7
|
}
|
|
8
8
|
/** The one dispatch point for dataset preview and query-builder execution. */
|
|
9
|
-
export declare function prepareDatasetQuery(
|
|
9
|
+
export declare function prepareDatasetQuery(source: AnyDatasetInstance, input: DatasetQuery, options: DatasetQueryExecutionOptions, mode?: 'execution' | 'logical'): PreparedDatasetQuery;
|
|
10
10
|
//# sourceMappingURL=compile-dataset-query.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compile-dataset-query.d.ts","sourceRoot":"","sources":["../../src/utils/compile-dataset-query.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compile-dataset-query.d.ts","sourceRoot":"","sources":["../../src/utils/compile-dataset-query.ts"],"names":[],"mappings":"AAIA,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;AAWpE,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,MAAM,EAAE,kBAAkB,EAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,IAAI,GAAE,WAAW,GAAG,SAAuB,GAC1C,oBAAoB,CAyEtB"}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { queryReadsFinal, withFinalSafety } from './dataset-source.js';
|
|
2
|
+
import { buildRelationshipBuilderContext } from './relationship-builder-plan.js';
|
|
3
|
+
import { resolveDatasetSqlDialect } from './dataset-sql-dialect.js';
|
|
1
4
|
import { queryMeasureDefinitions } from './relationship-measures.js';
|
|
2
5
|
import { buildDatasetQueryBuilder } from './build-dataset-query-builder.js';
|
|
3
6
|
import { baseMeasureNames } from './dataset-measures.js';
|
|
@@ -7,8 +10,13 @@ import { overfetchLimit } from './pagination.js';
|
|
|
7
10
|
import { resolveResultLimit } from './result-limits.js';
|
|
8
11
|
import { buildTimeMeasureDatasetSql } from './time-measure-dataset-sql.js';
|
|
9
12
|
import { selectedTimeMeasures } from './time-query-measures.js';
|
|
13
|
+
import { renderStatement } from './statement-parameters.js';
|
|
14
|
+
import { datasetOnTimeKey, selectedTimeKeyError } from './dataset-time-keys.js';
|
|
10
15
|
/** The one dispatch point for dataset preview and query-builder execution. */
|
|
11
|
-
export function prepareDatasetQuery(
|
|
16
|
+
export function prepareDatasetQuery(source, input, options, mode = 'execution') {
|
|
17
|
+
// Every planner below compiles against the selected time axis. An unknown
|
|
18
|
+
// axis is left for each planner's validation to report as a query error.
|
|
19
|
+
const dataset = selectedTimeKeyError(source, input.timeKey) ? source : datasetOnTimeKey(source, input.timeKey);
|
|
12
20
|
const query = structuredClone({
|
|
13
21
|
...input,
|
|
14
22
|
limit: mode === 'execution' ? resolveResultLimit(input.limit, dataset.limits).limit : input.limit,
|
|
@@ -23,19 +31,25 @@ export function prepareDatasetQuery(dataset, input, options, mode = 'execution')
|
|
|
23
31
|
let execute;
|
|
24
32
|
let plan;
|
|
25
33
|
const abortSignal = options.context?.abortSignal;
|
|
34
|
+
// Raw-SQL plans lose builder settings, so they carry the FINAL pin in their SQL.
|
|
35
|
+
// Called after planning, which validates the query and its enforced constraints.
|
|
36
|
+
const readsFinal = () => queryReadsFinal(dataset, buildRelationshipBuilderContext(dataset, query, options.context, resolveDatasetSqlDialect(options.builderFactory)));
|
|
26
37
|
if (selectedTimeMeasures(dataset, query).size) {
|
|
27
38
|
plan = 'time';
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
const planned = buildTimeMeasureDatasetSql(dataset, query, executionOptions);
|
|
40
|
+
const main = withFinalSafety(readsFinal(), renderStatement(planned.statement));
|
|
41
|
+
const preflight = withFinalSafety(readsFinal(), renderStatement(planned.preflight));
|
|
42
|
+
statement = main;
|
|
43
|
+
preflightStatements.push(preflight);
|
|
31
44
|
execute = async () => {
|
|
32
|
-
await options.builderFactory.rawQuery(
|
|
33
|
-
return options.builderFactory.rawQuery(
|
|
45
|
+
await options.builderFactory.rawQuery(preflight.sql, preflight.parameters, { abortSignal });
|
|
46
|
+
return options.builderFactory.rawQuery(main.sql, main.parameters, { abortSignal });
|
|
34
47
|
};
|
|
35
48
|
}
|
|
36
49
|
else if (needsOuterDatasetQuery(dataset, query)) {
|
|
37
50
|
plan = hasSelectedDerivedMeasure(dataset, query) ? 'derived' : 'aggregate';
|
|
38
|
-
|
|
51
|
+
const derived = renderStatement(buildDerivedDatasetSql(dataset, query, executionOptions, buildDatasetQueryBuilder));
|
|
52
|
+
statement = withFinalSafety(readsFinal(), derived);
|
|
39
53
|
execute = () => options.builderFactory.rawQuery(statement.sql, statement.parameters, { abortSignal });
|
|
40
54
|
}
|
|
41
55
|
else {
|
|
@@ -53,7 +67,7 @@ export function prepareDatasetQuery(dataset, input, options, mode = 'execution')
|
|
|
53
67
|
filters: Object.freeze((query.filters ?? []).map(({ field, operator }) => Object.freeze({ field, operator }))),
|
|
54
68
|
having: Object.freeze((query.having ?? []).map(({ measure, operator }) => Object.freeze({ measure, operator }))),
|
|
55
69
|
segments: Object.freeze([...(query.segments ?? [])]),
|
|
56
|
-
by: query.by, timezone: query.timezone,
|
|
70
|
+
by: query.by, timeKey: query.timeKey, timezone: query.timezone,
|
|
57
71
|
effectiveLimit: query.limit, executionLimit, offset: query.offset ?? 0,
|
|
58
72
|
parameterCount: statement.parameters.length,
|
|
59
73
|
preflightStatementCount: preflightStatements.length,
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { AnyDatasetInstance, DatasetQuery } from '../types.js';
|
|
2
2
|
import type { DatasetQueryExecutionOptions } from '../dataset-query.js';
|
|
3
3
|
import type { TimeMeasureAxis } from './time-measure-axis.js';
|
|
4
|
+
import type { StatementParameters } from './statement-parameters.js';
|
|
5
|
+
import type { PlannedTimeMeasureStatements } from './time-measure-dataset-sql.js';
|
|
4
6
|
/** 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
|
-
};
|
|
7
|
+
export declare function buildCompositeTimeMeasureSql(ds: AnyDatasetInstance, query: DatasetQuery, options: DatasetQueryExecutionOptions, axis: TimeMeasureAxis, parameters: StatementParameters): PlannedTimeMeasureStatements;
|
|
10
8
|
//# sourceMappingURL=composite-time-measure-sql.d.ts.map
|
|
@@ -1 +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;
|
|
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;AAS9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACrE,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAElF,mFAAmF;AACnF,wBAAgB,4BAA4B,CAC1C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,4BAA4B,EACrC,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,mBAAmB,GAC9B,4BAA4B,CAgD9B"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { queryMeasureDefinitions } from './relationship-measures.js';
|
|
2
|
-
import { quoteSQLIdentifier } from '../sql-utils.js';
|
|
3
2
|
import { isDerivedMeasure, isWindowMeasure } from './dataset-measures.js';
|
|
4
3
|
import { buildMeasureEvaluationGraph, evaluationFormula } from './measure-evaluation-graph.js';
|
|
5
4
|
import { buildTimeMeasureSourceSql } from './time-measure-source-sql.js';
|
|
@@ -9,17 +8,18 @@ import { timeMeasureLeafSql } from './time-measure-leaf-sql.js';
|
|
|
9
8
|
import { derivedProjection } from './derived-measure-sql.js';
|
|
10
9
|
import { buildTimeMeasureResultSql } from './time-measure-result-sql.js';
|
|
11
10
|
/** Aggregate each dependency in its evaluation context, then evaluate formulas. */
|
|
12
|
-
export function buildCompositeTimeMeasureSql(ds, query, options, axis) {
|
|
11
|
+
export function buildCompositeTimeMeasureSql(ds, query, options, axis, parameters) {
|
|
12
|
+
const { dialect } = parameters;
|
|
13
13
|
const graph = buildMeasureEvaluationGraph(queryMeasureDefinitions(ds, query.measures ?? []), query.measures ?? []);
|
|
14
14
|
const leaves = graph.nodes.filter(node => !isDerivedMeasure(node.definition));
|
|
15
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);
|
|
16
|
+
const source = buildTimeMeasureSourceSql(ds, query, baseNames, axis.filters, options, parameters);
|
|
17
|
+
const timeAxis = buildTimeMeasureAxisSql(parameters, source, axis);
|
|
18
|
+
const contexts = timeMeasureContextCtes(dialect, graph.contexts, axis);
|
|
19
19
|
const inputs = leaves.map(node => {
|
|
20
20
|
const window = isWindowMeasure(node.definition) ? node.definition : undefined;
|
|
21
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) };
|
|
22
|
+
return { node, ...timeMeasureLeafSql(dialect, node.id, node.context, base, window, axis, source.dimensions) };
|
|
23
23
|
});
|
|
24
24
|
const keys = source.dimensions.map(dim => dim.alias);
|
|
25
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` : '';
|
|
@@ -33,29 +33,28 @@ export function buildCompositeTimeMeasureSql(ds, query, options, axis) {
|
|
|
33
33
|
const next = `_hq_evaluated${node.id}`;
|
|
34
34
|
const value = input.count ? `coalesce(m.${output}, 0)` : `m.${output}`;
|
|
35
35
|
if (input.cumulative) {
|
|
36
|
-
const partition = keys.length ?
|
|
36
|
+
const partition = dialect.packRow(keys.length ? keys.map(key => `s.${key}`) : ['0']);
|
|
37
37
|
const lookup = `_hq_lookup${node.id}`;
|
|
38
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
|
|
39
|
+
ctes.push(`${next} AS (SELECT ${dialect.selectAllExcept('s', ['_hq_eval_end', '_hq_partition'])}, ${value} AS ${output} FROM ${lookup} AS s ${dialect.asofLeftJoin} ${input.aggregate} AS m ON s._hq_partition = m._hq_partition AND s._hq_eval_end > m._hq_time)`);
|
|
40
40
|
}
|
|
41
41
|
else {
|
|
42
|
-
const condition = ['s._hq_period = m._hq_period', ...keys.map(key =>
|
|
42
|
+
const condition = ['s._hq_period = m._hq_period', ...keys.map(key => dialect.isNotDistinctFrom(`s.${key}`, `m.${key}`))];
|
|
43
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
44
|
}
|
|
45
45
|
previous = next;
|
|
46
46
|
}
|
|
47
47
|
for (const node of graph.nodes.filter(node => isDerivedMeasure(node.definition))) {
|
|
48
48
|
const next = `_hq_formula${node.id}`;
|
|
49
|
-
ctes.push(`${next} AS (SELECT *, ${derivedProjection(`_hq_value${node.id}`, evaluationFormula(node))} FROM ${previous})`);
|
|
49
|
+
ctes.push(`${next} AS (SELECT *, ${derivedProjection(dialect, `_hq_value${node.id}`, evaluationFormula(node))} FROM ${previous})`);
|
|
50
50
|
previous = next;
|
|
51
51
|
}
|
|
52
|
-
const projections = ['v._hq_period AS period', ...source.dimensions.map(dim => `v.${dim.alias} AS ${
|
|
53
|
-
...Object.entries(graph.outputs).map(([name, id]) => `v._hq_value${id} AS ${
|
|
52
|
+
const projections = ['v._hq_period AS period', ...source.dimensions.map(dim => `v.${dim.alias} AS ${dialect.quoteIdentifier(dim.name)}`),
|
|
53
|
+
...Object.entries(graph.outputs).map(([name, id]) => `v._hq_value${id} AS ${dialect.quoteIdentifier(name)}`)];
|
|
54
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(' + ');
|
|
55
|
+
const guard = [timeAxis.guard, timeAxis.rangeGuard, ...graph.contexts.map(context => timeMeasureContextGuard(dialect, context, axis)), ...inputs.map(input => input.guard)].join(' + ');
|
|
56
56
|
return {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
timeAxisSql: `WITH ${guardCtes.join(',\n')} SELECT ${guard} AS _hq_validated FROM _hq_bounds`,
|
|
57
|
+
statement: { text: buildTimeMeasureResultSql(ds, query, options, source.dimensions, ctes, guard, true), parameters },
|
|
58
|
+
preflight: { text: `WITH ${guardCtes.join(',\n')} SELECT ${guard} AS _hq_validated FROM _hq_bounds`, parameters },
|
|
60
59
|
};
|
|
61
60
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count-aggregations.d.ts","sourceRoot":"","sources":["../../src/utils/count-aggregations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGrD,wDAAwD;AACxD,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAGzE"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { isCountStateMerge } from './aggregate-state-functions.js';
|
|
2
|
+
/** Counts are 0, not NULL, over an empty population. */
|
|
3
|
+
export function isCountAggregation(definition) {
|
|
4
|
+
return ['count', 'countDistinct', 'approxCountDistinct'].includes(definition.aggregation)
|
|
5
|
+
|| isCountStateMerge(definition);
|
|
6
|
+
}
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Two categories of check live here:
|
|
13
13
|
*
|
|
14
|
-
* - **Identifier safety.** `source`, `tenantKey`, `timeKey`, dimension columns
|
|
14
|
+
* - **Identifier safety.** `source`, `tenantKey`, `timeKey`, `timeKeys`, dimension columns
|
|
15
15
|
* and measure fields are all interpolated into SQL as identifiers. They are
|
|
16
16
|
* validated once here rather than trusted at every call site that builds a
|
|
17
17
|
* predicate from them.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataset-definition-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-definition-validation.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,wBAAwB,EACxB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"dataset-definition-validation.d.ts","sourceRoot":"","sources":["../../src/utils/dataset-definition-validation.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EACV,aAAa,EACb,wBAAwB,EACxB,mBAAmB,EAGnB,sBAAsB,EAEvB,MAAM,aAAa,CAAC;AAWrB,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AACzD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;AAC5D,KAAK,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CAAC;AA2R/D;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,aAAa,CAAC,aAAa,EAAE,WAAW,EAAE,gBAAgB,CAAC,GAClE,IAAI,CAmFN"}
|