@hypequery/datasets 0.21.0 → 0.23.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 +6 -0
- package/dist/api.type-test.js +7 -0
- package/dist/cache/protocol-cache-keys.d.ts.map +1 -1
- package/dist/cache/protocol-cache-keys.js +5 -0
- package/dist/cache/query-signature.d.ts.map +1 -1
- package/dist/cache/query-signature.js +9 -0
- package/dist/catalog.d.ts +12 -0
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +14 -1
- package/dist/constants.d.ts +12 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +24 -0
- package/dist/contract.d.ts +4 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +4 -0
- package/dist/dataset-compilation.d.ts +50 -0
- package/dist/dataset-compilation.d.ts.map +1 -0
- package/dist/dataset-compilation.js +1 -0
- package/dist/dataset-query.d.ts +3 -3
- package/dist/dataset-query.d.ts.map +1 -1
- package/dist/dataset-query.js +7 -71
- package/dist/dataset-sql-dialect.d.ts +11 -0
- package/dist/dataset-sql-dialect.d.ts.map +1 -0
- package/dist/dataset-sql-dialect.js +1 -0
- package/dist/dataset.d.ts +3 -2
- package/dist/dataset.d.ts.map +1 -1
- package/dist/execution-capabilities.d.ts +28 -0
- package/dist/execution-capabilities.d.ts.map +1 -0
- package/dist/execution-capabilities.js +17 -0
- package/dist/execution-capabilities.json +13 -0
- package/dist/executor.d.ts +17 -38
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +40 -400
- package/dist/index.d.ts +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +2 -0
- package/dist/measure.d.ts +54 -17
- package/dist/measure.d.ts.map +1 -1
- package/dist/measure.js +3 -63
- package/dist/metric-query-engine.d.ts +29 -0
- package/dist/metric-query-engine.d.ts.map +1 -0
- package/dist/metric-query-engine.js +106 -0
- package/dist/portable-executor.d.ts +20 -6
- package/dist/portable-executor.d.ts.map +1 -1
- package/dist/portable-executor.js +29 -9
- package/dist/protocol-adapter.d.ts.map +1 -1
- package/dist/protocol-adapter.js +1 -0
- package/dist/protocol-rehydrate.d.ts.map +1 -1
- package/dist/protocol-rehydrate.js +9 -2
- package/dist/query-builder-protocol.d.ts +11 -0
- package/dist/query-builder-protocol.d.ts.map +1 -1
- package/dist/query-helpers.d.ts +10 -10
- package/dist/query-helpers.d.ts.map +1 -1
- package/dist/query-helpers.js +1 -3
- package/dist/query-planner.d.ts +7 -3
- package/dist/query-planner.d.ts.map +1 -1
- package/dist/query-planner.js +10 -12
- package/dist/relationship-check.d.ts +3 -2
- package/dist/relationship-check.d.ts.map +1 -1
- package/dist/relationship-check.js +8 -3
- package/dist/relationships.d.ts +6 -16
- package/dist/relationships.d.ts.map +1 -1
- package/dist/relationships.js +3 -12
- package/dist/semantic-planner.d.ts.map +1 -1
- package/dist/semantic-planner.js +4 -0
- package/dist/semantic-query-schema.d.ts +6 -0
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +31 -4
- package/dist/types.d.ts +67 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.d.ts +1 -0
- package/dist/utils/agent-catalog-projection.d.ts.map +1 -1
- package/dist/utils/agent-catalog-projection.js +2 -1
- package/dist/utils/base-measure-definition.d.ts +23 -0
- package/dist/utils/base-measure-definition.d.ts.map +1 -0
- package/dist/utils/base-measure-definition.js +65 -0
- package/dist/utils/build-dataset-query-builder.d.ts +5 -0
- package/dist/utils/build-dataset-query-builder.d.ts.map +1 -0
- package/dist/utils/build-dataset-query-builder.js +65 -0
- package/dist/utils/calendar-shift-sql.d.ts +18 -0
- package/dist/utils/calendar-shift-sql.d.ts.map +1 -0
- package/dist/utils/calendar-shift-sql.js +21 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts +4 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.d.ts.map +1 -0
- package/dist/utils/clickhouse-dataset-sql-dialect.js +6 -0
- package/dist/utils/compile-dataset-query.d.ts +10 -0
- package/dist/utils/compile-dataset-query.d.ts.map +1 -0
- package/dist/utils/compile-dataset-query.js +72 -0
- package/dist/utils/composite-time-measure-sql.d.ts.map +1 -1
- package/dist/utils/composite-time-measure-sql.js +2 -1
- package/dist/utils/dataset-definition-validation.d.ts.map +1 -1
- package/dist/utils/dataset-definition-validation.js +52 -2
- package/dist/utils/dataset-derived-query.d.ts +6 -2
- package/dist/utils/dataset-derived-query.d.ts.map +1 -1
- package/dist/utils/dataset-derived-query.js +28 -26
- package/dist/utils/dataset-filter-types.d.ts +28 -0
- package/dist/utils/dataset-filter-types.d.ts.map +1 -0
- package/dist/utils/dataset-filter-types.js +1 -0
- package/dist/utils/dataset-having.d.ts +21 -0
- package/dist/utils/dataset-having.d.ts.map +1 -0
- package/dist/utils/dataset-having.js +106 -0
- package/dist/utils/dataset-normalization.d.ts.map +1 -1
- package/dist/utils/dataset-normalization.js +5 -1
- package/dist/utils/dataset-query-validation.d.ts.map +1 -1
- package/dist/utils/dataset-query-validation.js +17 -3
- package/dist/utils/dataset-sql-dialect.d.ts +5 -0
- package/dist/utils/dataset-sql-dialect.d.ts.map +1 -0
- package/dist/utils/dataset-sql-dialect.js +5 -0
- package/dist/utils/dataset-target.d.ts +3 -0
- package/dist/utils/dataset-target.d.ts.map +1 -0
- package/dist/utils/dataset-target.js +3 -0
- package/dist/utils/filter-definition.d.ts +3 -0
- package/dist/utils/filter-definition.d.ts.map +1 -0
- package/dist/utils/filter-definition.js +3 -0
- package/dist/utils/metric-derived-query.d.ts +8 -0
- package/dist/utils/metric-derived-query.d.ts.map +1 -0
- package/dist/utils/metric-derived-query.js +106 -0
- package/dist/utils/metric-query-builder.d.ts +5 -0
- package/dist/utils/metric-query-builder.d.ts.map +1 -0
- package/dist/utils/metric-query-builder.js +50 -0
- package/dist/utils/metric-query-result.d.ts +10 -0
- package/dist/utils/metric-query-result.d.ts.map +1 -0
- package/dist/utils/metric-query-result.js +18 -0
- package/dist/utils/metric-query-validation.d.ts +6 -0
- package/dist/utils/metric-query-validation.d.ts.map +1 -0
- package/dist/utils/metric-query-validation.js +116 -0
- package/dist/utils/protocol-metric-expressions.d.ts +8 -0
- package/dist/utils/protocol-metric-expressions.d.ts.map +1 -1
- package/dist/utils/protocol-metric-expressions.js +23 -11
- package/dist/utils/relationship-builder-plan.d.ts +6 -2
- package/dist/utils/relationship-builder-plan.d.ts.map +1 -1
- package/dist/utils/relationship-builder-plan.js +24 -15
- package/dist/utils/relationship-definition.d.ts +6 -0
- package/dist/utils/relationship-definition.d.ts.map +1 -0
- package/dist/utils/relationship-definition.js +9 -0
- package/dist/utils/relationship-key-check.d.ts +4 -0
- package/dist/utils/relationship-key-check.d.ts.map +1 -1
- package/dist/utils/relationship-key-check.js +19 -7
- package/dist/utils/relationship-keys.d.ts +17 -0
- package/dist/utils/relationship-keys.d.ts.map +1 -0
- package/dist/utils/relationship-keys.js +63 -0
- package/dist/utils/relationship-measure-sql.d.ts +14 -0
- package/dist/utils/relationship-measure-sql.d.ts.map +1 -0
- package/dist/utils/relationship-measure-sql.js +44 -0
- package/dist/utils/relationship-measure-types.d.ts +36 -0
- package/dist/utils/relationship-measure-types.d.ts.map +1 -0
- package/dist/utils/relationship-measure-types.js +1 -0
- package/dist/utils/relationship-measures.d.ts +23 -0
- package/dist/utils/relationship-measures.d.ts.map +1 -0
- package/dist/utils/relationship-measures.js +62 -0
- package/dist/utils/relationship-validation.d.ts +1 -1
- package/dist/utils/relationship-validation.d.ts.map +1 -1
- package/dist/utils/relationship-validation.js +3 -5
- package/dist/utils/semantic-query-syntax.d.ts +6 -0
- package/dist/utils/semantic-query-syntax.d.ts.map +1 -0
- package/dist/utils/semantic-query-syntax.js +24 -0
- package/dist/utils/shift-measure-sql.d.ts.map +1 -1
- package/dist/utils/shift-measure-sql.js +10 -3
- package/dist/utils/snapshot-execution-context.d.ts +4 -0
- package/dist/utils/snapshot-execution-context.d.ts.map +1 -0
- package/dist/utils/snapshot-execution-context.js +16 -0
- package/dist/utils/time-measure-context-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-context-sql.js +8 -2
- package/dist/utils/time-measure-dataset-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-dataset-sql.js +3 -2
- package/dist/utils/time-measure-result-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-result-sql.js +2 -1
- package/dist/utils/time-measure-source-sql.d.ts.map +1 -1
- package/dist/utils/time-measure-source-sql.js +8 -4
- package/dist/validation.js +1 -1
- package/package.json +10 -5
package/dist/executor.d.ts
CHANGED
|
@@ -1,19 +1,14 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Semantic dataset client internals.
|
|
3
|
-
*
|
|
4
|
-
* Public callers use `createDatasetClient(...).execute(...)`. The implementation
|
|
5
|
-
* stays database-agnostic via query-builder and backend protocol interfaces.
|
|
6
|
-
*/
|
|
1
|
+
/** Dataset client orchestration: runtime defaults, caching, limits and target dispatch. */
|
|
7
2
|
import type { MetricRef, GrainedMetricRef, MetricQuery, MetricQueryFor, MetricResult, MetricResultFor, DatasetQuery, DatasetQueryFor, DatasetQueryResult, DatasetQueryResultFor, ExecutionContext, AnyDatasetInstance, DatasetInstance } from './types.js';
|
|
8
|
-
import type {
|
|
3
|
+
import type { QueryBuilderFactoryInput } from './query-builder-protocol.js';
|
|
9
4
|
import type { PlanNode, SemanticBackend } from './semantic-plan.js';
|
|
10
|
-
import {
|
|
5
|
+
import type { ValidationResult } from './validation.js';
|
|
6
|
+
import { MetricQueryEngine } from './metric-query-engine.js';
|
|
11
7
|
import { type DatasetQueryExecutionOptions } from './dataset-query.js';
|
|
8
|
+
import type { DatasetCompilation } from './dataset-compilation.js';
|
|
12
9
|
import { type SemanticCacheOptions, type SemanticCacheStats } from './cache/semantic-query-cache.js';
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
builderFactory: QueryBuilderFactoryInput;
|
|
16
|
-
}
|
|
10
|
+
export { MetricQueryEngine } from './metric-query-engine.js';
|
|
11
|
+
export type { MetricQueryEngineOptions } from './metric-query-engine.js';
|
|
17
12
|
export interface CreateDatasetClientOptions {
|
|
18
13
|
/** Default IANA timezone for buckets and local time-key bounds. Queries can override it. */
|
|
19
14
|
timezone?: string;
|
|
@@ -46,7 +41,14 @@ export interface DatasetClient {
|
|
|
46
41
|
execute<TDataset extends TypedDataset, const TQuery extends DatasetQueryFor<TDataset> = DatasetQueryFor<TDataset>>(target: TDataset, query?: TQuery, context?: ExecutionContext): Promise<DatasetQueryResultFor<TDataset, TQuery>>;
|
|
47
42
|
execute<TDatasetName extends string, TMetricName extends string, TDataset extends TypedDataset<TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: TypedMetricRef<TDatasetName, TMetricName, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
48
43
|
execute<TDatasetName extends string, TMetricName extends string, TDataset extends TypedDataset<TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: TypedGrainedMetricRef<TDatasetName, TMetricName, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
49
|
-
execute<
|
|
44
|
+
execute<TDatasetName extends string, TMetricName extends string, TDataset extends TypedDataset<TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: TypedMetricRef<TDatasetName, TMetricName, TDataset> | TypedGrainedMetricRef<TDatasetName, TMetricName, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
45
|
+
/** Explicit row types opt into the legacy dynamic query contract. */
|
|
46
|
+
execute<TRow = never, TTarget extends SemanticTarget = SemanticTarget>(target: [TRow] extends [never] ? never : TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): Promise<SemanticResult<TTarget, TRow>>;
|
|
47
|
+
/** Compile the actual dataset execution SQL, including ceilings and pagination overfetch.
|
|
48
|
+
* SQL and parameters are trusted-only; describe()/JSON serialization omit values.
|
|
49
|
+
* Frozen semantic backends and standalone metric handles are not supported.
|
|
50
|
+
*/
|
|
51
|
+
compileDataset<TDataset extends TypedDataset>(target: TDataset, query?: DatasetQueryFor<TDataset>, context?: ExecutionContext): DatasetCompilation;
|
|
50
52
|
toSQL<TTarget extends SemanticTarget>(target: TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): string;
|
|
51
53
|
validate<TTarget extends SemanticTarget>(target: TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): ValidationResult;
|
|
52
54
|
/** Lookup counters for this client's semantic result cache. */
|
|
@@ -57,26 +59,6 @@ export interface DatasetClient {
|
|
|
57
59
|
*/
|
|
58
60
|
clearCache(): Promise<boolean>;
|
|
59
61
|
}
|
|
60
|
-
export declare class MetricQueryEngine {
|
|
61
|
-
private builderFactory;
|
|
62
|
-
constructor(options: MetricQueryEngineOptions);
|
|
63
|
-
protected getBuilderFactory(): QueryBuilderFactoryLike;
|
|
64
|
-
/**
|
|
65
|
-
* Execute a metric query. Generates SQL, applies tenant/filter context, executes.
|
|
66
|
-
*/
|
|
67
|
-
run<T = Record<string, unknown>>(metric: MetricRef | GrainedMetricRef, query?: MetricQuery, context?: ExecutionContext): Promise<MetricResult<T>>;
|
|
68
|
-
/**
|
|
69
|
-
* Generate SQL without executing.
|
|
70
|
-
*/
|
|
71
|
-
toSQL(metric: MetricRef | GrainedMetricRef, query?: MetricQuery, context?: ExecutionContext): string;
|
|
72
|
-
/**
|
|
73
|
-
* Validate a metric query against the metric's contract.
|
|
74
|
-
*/
|
|
75
|
-
validate(metric: MetricRef | GrainedMetricRef, query: MetricQuery, context?: ExecutionContext): ValidationResult;
|
|
76
|
-
private runViaBuilder;
|
|
77
|
-
private buildBaseQuery;
|
|
78
|
-
private buildDerivedSQLViaBuilder;
|
|
79
|
-
}
|
|
80
62
|
export declare class DatasetClientImpl extends MetricQueryEngine implements DatasetClient {
|
|
81
63
|
private backend?;
|
|
82
64
|
private readonly defaultTimezone?;
|
|
@@ -95,11 +77,6 @@ export declare class DatasetClientImpl extends MetricQueryEngine implements Data
|
|
|
95
77
|
* the common uncached path.
|
|
96
78
|
*/
|
|
97
79
|
private isCacheable;
|
|
98
|
-
/**
|
|
99
|
-
* Context used for cache-key building: fills in the client-level default
|
|
100
|
-
* `cache.scope` unless the call sets its own, so clients sharing a custom
|
|
101
|
-
* store can be namespaced apart.
|
|
102
|
-
*/
|
|
103
80
|
/** The call's cache partition: its own `cache.scope`, else the client default. */
|
|
104
81
|
private cacheScope;
|
|
105
82
|
planMetric(metric: MetricRef | GrainedMetricRef, query?: MetricQuery, context?: ExecutionContext): PlanNode;
|
|
@@ -110,6 +87,8 @@ export declare class DatasetClientImpl extends MetricQueryEngine implements Data
|
|
|
110
87
|
execute<TDataset extends DatasetInstance<any, any, any, any>, const TQuery extends DatasetQueryFor<TDataset> = DatasetQueryFor<TDataset>>(target: TDataset, query?: TQuery, context?: ExecutionContext): Promise<DatasetQueryResultFor<TDataset, TQuery>>;
|
|
111
88
|
execute<TDatasetName extends string, TMetricName extends string, TDataset extends DatasetInstance<any, any, any, TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: MetricRef<TDatasetName, TMetricName, any, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
112
89
|
execute<TDatasetName extends string, TMetricName extends string, TDataset extends DatasetInstance<any, any, any, TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: GrainedMetricRef<TDatasetName, TMetricName, any, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
90
|
+
execute<TDatasetName extends string, TMetricName extends string, TDataset extends TypedDataset<TDatasetName>, const TQuery extends MetricQueryFor<TDataset, TMetricName> = MetricQueryFor<TDataset, TMetricName>>(target: TypedMetricRef<TDatasetName, TMetricName, TDataset> | TypedGrainedMetricRef<TDatasetName, TMetricName, TDataset>, query?: TQuery, context?: ExecutionContext): Promise<MetricResultFor<TDataset, TMetricName, TQuery>>;
|
|
91
|
+
compileDataset<TDataset extends TypedDataset>(target: TDataset, query?: DatasetQueryFor<TDataset>, context?: ExecutionContext): DatasetCompilation;
|
|
113
92
|
toSQL<TTarget extends SemanticTarget>(target: TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): string;
|
|
114
93
|
validate<TTarget extends SemanticTarget>(target: TTarget, query?: SemanticQuery<TTarget>, context?: ExecutionContext): ValidationResult;
|
|
115
94
|
private executeMetric;
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
CHANGED
|
@@ -1,386 +1,23 @@
|
|
|
1
|
-
import { queryTimezoneErrors, queryTimeFilterSql } from './utils/query-timezone.js';
|
|
2
|
-
import { selectedTimeMeasures, rejectTimeMeasuresOnBackend } from './utils/time-query-measures.js';
|
|
3
|
-
import { buildTimeMeasureDatasetSql } from './utils/time-measure-dataset-sql.js';
|
|
4
|
-
import { baseMeasureNames } from './utils/dataset-measures.js';
|
|
5
|
-
import { protocolMetricCapabilityErrors } from './utils/protocol-metric-capabilities.js';
|
|
6
1
|
import { toQueryBuilderFactory } from './query-builder-protocol.js';
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
2
|
+
import { MetricQueryEngine } from './metric-query-engine.js';
|
|
3
|
+
import { runDatasetQuery, validateDatasetQuery } from './dataset-query.js';
|
|
4
|
+
import { buildDatasetPlan, buildMetricPlan } from './semantic-planner.js';
|
|
5
|
+
import { assertMetricHandle, getMetricRef } from './utils/metric-handle.js';
|
|
6
|
+
import { validateMetricQueryInput } from './utils/metric-query-validation.js';
|
|
7
|
+
import { prepareDatasetQuery } from './utils/compile-dataset-query.js';
|
|
8
|
+
import { snapshotExecutionContext } from './utils/snapshot-execution-context.js';
|
|
9
|
+
import { isDatasetInstance } from './utils/dataset-target.js';
|
|
10
|
+
import { queryTimezoneErrors } from './utils/query-timezone.js';
|
|
11
|
+
import { rejectTimeMeasuresOnBackend } from './utils/time-query-measures.js';
|
|
12
|
+
import { baseMeasureNames } from './utils/dataset-measures.js';
|
|
13
|
+
import { hasSelectedDerivedMeasure } from './utils/dataset-derived-query.js';
|
|
18
14
|
import { serializeSemanticMeasureValues } from './utils/semantic-result-serialization.js';
|
|
15
|
+
import { resolveResultLimit, withResultLimit } from './utils/result-limits.js';
|
|
16
|
+
import { resolveDatasetCacheRuntime } from './utils/dataset-cache-policy.js';
|
|
19
17
|
import { SemanticQueryCache, } from './cache/semantic-query-cache.js';
|
|
20
18
|
import { datasetCacheKey, metricCacheKey, resolveProtocolCacheKeySettings, } from './cache/protocol-cache-keys.js';
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
import { isQualifiedField, resolveQualifiedField } from './utils/relationship-fields.js';
|
|
24
|
-
import { validateQualifiedFilter, validateRelationshipTenantRuntime, } from './utils/relationship-validation.js';
|
|
25
|
-
import { applyRelationshipJoins, buildRelationshipBuilderContext, qualifyBaseColumn, } from './utils/relationship-builder-plan.js';
|
|
26
|
-
import { segmentFilters, segmentSelectionErrors } from './utils/segments.js';
|
|
27
|
-
function validateQuery(metric, query, context) {
|
|
28
|
-
const errors = [...protocolMetricCapabilityErrors(metric, query), ...queryTimezoneErrors(query.timezone)];
|
|
29
|
-
const ref = getMetricRef(metric);
|
|
30
|
-
const ds = ref.dataset;
|
|
31
|
-
const dimensionNames = Object.keys(ds.dimensions);
|
|
32
|
-
const filterNames = Object.keys(ds.filters);
|
|
33
|
-
const grain = getMetricGrain(metric, query);
|
|
34
|
-
const orderableFields = new Set([
|
|
35
|
-
...(query.dimensions ?? []),
|
|
36
|
-
ref.name,
|
|
37
|
-
...(grain ? ['period'] : []),
|
|
38
|
-
]);
|
|
39
|
-
const tenantRuntimeError = validateTenantRuntime(ds, context);
|
|
40
|
-
if (tenantRuntimeError) {
|
|
41
|
-
errors.push(tenantRuntimeError);
|
|
42
|
-
}
|
|
43
|
-
const relationshipTenantError = validateRelationshipTenantRuntime(ds, query, context);
|
|
44
|
-
if (relationshipTenantError) {
|
|
45
|
-
errors.push(relationshipTenantError);
|
|
46
|
-
}
|
|
47
|
-
if (metric.__type === 'grained_metric_ref' && query.by && query.by !== metric.grain) {
|
|
48
|
-
errors.push(`Metric "${ref.name}" is already grained by "${metric.grain}" and cannot be queried with by="${query.by}".`);
|
|
49
|
-
}
|
|
50
|
-
// Validate dimensions
|
|
51
|
-
for (const dim of query.dimensions ?? []) {
|
|
52
|
-
if (isQualifiedField(dim)) {
|
|
53
|
-
const resolution = resolveQualifiedField(ds, dim);
|
|
54
|
-
if (resolution?.error) {
|
|
55
|
-
errors.push(resolution.error);
|
|
56
|
-
}
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (!dimensionNames.includes(dim)) {
|
|
60
|
-
errors.push(`Unknown dimension "${dim}". Available: ${dimensionNames.join(', ')}`);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
// Validate filters
|
|
64
|
-
for (const filter of query.filters ?? []) {
|
|
65
|
-
if (isQualifiedField(filter.field)) {
|
|
66
|
-
const filterError = validateQualifiedFilter(ds, filter, context);
|
|
67
|
-
if (filterError) {
|
|
68
|
-
errors.push(filterError);
|
|
69
|
-
}
|
|
70
|
-
continue;
|
|
71
|
-
}
|
|
72
|
-
if (!filterNames.includes(filter.field)) {
|
|
73
|
-
errors.push(`Unknown filter field "${filter.field}". Available: ${filterNames.join(', ')}`);
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const filterDefinition = ds.filters[filter.field];
|
|
77
|
-
if (filterDefinition?.operators && !filterDefinition.operators.includes(filter.operator)) {
|
|
78
|
-
errors.push(`Filter "${filter.field}" does not allow operator "${filter.operator}". Allowed: ${filterDefinition.operators.join(', ')}`);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const resolvedField = ds.filters[filter.field]?.field ?? filter.field;
|
|
82
|
-
if (isTenantScopedFilter(ds, filter, context)) {
|
|
83
|
-
errors.push(`Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`);
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
const fieldType = ds.dimensions[resolvedField]?.fieldType;
|
|
87
|
-
if (fieldType) {
|
|
88
|
-
const filterError = validateFilterValue(filter, fieldType);
|
|
89
|
-
if (filterError) {
|
|
90
|
-
errors.push(filterError);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Validate order by fields against the metric output shape
|
|
95
|
-
for (const order of query.orderBy ?? []) {
|
|
96
|
-
if (!orderableFields.has(order.field)) {
|
|
97
|
-
errors.push(`Unknown orderBy field "${order.field}". Available: ${Array.from(orderableFields).join(', ')}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Validate grain requires timeKey
|
|
101
|
-
if (query.by && !ds.timeKey) {
|
|
102
|
-
errors.push(`Cannot use "by" grain — dataset "${ds.name}" has no timeKey.`);
|
|
103
|
-
}
|
|
104
|
-
// Validate grain is one the planner can bucket on
|
|
105
|
-
errors.push(...segmentSelectionErrors(ds, query.segments));
|
|
106
|
-
const grainError = query.by && ds.timeKey ? unsupportedTimeGrainError(ds, query.by) : undefined;
|
|
107
|
-
if (grainError) {
|
|
108
|
-
errors.push(grainError);
|
|
109
|
-
}
|
|
110
|
-
if (query.limit != null && (!Number.isInteger(query.limit) || query.limit < 0)) {
|
|
111
|
-
errors.push(`Invalid limit: expected a non-negative integer.`);
|
|
112
|
-
}
|
|
113
|
-
if (query.offset != null && (!Number.isInteger(query.offset) || query.offset < 0)) {
|
|
114
|
-
errors.push(`Invalid offset: expected a non-negative integer.`);
|
|
115
|
-
}
|
|
116
|
-
// Validate limits
|
|
117
|
-
if (ds.limits) {
|
|
118
|
-
if (ds.limits.maxDimensions && (query.dimensions?.length ?? 0) > ds.limits.maxDimensions) {
|
|
119
|
-
errors.push(`Too many dimensions (${query.dimensions?.length}). Max: ${ds.limits.maxDimensions}`);
|
|
120
|
-
}
|
|
121
|
-
if (ds.limits.maxMeasures && 1 > ds.limits.maxMeasures) {
|
|
122
|
-
errors.push(`Too many measures (1). Max: ${ds.limits.maxMeasures}`);
|
|
123
|
-
}
|
|
124
|
-
if (ds.limits.maxFilters && (query.filters?.length ?? 0) > ds.limits.maxFilters) {
|
|
125
|
-
errors.push(`Too many filters (${query.filters?.length}). Max: ${ds.limits.maxFilters}`);
|
|
126
|
-
}
|
|
127
|
-
if (ds.limits.maxResultSize && query.limit != null && query.limit > ds.limits.maxResultSize) {
|
|
128
|
-
errors.push(`Too many results requested (${query.limit}). Max: ${ds.limits.maxResultSize}`);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return { valid: errors.length === 0, errors };
|
|
132
|
-
}
|
|
133
|
-
function isDatasetInstance(target) {
|
|
134
|
-
return !!target && typeof target === 'object' && '__type' in target && target.__type === 'dataset';
|
|
135
|
-
}
|
|
136
|
-
/** Resolves the per-call builder override, adapted to the call contract. */
|
|
137
|
-
function resolveBuilderFactory(context, fallback) {
|
|
138
|
-
const override = context?.runtime?.builderFactory;
|
|
139
|
-
return override ? toQueryBuilderFactory(override) : fallback;
|
|
140
|
-
}
|
|
141
|
-
export class MetricQueryEngine {
|
|
142
|
-
builderFactory;
|
|
143
|
-
constructor(options) {
|
|
144
|
-
this.builderFactory = toQueryBuilderFactory(options.builderFactory);
|
|
145
|
-
}
|
|
146
|
-
getBuilderFactory() {
|
|
147
|
-
return this.builderFactory;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Execute a metric query. Generates SQL, applies tenant/filter context, executes.
|
|
151
|
-
*/
|
|
152
|
-
async run(metric, query = {}, context) {
|
|
153
|
-
assertMetricHandle(metric);
|
|
154
|
-
const validation = this.validate(metric, query, context);
|
|
155
|
-
if (!validation.valid) {
|
|
156
|
-
throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
|
|
157
|
-
}
|
|
158
|
-
const start = Date.now();
|
|
159
|
-
return this.runViaBuilder(metric, query, context, start);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Generate SQL without executing.
|
|
163
|
-
*/
|
|
164
|
-
toSQL(metric, query = {}, context) {
|
|
165
|
-
assertMetricHandle(metric);
|
|
166
|
-
const validation = this.validate(metric, query, context);
|
|
167
|
-
if (!validation.valid) {
|
|
168
|
-
throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
|
|
169
|
-
}
|
|
170
|
-
const ref = getMetricRef(metric);
|
|
171
|
-
const grain = getMetricGrain(metric, query);
|
|
172
|
-
const spec = ref.spec;
|
|
173
|
-
if (spec.__type === 'derived_metric_spec') {
|
|
174
|
-
return this.buildDerivedSQLViaBuilder(ref, spec, query, grain, context).sql;
|
|
175
|
-
}
|
|
176
|
-
const builder = this.buildBaseQuery(ref, spec, ref.dataset, query, grain, context);
|
|
177
|
-
return builder.toSQLWithParams().sql;
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* Validate a metric query against the metric's contract.
|
|
181
|
-
*/
|
|
182
|
-
validate(metric, query, context) {
|
|
183
|
-
assertMetricHandle(metric);
|
|
184
|
-
const queryValidation = validateQuery(metric, query, context);
|
|
185
|
-
if (!queryValidation.valid) {
|
|
186
|
-
return queryValidation;
|
|
187
|
-
}
|
|
188
|
-
const ref = getMetricRef(metric);
|
|
189
|
-
const grain = getMetricGrain(metric, query);
|
|
190
|
-
try {
|
|
191
|
-
if (ref.spec.__type === 'derived_metric_spec') {
|
|
192
|
-
this.buildDerivedSQLViaBuilder(ref, ref.spec, query, grain, context);
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
this.buildBaseQuery(ref, ref.spec, ref.dataset, query, grain, context).toSQLWithParams();
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
return {
|
|
200
|
-
valid: false,
|
|
201
|
-
errors: [error instanceof Error ? error.message : String(error)],
|
|
202
|
-
};
|
|
203
|
-
}
|
|
204
|
-
return queryValidation;
|
|
205
|
-
}
|
|
206
|
-
// ---------------------------------------------------------------------------
|
|
207
|
-
// Query builder path
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
async runViaBuilder(metric, query, context, start) {
|
|
210
|
-
const ref = getMetricRef(metric);
|
|
211
|
-
const grain = getMetricGrain(metric, query);
|
|
212
|
-
const spec = ref.spec;
|
|
213
|
-
const activeBuilderFactory = resolveBuilderFactory(context, this.builderFactory);
|
|
214
|
-
// Over-fetch one row so we can report `hasMore` without a count query.
|
|
215
|
-
const buildQuery = { ...query, limit: overfetchLimit(query.limit) };
|
|
216
|
-
if (spec.__type === 'derived_metric_spec') {
|
|
217
|
-
// Derived metrics: build CTE via builder, outer query via string, execute via rawQuery
|
|
218
|
-
const { sql, params } = this.buildDerivedSQLViaBuilder(ref, spec, buildQuery, grain, context);
|
|
219
|
-
const rows = await activeBuilderFactory.rawQuery(sql, params, {
|
|
220
|
-
abortSignal: context?.abortSignal,
|
|
221
|
-
});
|
|
222
|
-
const timingMs = Date.now() - start;
|
|
223
|
-
const { data, pagination } = applyPagination(rows, query.limit, query.offset);
|
|
224
|
-
const serializedData = serializeSemanticMeasureValues(data, [ref.name]);
|
|
225
|
-
return {
|
|
226
|
-
data: serializedData,
|
|
227
|
-
meta: {
|
|
228
|
-
sql,
|
|
229
|
-
timingMs,
|
|
230
|
-
tenant: getRuntimeTenantId(context),
|
|
231
|
-
rowCount: serializedData.length,
|
|
232
|
-
pagination,
|
|
233
|
-
},
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
// Base metrics: fully use the builder's execute()
|
|
237
|
-
const builder = this.buildBaseQuery(ref, spec, ref.dataset, buildQuery, grain, context);
|
|
238
|
-
const { sql } = builder.toSQLWithParams();
|
|
239
|
-
const rows = await builder.execute({ abortSignal: context?.abortSignal });
|
|
240
|
-
const timingMs = Date.now() - start;
|
|
241
|
-
const { data, pagination } = applyPagination(rows, query.limit, query.offset);
|
|
242
|
-
const serializedData = serializeSemanticMeasureValues(data, [ref.name]);
|
|
243
|
-
return {
|
|
244
|
-
data: serializedData,
|
|
245
|
-
meta: {
|
|
246
|
-
sql,
|
|
247
|
-
timingMs,
|
|
248
|
-
tenant: getRuntimeTenantId(context),
|
|
249
|
-
rowCount: serializedData.length,
|
|
250
|
-
pagination,
|
|
251
|
-
},
|
|
252
|
-
};
|
|
253
|
-
}
|
|
254
|
-
buildBaseQuery(ref, spec, ds, query, grain, context) {
|
|
255
|
-
const activeBuilderFactory = resolveBuilderFactory(context, this.builderFactory);
|
|
256
|
-
const joinCtx = buildRelationshipBuilderContext(ds, query, context);
|
|
257
|
-
let qb = activeBuilderFactory.table(ds.source);
|
|
258
|
-
qb = applyRelationshipJoins(qb, joinCtx);
|
|
259
|
-
const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain, joinCtx, query.timezone);
|
|
260
|
-
if (selectParts.length > 0) {
|
|
261
|
-
qb = qb.select(selectParts);
|
|
262
|
-
}
|
|
263
|
-
// Aggregation (appends to select, auto-sets groupBy on non-agg columns)
|
|
264
|
-
qb = applyAggregationSpec(qb, ds, spec, ref.name, joinCtx);
|
|
265
|
-
// Explicit groupBy (ensures period + dims are grouped even if aggregation auto-groupBy misses them)
|
|
266
|
-
if (groupByParts.length > 0) {
|
|
267
|
-
qb = qb.groupBy(groupByParts);
|
|
268
|
-
}
|
|
269
|
-
// Tenant auto-injection
|
|
270
|
-
const tenantColumn = resolveTenantFilterColumn(ds, context);
|
|
271
|
-
const tenantPredicate = getRuntimeTenantPredicate(context);
|
|
272
|
-
if (tenantPredicate && tenantColumn) {
|
|
273
|
-
qb = qb.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
|
|
274
|
-
}
|
|
275
|
-
// User filters
|
|
276
|
-
for (const filter of query.filters ?? []) {
|
|
277
|
-
if (isTenantScopedFilter(ds, filter, context)) {
|
|
278
|
-
throw new Error(`Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`);
|
|
279
|
-
}
|
|
280
|
-
const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
|
|
281
|
-
qb = qb.where(queryTimeFilterSql(ds, filter.field, resolvedField, query.timezone), filter.operator, filter.value);
|
|
282
|
-
}
|
|
283
|
-
// Segments: author-defined, so they resolve dimensions directly rather
|
|
284
|
-
// than through the caller-facing filter allow-list.
|
|
285
|
-
for (const filter of segmentFilters(ds, query.segments)) {
|
|
286
|
-
qb = qb.where(resolveDimensionExpression(ds, filter.field, joinCtx), filter.operator, filter.value);
|
|
287
|
-
}
|
|
288
|
-
// Order, limit, offset
|
|
289
|
-
qb = appendOrderLimitOffset(qb, query.orderBy, grain, query.limit, query.offset, joinCtx);
|
|
290
|
-
return qb;
|
|
291
|
-
}
|
|
292
|
-
buildDerivedSQLViaBuilder(ref, spec, query, grain, context) {
|
|
293
|
-
const activeBuilderFactory = resolveBuilderFactory(context, this.builderFactory);
|
|
294
|
-
const ds = ref.dataset;
|
|
295
|
-
const joinCtx = buildRelationshipBuilderContext(ds, query, context);
|
|
296
|
-
// Build the CTE inner query using the builder
|
|
297
|
-
let cteBuilder = activeBuilderFactory.table(ds.source);
|
|
298
|
-
cteBuilder = applyRelationshipJoins(cteBuilder, joinCtx);
|
|
299
|
-
const { selectParts, groupByParts } = buildDimensionSelectionPlan(ds, query.dimensions ?? [], grain, joinCtx, query.timezone);
|
|
300
|
-
if (selectParts.length > 0) {
|
|
301
|
-
cteBuilder = cteBuilder.select(selectParts);
|
|
302
|
-
}
|
|
303
|
-
// Base aggregations
|
|
304
|
-
const refAliases = {};
|
|
305
|
-
const aggregateAliases = [];
|
|
306
|
-
for (const [alias, baseMetric] of Object.entries(spec.uses)) {
|
|
307
|
-
const baseSpec = baseMetric.spec;
|
|
308
|
-
if (baseSpec.__type !== 'aggregation_spec') {
|
|
309
|
-
throw new Error(`Derived metric "${ref.name}" references non-base metric "${alias}".`);
|
|
310
|
-
}
|
|
311
|
-
cteBuilder = applyAggregationSpec(cteBuilder, ds, baseSpec, alias, joinCtx);
|
|
312
|
-
refAliases[alias] = alias;
|
|
313
|
-
aggregateAliases.push(alias);
|
|
314
|
-
}
|
|
315
|
-
if (groupByParts.length > 0) {
|
|
316
|
-
cteBuilder = cteBuilder.groupBy(groupByParts);
|
|
317
|
-
}
|
|
318
|
-
// Filters on CTE
|
|
319
|
-
const tenantColumn = resolveTenantFilterColumn(ds, context);
|
|
320
|
-
const tenantPredicate = getRuntimeTenantPredicate(context);
|
|
321
|
-
if (tenantPredicate && tenantColumn) {
|
|
322
|
-
cteBuilder = cteBuilder.where(qualifyBaseColumn(joinCtx, tenantColumn), tenantPredicate.operator, tenantPredicate.value);
|
|
323
|
-
}
|
|
324
|
-
for (const filter of query.filters ?? []) {
|
|
325
|
-
if (isTenantScopedFilter(ds, filter, context)) {
|
|
326
|
-
throw new Error(`Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`);
|
|
327
|
-
}
|
|
328
|
-
const resolvedField = resolveFilterField(ds, filter.field, joinCtx);
|
|
329
|
-
cteBuilder = cteBuilder.where(queryTimeFilterSql(ds, filter.field, resolvedField, query.timezone), filter.operator, filter.value);
|
|
330
|
-
}
|
|
331
|
-
for (const filter of segmentFilters(ds, query.segments)) {
|
|
332
|
-
cteBuilder = cteBuilder.where(resolveDimensionExpression(ds, filter.field, joinCtx), filter.operator, filter.value);
|
|
333
|
-
}
|
|
334
|
-
const { sql: cteSql, parameters: cteParams } = cteBuilder.toSQLWithParams();
|
|
335
|
-
const groupingErrors = validateDerivedCteGrouping(cteSql, aggregateAliases, groupByParts);
|
|
336
|
-
if (groupingErrors.length > 0) {
|
|
337
|
-
throw new Error(groupingErrors.join('; '));
|
|
338
|
-
}
|
|
339
|
-
// Outer query: trivial SELECT from the CTE — stays as string concat
|
|
340
|
-
// because table('base') would fail schema typing
|
|
341
|
-
const outerSelectParts = [];
|
|
342
|
-
if (grain)
|
|
343
|
-
outerSelectParts.push('period');
|
|
344
|
-
for (const dim of query.dimensions ?? []) {
|
|
345
|
-
// Joined dimensions surface from the CTE under their quoted qualified alias.
|
|
346
|
-
if (joinCtx && isQualifiedField(dim)) {
|
|
347
|
-
outerSelectParts.push(quoteSQLIdentifier(dim));
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
validateSQLIdentifier(dim, 'dimension name');
|
|
351
|
-
outerSelectParts.push(dim);
|
|
352
|
-
}
|
|
353
|
-
const formulaExpr = spec.formula(refAliases);
|
|
354
|
-
validateSQLIdentifier(ref.name, 'metric name');
|
|
355
|
-
outerSelectParts.push(`${formulaExpr.toSQL()} AS ${ref.name}`);
|
|
356
|
-
let sql = `WITH base AS (${cteSql}) SELECT ${outerSelectParts.join(', ')} FROM base`;
|
|
357
|
-
// ORDER BY
|
|
358
|
-
if (query.orderBy && query.orderBy.length > 0) {
|
|
359
|
-
const orderParts = query.orderBy.map(o => {
|
|
360
|
-
if (joinCtx && isQualifiedField(o.field)) {
|
|
361
|
-
return `${quoteSQLIdentifier(o.field)} ${o.direction.toUpperCase()}`;
|
|
362
|
-
}
|
|
363
|
-
validateSQLIdentifier(o.field, 'order by field');
|
|
364
|
-
return `${o.field} ${o.direction.toUpperCase()}`;
|
|
365
|
-
});
|
|
366
|
-
sql += ` ORDER BY ${orderParts.join(', ')}`;
|
|
367
|
-
}
|
|
368
|
-
else if (grain) {
|
|
369
|
-
sql += ' ORDER BY period';
|
|
370
|
-
}
|
|
371
|
-
if (query.limit != null) {
|
|
372
|
-
// Ensure limit is a safe integer
|
|
373
|
-
const limit = Math.floor(Math.abs(query.limit));
|
|
374
|
-
sql += ` LIMIT ${limit}`;
|
|
375
|
-
}
|
|
376
|
-
if (query.offset != null) {
|
|
377
|
-
// Ensure offset is a safe integer
|
|
378
|
-
const offset = Math.floor(Math.abs(query.offset));
|
|
379
|
-
sql += ` OFFSET ${offset}`;
|
|
380
|
-
}
|
|
381
|
-
return { sql, params: cteParams };
|
|
382
|
-
}
|
|
383
|
-
}
|
|
19
|
+
// Preserve the existing module surface for internal consumers.
|
|
20
|
+
export { MetricQueryEngine } from './metric-query-engine.js';
|
|
384
21
|
export class DatasetClientImpl extends MetricQueryEngine {
|
|
385
22
|
backend;
|
|
386
23
|
defaultTimezone;
|
|
@@ -460,11 +97,6 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
460
97
|
}
|
|
461
98
|
return this.cacheEnabledByDefault;
|
|
462
99
|
}
|
|
463
|
-
/**
|
|
464
|
-
* Context used for cache-key building: fills in the client-level default
|
|
465
|
-
* `cache.scope` unless the call sets its own, so clients sharing a custom
|
|
466
|
-
* store can be namespaced apart.
|
|
467
|
-
*/
|
|
468
100
|
/** The call's cache partition: its own `cache.scope`, else the client default. */
|
|
469
101
|
cacheScope(context) {
|
|
470
102
|
if (context?.cache === false)
|
|
@@ -474,7 +106,7 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
474
106
|
planMetric(metric, query = {}, context) {
|
|
475
107
|
query = this.withTimezone(query);
|
|
476
108
|
assertMetricHandle(metric);
|
|
477
|
-
const validation =
|
|
109
|
+
const validation = validateMetricQueryInput(metric, query, context);
|
|
478
110
|
if (!validation.valid) {
|
|
479
111
|
throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
|
|
480
112
|
}
|
|
@@ -484,7 +116,10 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
484
116
|
return buildDatasetPlan(ds, this.withTimezone(query), context);
|
|
485
117
|
}
|
|
486
118
|
execute(target, query = {}, context) {
|
|
487
|
-
|
|
119
|
+
// An async cache read must not let caller mutations change the query or
|
|
120
|
+
// tenant after its key has been derived.
|
|
121
|
+
query = this.withTimezone(structuredClone(query));
|
|
122
|
+
context = snapshotExecutionContext(context);
|
|
488
123
|
if (this.backend && query.timezone !== undefined) {
|
|
489
124
|
throw new Error('Execution timezone requires the queryBuilder execution path.');
|
|
490
125
|
}
|
|
@@ -493,6 +128,16 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
493
128
|
}
|
|
494
129
|
return this.executeMetric(target, query, context);
|
|
495
130
|
}
|
|
131
|
+
compileDataset(target, query = {}, context) {
|
|
132
|
+
if (this.backend)
|
|
133
|
+
throw new Error('Dataset compilation requires the queryBuilder execution path.');
|
|
134
|
+
if (!isDatasetInstance(target))
|
|
135
|
+
throw new Error('Dataset compilation requires a dataset target.');
|
|
136
|
+
return prepareDatasetQuery(target, this.withTimezone(query), {
|
|
137
|
+
builderFactory: this.resolveBuilderFactory(context),
|
|
138
|
+
context,
|
|
139
|
+
}).compilation;
|
|
140
|
+
}
|
|
496
141
|
toSQL(target, query = {}, context) {
|
|
497
142
|
query = this.withTimezone(query);
|
|
498
143
|
if (this.backend && query.timezone !== undefined) {
|
|
@@ -522,7 +167,7 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
522
167
|
const cacheRuntime = this.datasetCacheContext(ds, context);
|
|
523
168
|
const run = () => {
|
|
524
169
|
if (this.backend) {
|
|
525
|
-
const validation =
|
|
170
|
+
const validation = validateMetricQueryInput(metric, boundedQuery, context);
|
|
526
171
|
if (!validation.valid) {
|
|
527
172
|
throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
|
|
528
173
|
}
|
|
@@ -540,7 +185,7 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
540
185
|
// missing tenant runtime cannot be served from cache. Avoid this.validate()
|
|
541
186
|
// here because it dry-builds SQL, which is unnecessary on cache hits and
|
|
542
187
|
// invalid for backend-only clients.
|
|
543
|
-
const validation =
|
|
188
|
+
const validation = validateMetricQueryInput(metric, boundedQuery, context);
|
|
544
189
|
if (!validation.valid) {
|
|
545
190
|
throw new Error(`Invalid metric query: ${validation.errors.join('; ')}`);
|
|
546
191
|
}
|
|
@@ -567,6 +212,9 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
567
212
|
if (hasSelectedDerivedMeasure(ds, boundedQuery)) {
|
|
568
213
|
throw new Error('Derived dataset measures require the queryBuilder execution path.');
|
|
569
214
|
}
|
|
215
|
+
if (boundedQuery.having?.length) {
|
|
216
|
+
throw new Error('Dataset having conditions require the queryBuilder execution path.');
|
|
217
|
+
}
|
|
570
218
|
return this.backend.execute(this.planDataset(ds, boundedQuery, context), { abortSignal: context?.abortSignal }).then((result) => ({
|
|
571
219
|
...result,
|
|
572
220
|
data: serializeSemanticMeasureValues(result.data,
|
|
@@ -575,7 +223,7 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
575
223
|
}));
|
|
576
224
|
}
|
|
577
225
|
return runDatasetQuery(ds, boundedQuery, {
|
|
578
|
-
builderFactory: resolveBuilderFactory(context
|
|
226
|
+
builderFactory: this.resolveBuilderFactory(context),
|
|
579
227
|
context,
|
|
580
228
|
});
|
|
581
229
|
};
|
|
@@ -603,18 +251,10 @@ export class DatasetClientImpl extends MetricQueryEngine {
|
|
|
603
251
|
return { ...context, cache };
|
|
604
252
|
}
|
|
605
253
|
toDatasetSQL(ds, query, context) {
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
return buildTimeMeasureDatasetSql(ds, query, { builderFactory, context }).sql;
|
|
609
|
-
}
|
|
610
|
-
if (hasSelectedDerivedMeasure(ds, query)) {
|
|
611
|
-
return buildDerivedDatasetSql(ds, query, { builderFactory, context }, buildDatasetQueryBuilder).sql;
|
|
612
|
-
}
|
|
613
|
-
const builder = buildDatasetQueryBuilder(ds, query, {
|
|
614
|
-
builderFactory,
|
|
254
|
+
return prepareDatasetQuery(ds, query, {
|
|
255
|
+
builderFactory: this.resolveBuilderFactory(context),
|
|
615
256
|
context,
|
|
616
|
-
});
|
|
617
|
-
return builder.toSQLWithParams().sql;
|
|
257
|
+
}, 'logical').compilation.sql;
|
|
618
258
|
}
|
|
619
259
|
}
|
|
620
260
|
export function createDatasetClient(options) {
|