@hypequery/datasets 0.17.0 → 0.18.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/dist/api.type-test.js +5 -1
- package/dist/cloud-publishing.d.ts +23 -0
- package/dist/cloud-publishing.d.ts.map +1 -0
- package/dist/cloud-publishing.js +57 -0
- package/dist/executor.d.ts.map +1 -1
- package/dist/executor.js +1 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/semantic-query-schema.d.ts.map +1 -1
- package/dist/semantic-query-schema.js +38 -15
- package/dist/sql-portability.d.ts.map +1 -1
- package/dist/sql-portability.js +88 -19
- package/dist/utils/collect-cloud-datasets.d.ts +3 -0
- package/dist/utils/collect-cloud-datasets.d.ts.map +1 -0
- package/dist/utils/collect-cloud-datasets.js +12 -0
- package/dist/utils/filtered-aggregation-sql.d.ts.map +1 -1
- package/dist/utils/filtered-aggregation-sql.js +4 -1
- package/dist/utils/relationship-fields.d.ts.map +1 -1
- package/dist/utils/relationship-fields.js +6 -2
- package/dist/utils/relationship-validation.d.ts.map +1 -1
- package/dist/utils/relationship-validation.js +12 -1
- package/package.json +3 -3
package/dist/api.type-test.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { add, between, buildCanonicalSemanticQuerySchemas, buildProtocolDeploymentContract, createDatasetClient, dataset, desc, dimension, eq, measure, publishDatasets, rehydrateProtocolDeploymentContract, } from './index.js';
|
|
1
|
+
import { add, between, buildCanonicalSemanticQuerySchemas, buildProtocolDeploymentContract, createDatasetClient, dataset, desc, dimension, eq, measure, publishDatasets, publishToCloud, rehydrateProtocolDeploymentContract, } from './index.js';
|
|
2
2
|
const Orders = dataset('orders', {
|
|
3
3
|
source: 'orders',
|
|
4
4
|
tenantKey: 'tenant_id',
|
|
@@ -85,6 +85,10 @@ const _publishedDatasets = publishDatasets()
|
|
|
85
85
|
metrics: { customers: customerCountMetric },
|
|
86
86
|
})
|
|
87
87
|
.build();
|
|
88
|
+
const _cloudPublication = publishToCloud({
|
|
89
|
+
datasets: { Orders, Customers },
|
|
90
|
+
access: { roles: [], scopes: [] },
|
|
91
|
+
});
|
|
88
92
|
// @ts-expect-error published metrics must belong to the dataset being published.
|
|
89
93
|
publishDatasets().publish(Orders, { metrics: { customerCountMetric } });
|
|
90
94
|
const canonicalSchemas = buildCanonicalSemanticQuerySchemas({
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ProtocolDeploymentContract } from '@hypequery/protocol';
|
|
2
|
+
import type { AnyDatasetInstance } from './types.js';
|
|
3
|
+
export interface CloudPublishingOptions {
|
|
4
|
+
/** Datasets exposed to Cloud. Relationships are included as supporting datasets. */
|
|
5
|
+
readonly datasets: Readonly<Record<string, AnyDatasetInstance>>;
|
|
6
|
+
/** Explicit policy for every published dataset unless overridden below. */
|
|
7
|
+
readonly access: {
|
|
8
|
+
readonly roles: readonly string[];
|
|
9
|
+
readonly scopes: readonly string[];
|
|
10
|
+
};
|
|
11
|
+
/** Optional stricter or different policy by published dataset name. */
|
|
12
|
+
readonly datasetAccess?: Readonly<Record<string, {
|
|
13
|
+
readonly roles: readonly string[];
|
|
14
|
+
readonly scopes: readonly string[];
|
|
15
|
+
}>>;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Declare the dataset-only contract uploaded by `hypequery deploy`.
|
|
19
|
+
* This function performs no network request and requires no Serve API.
|
|
20
|
+
* Export its result as `cloud` from the deployment entrypoint.
|
|
21
|
+
*/
|
|
22
|
+
export declare function publishToCloud(options: CloudPublishingOptions): ProtocolDeploymentContract;
|
|
23
|
+
//# sourceMappingURL=cloud-publishing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-publishing.d.ts","sourceRoot":"","sources":["../src/cloud-publishing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,0BAA0B,EAA+B,MAAM,qBAAqB,CAAC;AAEnG,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAGrD,MAAM,WAAW,sBAAsB;IACrC,oFAAoF;IACpF,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAChE,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC;IACF,uEAAuE;IACvE,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAC/C,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;QAClC,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;KACpC,CAAC,CAAC,CAAC;CACL;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,sBAAsB,GAAG,0BAA0B,CAyD1F"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildProtocolDeploymentContract } from './protocol-deployment-adapter.js';
|
|
2
|
+
import { collectCloudDatasets } from './utils/collect-cloud-datasets.js';
|
|
3
|
+
/**
|
|
4
|
+
* Declare the dataset-only contract uploaded by `hypequery deploy`.
|
|
5
|
+
* This function performs no network request and requires no Serve API.
|
|
6
|
+
* Export its result as `cloud` from the deployment entrypoint.
|
|
7
|
+
*/
|
|
8
|
+
export function publishToCloud(options) {
|
|
9
|
+
const entries = Object.entries(options.datasets);
|
|
10
|
+
if (entries.length === 0)
|
|
11
|
+
throw new Error('Cloud publishing requires at least one dataset.');
|
|
12
|
+
if (!options.access || !Array.isArray(options.access.roles)
|
|
13
|
+
|| !Array.isArray(options.access.scopes)) {
|
|
14
|
+
throw new Error('Cloud publishing requires an explicit authenticated access policy.');
|
|
15
|
+
}
|
|
16
|
+
const datasets = new Map();
|
|
17
|
+
const exposed = new Set();
|
|
18
|
+
for (const [, dataset] of entries) {
|
|
19
|
+
collectCloudDatasets(datasets, dataset);
|
|
20
|
+
exposed.add(dataset.name);
|
|
21
|
+
}
|
|
22
|
+
for (const dataset of datasets.values()) {
|
|
23
|
+
const namedMetrics = dataset.metrics;
|
|
24
|
+
if (namedMetrics && Object.keys(namedMetrics).length > 0) {
|
|
25
|
+
throw new Error(`Cloud dataset "${dataset.name}" contains named metrics. `
|
|
26
|
+
+ 'The dataset-only Cloud publisher does not support named metrics yet.');
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
for (const name of Object.keys(options.datasetAccess ?? {})) {
|
|
30
|
+
if (!exposed.has(name)) {
|
|
31
|
+
throw new Error(`Cloud access policy names an unpublished dataset: "${name}".`);
|
|
32
|
+
}
|
|
33
|
+
const access = options.datasetAccess?.[name];
|
|
34
|
+
if (!access || !Array.isArray(access.roles) || !Array.isArray(access.scopes)) {
|
|
35
|
+
throw new Error(`Cloud access policy for "${name}" must set roles and scopes.`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const endpoints = {};
|
|
39
|
+
for (const dataset of datasets.values()) {
|
|
40
|
+
if (!exposed.has(dataset.name))
|
|
41
|
+
continue;
|
|
42
|
+
const access = options.datasetAccess?.[dataset.name] ?? options.access;
|
|
43
|
+
endpoints[dataset.name] = {
|
|
44
|
+
access: {
|
|
45
|
+
kind: 'authenticated',
|
|
46
|
+
roles: [...new Set(access.roles)].sort(),
|
|
47
|
+
scopes: [...new Set(access.scopes)].sort(),
|
|
48
|
+
},
|
|
49
|
+
tenant: dataset.tenantKey
|
|
50
|
+
? { kind: 'required', mode: 'auto-inject', column: dataset.tenantKey }
|
|
51
|
+
: { kind: 'not-required' },
|
|
52
|
+
maxLimit: dataset.limits?.maxResultSize ?? 1_000,
|
|
53
|
+
path: `/api/analytics/datasets/${dataset.name}/query`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return buildProtocolDeploymentContract([...datasets.values()].sort((left, right) => left.name.localeCompare(right.name)), { endpoints });
|
|
57
|
+
}
|
package/dist/executor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AACA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,SAAS,EACT,gBAAgB,EAChB,WAAW,EACX,cAAc,EACd,YAAY,EACZ,eAAe,EACf,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,qBAAqB,EACrB,gBAAgB,EAGhB,kBAAkB,EAClB,eAAe,EAEhB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAEV,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,6BAA6B,CAAC;AAErC,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EAChB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAAuB,KAAK,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAgB7E,OAAO,EAIL,KAAK,4BAA4B,EAClC,MAAM,oBAAoB,CAAC;AAgB5B,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,iCAAiC,CAAC;
|
|
1
|
+
{"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}
|
package/dist/executor.js
CHANGED
|
@@ -24,9 +24,7 @@ function validateQuery(metric, query, context) {
|
|
|
24
24
|
const ref = getMetricRef(metric);
|
|
25
25
|
const ds = ref.dataset;
|
|
26
26
|
const dimensionNames = Object.keys(ds.dimensions);
|
|
27
|
-
const filterNames = Object.keys(ds.filters)
|
|
28
|
-
? Object.keys(ds.filters)
|
|
29
|
-
: dimensionNames;
|
|
27
|
+
const filterNames = Object.keys(ds.filters);
|
|
30
28
|
const grain = getMetricGrain(metric, query);
|
|
31
29
|
const orderableFields = new Set([
|
|
32
30
|
...(query.dimensions ?? []),
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export { buildProtocolDeploymentContract } from './protocol-deployment-adapter.j
|
|
|
22
22
|
export type { BuildProtocolDeploymentContractOptions } from './protocol-deployment-adapter.js';
|
|
23
23
|
export { rehydrateProtocolDeploymentContract } from './protocol-rehydrate.js';
|
|
24
24
|
export type { BuildProtocolDatasetContractOptions } from './protocol-adapter.js';
|
|
25
|
+
export { publishToCloud } from './cloud-publishing.js';
|
|
26
|
+
export type { CloudPublishingOptions } from './cloud-publishing.js';
|
|
25
27
|
export { createPortableSemanticExecutor, PortableExecutionBudgetError, PortableExecutionTenantError, PortableExecutionUnsupportedError, } from './portable-executor.js';
|
|
26
28
|
export type { PortableSemanticBudget, PortableSemanticExecutionInput, PortableSemanticExecutorOptions, } from './portable-executor.js';
|
|
27
29
|
export { compilePortableSqlExpression, DEFAULT_SQL_PORTABILITY_LIMITS, } from './sql-portability.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAGhE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,aAAa,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGnI,OAAO,EACL,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAC/B,UAAU,EAAE,QAAQ,EACpB,KAAK,EAAE,KAAK,EAAE,IAAI,GACnB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EACzB,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAChC,GAAG,EAAE,IAAI,EACT,MAAM,EAAE,KAAK,GACd,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAGtD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,YAAY,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,YAAY,EACV,iBAAiB,EACjB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,wBAAwB,EACxB,qBAAqB,GACtB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,8BAA8B,EAAE,MAAM,cAAc,CAAC;AACrG,OAAO,EAAE,+BAA+B,EAAE,MAAM,gCAAgC,CAAC;AACjF,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,GACzB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,+BAA+B,EAC/B,4BAA4B,EAC5B,uBAAuB,EACvB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,mBAAmB,EACnB,2BAA2B,EAC3B,6BAA6B,EAC7B,qBAAqB,EACrB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,wBAAwB,EACxB,kBAAkB,EAClB,gBAAgB,EAChB,mBAAmB,EACnB,gCAAgC,GACjC,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,yBAAyB,EACzB,oBAAoB,EACpB,YAAY,EACZ,yBAAyB,GAC1B,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,yBAAyB,EACzB,+BAA+B,GAChC,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,iBAAiB,EACjB,gCAAgC,GACjC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,4BAA4B,EAAE,MAAM,uBAAuB,CAAC;AACrE,OAAO,EAAE,+BAA+B,EAAE,MAAM,kCAAkC,CAAC;AACnF,YAAY,EAAE,sCAAsC,EAAE,MAAM,kCAAkC,CAAC;AAC/F,OAAO,EAAE,mCAAmC,EAAE,MAAM,yBAAyB,CAAC;AAC9E,YAAY,EAAE,mCAAmC,EAAE,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -28,6 +28,7 @@ export { rehydrateProtocolDatasets, UnsupportedContractFeatureError, } from './p
|
|
|
28
28
|
export { buildProtocolDatasetContract } from './protocol-adapter.js';
|
|
29
29
|
export { buildProtocolDeploymentContract } from './protocol-deployment-adapter.js';
|
|
30
30
|
export { rehydrateProtocolDeploymentContract } from './protocol-rehydrate.js';
|
|
31
|
+
export { publishToCloud } from './cloud-publishing.js';
|
|
31
32
|
// Portable native execution of a semantic invocation (decision 0005).
|
|
32
33
|
export { createPortableSemanticExecutor, PortableExecutionBudgetError, PortableExecutionTenantError, PortableExecutionUnsupportedError, } from './portable-executor.js';
|
|
33
34
|
// SQL portability compiler (R1A-07)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-query-schema.d.ts","sourceRoot":"","sources":["../src/semantic-query-schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAK,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAEzC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"semantic-query-schema.d.ts","sourceRoot":"","sources":["../src/semantic-query-schema.ts"],"names":[],"mappings":"AAEA,OAAO,EAAK,KAAK,UAAU,EAAE,MAAM,KAAK,CAAC;AAEzC,OAAO,EAIL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACxB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAI1D,MAAM,WAAW,yBAAyB;IACxC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA2B,SAAQ,yBAAyB;IAC3E,iFAAiF;IACjF,UAAU,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IAC5B,mDAAmD;IACnD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qEAAqE;IACrE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iFAAiF;IACjF,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC;AAED,gGAAgG;AAChG,MAAM,MAAM,2BAA2B,GAAG,IAAI,CAC5C,kBAAkB,EAClB,YAAY,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAC9C,CAAC;AAEF,eAAO,MAAM,oCAAoC;;;;;;;EAO/C,CAAC;AAEH,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,sBAAsB,EAAE,UAAU,CAAC;IAC5C,QAAQ,CAAC,qBAAqB,EAAE,UAAU,CAAC;IAC3C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,oFAAoF;AACpF,MAAM,MAAM,yBAAyB,GAAG,oBAAoB,GAAG,cAAc,CAAC;AAyK9E,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,yBAAyB,EAClC,OAAO,GAAE,0BAA+B,GACvC,UAAU,CASZ;AAED,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,0BAA+B,EACxC,cAAc,CAAC,EAAE,2BAA2B,GAC3C,UAAU,CAcZ;AAsBD,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC3C,UAAU,CAMZ;AAyBD,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,EACnD,OAAO,GAAE,0BAA+B,GACvC,6BAA6B,CAgD/B;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,yBAAyB,EAClC,OAAO,GAAE,0BAA+B,EACxC,IAAI,SAAkB,GACrB,cAAc,CAEhB;AAED,kEAAkE;AAClE,wBAAgB,8BAA8B,CAC5C,OAAO,EAAE,yBAAyB,EAClC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,0BAA+B,EACxC,cAAc,CAAC,EAAE,2BAA2B,EAC5C,IAAI,SAAiB,GACpB,cAAc,CAKhB"}
|
|
@@ -22,6 +22,27 @@ function resolveCatalog(source) {
|
|
|
22
22
|
? source
|
|
23
23
|
: getDatasetCatalog(source);
|
|
24
24
|
}
|
|
25
|
+
function relationshipFilterPolicy(source) {
|
|
26
|
+
const fields = new Map();
|
|
27
|
+
// A serialized catalog has no target filter allowlist. Do not infer one
|
|
28
|
+
// from its queryable fields: a field may be selectable but not filterable.
|
|
29
|
+
if (isDatasetCatalog(source))
|
|
30
|
+
return fields;
|
|
31
|
+
for (const [name, relationship] of Object.entries(source.relationships)) {
|
|
32
|
+
if (relationship.kind === 'hasMany')
|
|
33
|
+
continue;
|
|
34
|
+
const target = relationship.target();
|
|
35
|
+
for (const [field, dimension] of Object.entries(target.dimensions)) {
|
|
36
|
+
const definition = target.filters[field];
|
|
37
|
+
if (!dimension.sql && definition?.field === field) {
|
|
38
|
+
fields.set(`${name}.${field}`, definition.operators
|
|
39
|
+
? [...definition.operators]
|
|
40
|
+
: [...SEMANTIC_FILTER_OPERATORS]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return fields;
|
|
45
|
+
}
|
|
25
46
|
function lowerLimit(...values) {
|
|
26
47
|
const finite = values.filter((value) => value !== undefined);
|
|
27
48
|
return finite.length > 0 ? Math.min(...finite) : undefined;
|
|
@@ -36,13 +57,12 @@ function boundedArray(item, maximum) {
|
|
|
36
57
|
const array = z.array(item);
|
|
37
58
|
return (maximum === undefined ? array : array.max(maximum)).optional();
|
|
38
59
|
}
|
|
39
|
-
function filterSchema(catalog, fields) {
|
|
60
|
+
function filterSchema(catalog, fields, relationshipOperators) {
|
|
40
61
|
const filterValue = z.unknown().refine(value => value !== undefined, 'Required');
|
|
41
|
-
const relationshipFields = new Set(getQueryableRelationshipFields(catalog));
|
|
42
62
|
const variants = uniqueSorted(fields).map((field) => z.object({
|
|
43
63
|
field: z.literal(field),
|
|
44
|
-
operator:
|
|
45
|
-
?
|
|
64
|
+
operator: relationshipOperators.has(field)
|
|
65
|
+
? fieldEnum(relationshipOperators.get(field) ?? [])
|
|
46
66
|
: fieldEnum(catalog.filters[field]?.operators ?? [...SEMANTIC_FILTER_OPERATORS]),
|
|
47
67
|
value: filterValue,
|
|
48
68
|
}).strict());
|
|
@@ -57,9 +77,11 @@ function filterSchema(catalog, fields) {
|
|
|
57
77
|
return variants[0];
|
|
58
78
|
return z.union(variants);
|
|
59
79
|
}
|
|
60
|
-
function queryShape(catalog, metricName, options, metric, localRelationshipFields = []) {
|
|
80
|
+
function queryShape(catalog, metricName, options, relationshipOperators, metric, localRelationshipFields = []) {
|
|
61
81
|
const limits = { ...DEFAULT_SEMANTIC_QUERY_SCHEMA_LIMITS, ...options };
|
|
62
82
|
const relationshipFields = getQueryableRelationshipFields(catalog);
|
|
83
|
+
const filterableRelationshipFields = [...relationshipOperators.keys()];
|
|
84
|
+
const filterableRelationshipSet = new Set(filterableRelationshipFields);
|
|
63
85
|
// `groupable: false` declares a dimension that exists to back a measure, not
|
|
64
86
|
// to be selected. The agent-safe catalog already hides those, so the
|
|
65
87
|
// generated schema must refuse them too — otherwise a dataset advertises one
|
|
@@ -80,14 +102,15 @@ function queryShape(catalog, metricName, options, metric, localRelationshipField
|
|
|
80
102
|
...relationshipFields.filter(isGroupable),
|
|
81
103
|
]);
|
|
82
104
|
const declaredFilters = Object.keys(catalog.filters);
|
|
105
|
+
const declaredFilterSet = new Set(declaredFilters);
|
|
83
106
|
const filterFields = metric
|
|
84
|
-
? uniqueSorted([
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
107
|
+
? uniqueSorted([
|
|
108
|
+
...metric.filters.filter(field => field.includes('.')
|
|
109
|
+
? filterableRelationshipSet.has(field)
|
|
110
|
+
: declaredFilterSet.has(field)),
|
|
111
|
+
...localRelationshipFields.filter(field => filterableRelationshipSet.has(field)),
|
|
112
|
+
])
|
|
113
|
+
: uniqueSorted([...declaredFilters, ...filterableRelationshipFields]);
|
|
91
114
|
const grains = metric
|
|
92
115
|
? metric.grain ? [metric.grain] : metric.grains
|
|
93
116
|
: catalog.supportedGrains;
|
|
@@ -114,7 +137,7 @@ function queryShape(catalog, metricName, options, metric, localRelationshipField
|
|
|
114
137
|
...(metricName ? {} : {
|
|
115
138
|
measures: boundedArray(fieldEnum([...Object.keys(catalog.measures), ...Object.keys(catalog.derivedMeasures ?? {})]), lowerLimit(catalog.limits?.maxMeasures, limits.maxMeasures)),
|
|
116
139
|
}),
|
|
117
|
-
filters: boundedArray(filterSchema(catalog, filterFields), lowerLimit(catalog.limits?.maxFilters, limits.maxFilters)),
|
|
140
|
+
filters: boundedArray(filterSchema(catalog, filterFields, relationshipOperators), lowerLimit(catalog.limits?.maxFilters, limits.maxFilters)),
|
|
118
141
|
orderBy: boundedArray(z.object({
|
|
119
142
|
field: fieldEnum(orderable),
|
|
120
143
|
direction: z.enum(['asc', 'desc']),
|
|
@@ -130,7 +153,7 @@ function queryShape(catalog, metricName, options, metric, localRelationshipField
|
|
|
130
153
|
};
|
|
131
154
|
}
|
|
132
155
|
export function buildDatasetInputSchema(dataset, options = {}) {
|
|
133
|
-
const schema = z.object(queryShape(resolveCatalog(dataset), undefined, options)).strict();
|
|
156
|
+
const schema = z.object(queryShape(resolveCatalog(dataset), undefined, options, relationshipFilterPolicy(dataset))).strict();
|
|
134
157
|
if (options.requireSelection === false)
|
|
135
158
|
return schema;
|
|
136
159
|
return schema.refine(input => (input.dimensions?.length ?? 0) > 0 || (input.measures?.length ?? 0) > 0, { message: 'At least one dimension or measure must be specified' });
|
|
@@ -141,7 +164,7 @@ export function buildMetricInputSchema(dataset, metricName, options = {}, metric
|
|
|
141
164
|
const localRelationshipFields = isDatasetCatalog(dataset)
|
|
142
165
|
? []
|
|
143
166
|
: getQueryableRelationshipFields(catalog);
|
|
144
|
-
return z.object(queryShape(catalog, metricName, options, metric, localRelationshipFields)).strict();
|
|
167
|
+
return z.object(queryShape(catalog, metricName, options, relationshipFilterPolicy(dataset), metric, localRelationshipFields)).strict();
|
|
145
168
|
}
|
|
146
169
|
function unionSchemas(schemas, emptyShape) {
|
|
147
170
|
if (schemas.length === 0)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql-portability.d.ts","sourceRoot":"","sources":["../src/sql-portability.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,MAAM,MAAM,uBAAuB,GAC/B,oBAAoB,GACpB,kCAAkC,GAClC,kCAAkC,GAClC,iCAAiC,GACjC,gCAAgC,GAChC,yBAAyB,GACzB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEjF,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,oBAAoB,CAKtE,CAAC;
|
|
1
|
+
{"version":3,"file":"sql-portability.d.ts","sourceRoot":"","sources":["../src/sql-portability.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,kBAAkB,EAExB,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;GAOG;AAEH,MAAM,MAAM,uBAAuB,GAC/B,oBAAoB,GACpB,kCAAkC,GAClC,kCAAkC,GAClC,iCAAiC,GACjC,gCAAgC,GAChC,yBAAyB,GACzB,uBAAuB,CAAC;AAE5B,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACjD;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;IACxC,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,SAAS,mBAAmB,EAAE,CAAC;CACjD;AAED,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEjF,eAAO,MAAM,8BAA8B,EAAE,QAAQ,CAAC,oBAAoB,CAKtE,CAAC;AAi0BL;;;;GAIG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,qBAA0B,GAClC,oBAAoB,CAyBtB"}
|
package/dist/sql-portability.js
CHANGED
|
@@ -141,6 +141,20 @@ function tokenize(sql) {
|
|
|
141
141
|
if (current === '\\') {
|
|
142
142
|
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'Backslash escapes are not portable; double the quote instead.', index, index + 1);
|
|
143
143
|
}
|
|
144
|
+
const code = current.charCodeAt(0);
|
|
145
|
+
if ((code <= 0x1f && code !== 0x09 && code !== 0x0a && code !== 0x0d)
|
|
146
|
+
|| (code >= 0x7f && code <= 0x9f)) {
|
|
147
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'String literals cannot contain control characters.', index, index + 1);
|
|
148
|
+
}
|
|
149
|
+
if (code >= 0xd800 && code <= 0xdfff) {
|
|
150
|
+
const low = sql.charCodeAt(index + 1);
|
|
151
|
+
if (code > 0xdbff || !(low >= 0xdc00 && low <= 0xdfff)) {
|
|
152
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_LITERAL', 'String literals must be well-formed Unicode.', index, index + 1);
|
|
153
|
+
}
|
|
154
|
+
value += current + sql[index + 1];
|
|
155
|
+
index += 2;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
144
158
|
value += current;
|
|
145
159
|
index += 1;
|
|
146
160
|
}
|
|
@@ -222,7 +236,9 @@ function tokenize(sql) {
|
|
|
222
236
|
}
|
|
223
237
|
continue;
|
|
224
238
|
}
|
|
225
|
-
|
|
239
|
+
// Report a whole code point: an astral character is two UTF-16 code units.
|
|
240
|
+
const unexpected = String.fromCodePoint(sql.codePointAt(index));
|
|
241
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_SYNTAX', `Unexpected character "${unexpected}".`, start, start + unexpected.length);
|
|
226
242
|
}
|
|
227
243
|
return tokens;
|
|
228
244
|
}
|
|
@@ -260,12 +276,32 @@ const COMPARISON_OPERATORS = Object.freeze({
|
|
|
260
276
|
function node(expression) {
|
|
261
277
|
return Object.freeze(expression);
|
|
262
278
|
}
|
|
279
|
+
/**
|
|
280
|
+
* RFC 0003 caps the operands of one AND/OR node at the expression validator's
|
|
281
|
+
* collection limit.
|
|
282
|
+
*/
|
|
283
|
+
const MAX_LOGICAL_OPERANDS = 100;
|
|
284
|
+
function childExpressions(expression) {
|
|
285
|
+
switch (expression.kind) {
|
|
286
|
+
case 'binary':
|
|
287
|
+
case 'comparison':
|
|
288
|
+
return [expression.left, expression.right];
|
|
289
|
+
case 'logical':
|
|
290
|
+
return 'operands' in expression ? expression.operands : [expression.operand];
|
|
291
|
+
case 'call':
|
|
292
|
+
return expression.args;
|
|
293
|
+
default:
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
}
|
|
263
297
|
class Parser {
|
|
264
298
|
tokens;
|
|
265
299
|
limits;
|
|
266
300
|
position = 0;
|
|
267
301
|
nodes = 0;
|
|
268
302
|
dependencies = new Set();
|
|
303
|
+
/** Tree levels beneath each composite node, counting it; leaves are 1. */
|
|
304
|
+
levels = new WeakMap();
|
|
269
305
|
constructor(tokens, limits) {
|
|
270
306
|
this.tokens = tokens;
|
|
271
307
|
this.limits = limits;
|
|
@@ -303,38 +339,67 @@ class Parser {
|
|
|
303
339
|
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its depth limit.', token.start, token.end);
|
|
304
340
|
}
|
|
305
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Freeze a composite node, rejecting it once its tree has more levels than
|
|
344
|
+
* the depth limit (RFC 0003 counts the root as depth 1). Parse depth alone
|
|
345
|
+
* undercounts left-associative chains such as `a + b + c`, whose left spine
|
|
346
|
+
* grows with every operator.
|
|
347
|
+
*/
|
|
348
|
+
build(expression, token) {
|
|
349
|
+
let levels = 1;
|
|
350
|
+
for (const child of childExpressions(expression)) {
|
|
351
|
+
levels = Math.max(levels, (this.levels.get(child) ?? 1) + 1);
|
|
352
|
+
}
|
|
353
|
+
if (levels > this.limits.maxDepth) {
|
|
354
|
+
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its depth limit.', token.start, token.end);
|
|
355
|
+
}
|
|
356
|
+
const frozen = node(expression);
|
|
357
|
+
this.levels.set(frozen, levels);
|
|
358
|
+
return frozen;
|
|
359
|
+
}
|
|
360
|
+
/** Called after consuming AND/OR, before parsing the operand it introduces. */
|
|
361
|
+
rejectExtraOperand(operands) {
|
|
362
|
+
if (operands.length >= MAX_LOGICAL_OPERANDS) {
|
|
363
|
+
const token = this.tokens[this.position - 1];
|
|
364
|
+
fail('HQ_SQL_PORT_TOO_COMPLEX', 'The expression exceeds its operand limit.', token.start, token.end);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
306
367
|
parseOr(depth) {
|
|
307
368
|
const operands = [this.parseAnd(depth)];
|
|
308
369
|
while (this.peek()?.type === 'keyword' && this.peek().value === 'or') {
|
|
309
370
|
this.next();
|
|
371
|
+
this.rejectExtraOperand(operands);
|
|
310
372
|
operands.push(this.parseAnd(depth));
|
|
311
373
|
}
|
|
312
374
|
if (operands.length === 1)
|
|
313
375
|
return operands[0];
|
|
314
|
-
|
|
315
|
-
|
|
376
|
+
const token = this.tokens[this.position - 1];
|
|
377
|
+
this.enter(depth, token);
|
|
378
|
+
return this.build({ kind: 'logical', operator: 'or', operands: Object.freeze(operands) }, token);
|
|
316
379
|
}
|
|
317
380
|
parseAnd(depth) {
|
|
318
381
|
const operands = [this.parseNot(depth)];
|
|
319
382
|
while (this.peek()?.type === 'keyword' && this.peek().value === 'and') {
|
|
320
383
|
this.next();
|
|
384
|
+
this.rejectExtraOperand(operands);
|
|
321
385
|
operands.push(this.parseNot(depth));
|
|
322
386
|
}
|
|
323
387
|
if (operands.length === 1)
|
|
324
388
|
return operands[0];
|
|
325
|
-
|
|
326
|
-
|
|
389
|
+
const token = this.tokens[this.position - 1];
|
|
390
|
+
this.enter(depth, token);
|
|
391
|
+
return this.build({ kind: 'logical', operator: 'and', operands: Object.freeze(operands) }, token);
|
|
327
392
|
}
|
|
328
393
|
parseNot(depth) {
|
|
329
394
|
const token = this.peek();
|
|
330
395
|
if (token?.type === 'keyword' && token.value === 'not') {
|
|
331
396
|
this.next();
|
|
332
397
|
this.enter(depth + 1, token);
|
|
333
|
-
return
|
|
398
|
+
return this.build({
|
|
334
399
|
kind: 'logical',
|
|
335
400
|
operator: 'not',
|
|
336
401
|
operand: this.parseNot(depth + 1),
|
|
337
|
-
});
|
|
402
|
+
}, token);
|
|
338
403
|
}
|
|
339
404
|
return this.parseComparison(depth);
|
|
340
405
|
}
|
|
@@ -349,14 +414,17 @@ class Parser {
|
|
|
349
414
|
const right = this.parseAdditive(depth);
|
|
350
415
|
this.enter(depth + 1, token);
|
|
351
416
|
this.rejectChained();
|
|
352
|
-
return
|
|
417
|
+
return this.build({ kind: 'comparison', operator, left, right }, token);
|
|
353
418
|
}
|
|
354
419
|
if (token.type === 'keyword' && token.value === 'like') {
|
|
355
420
|
this.next();
|
|
356
421
|
const right = this.parseAdditive(depth);
|
|
357
422
|
this.enter(depth + 1, token);
|
|
358
423
|
this.rejectChained();
|
|
359
|
-
|
|
424
|
+
if (right.kind !== 'literal' || typeof right.value !== 'string') {
|
|
425
|
+
fail('HQ_SQL_PORT_UNSUPPORTED_SYNTAX', 'LIKE patterns must be string literals.', token.start, token.end);
|
|
426
|
+
}
|
|
427
|
+
return this.build({ kind: 'comparison', operator: 'like', left, right }, token);
|
|
360
428
|
}
|
|
361
429
|
if (token.type === 'keyword' && token.value === 'between') {
|
|
362
430
|
this.next();
|
|
@@ -369,12 +437,12 @@ class Parser {
|
|
|
369
437
|
const upper = this.parseAdditive(depth);
|
|
370
438
|
this.enter(depth + 1, token);
|
|
371
439
|
this.rejectChained();
|
|
372
|
-
return
|
|
440
|
+
return this.build({
|
|
373
441
|
kind: 'comparison',
|
|
374
442
|
operator: 'between',
|
|
375
443
|
left,
|
|
376
444
|
right: this.tupleLiteral(lower, upper, token),
|
|
377
|
-
});
|
|
445
|
+
}, token);
|
|
378
446
|
}
|
|
379
447
|
if (token.type === 'keyword' && (token.value === 'in' || token.value === 'not')) {
|
|
380
448
|
this.next();
|
|
@@ -449,7 +517,7 @@ class Parser {
|
|
|
449
517
|
}
|
|
450
518
|
this.enter(depth + 1, token);
|
|
451
519
|
this.rejectChained();
|
|
452
|
-
return
|
|
520
|
+
return this.build({
|
|
453
521
|
kind: 'comparison',
|
|
454
522
|
operator,
|
|
455
523
|
left,
|
|
@@ -463,7 +531,7 @@ class Parser {
|
|
|
463
531
|
},
|
|
464
532
|
},
|
|
465
533
|
},
|
|
466
|
-
});
|
|
534
|
+
}, token);
|
|
467
535
|
}
|
|
468
536
|
parseAdditive(depth) {
|
|
469
537
|
let left = this.parseMultiplicative(depth);
|
|
@@ -474,12 +542,12 @@ class Parser {
|
|
|
474
542
|
this.next();
|
|
475
543
|
const right = this.parseMultiplicative(depth + 1);
|
|
476
544
|
this.enter(depth + 1, token);
|
|
477
|
-
left =
|
|
545
|
+
left = this.build({
|
|
478
546
|
kind: 'binary',
|
|
479
547
|
operator: BINARY_OPERATORS[token.value],
|
|
480
548
|
left,
|
|
481
549
|
right,
|
|
482
|
-
});
|
|
550
|
+
}, token);
|
|
483
551
|
}
|
|
484
552
|
return left;
|
|
485
553
|
}
|
|
@@ -492,12 +560,12 @@ class Parser {
|
|
|
492
560
|
this.next();
|
|
493
561
|
const right = this.parseUnary(depth + 1);
|
|
494
562
|
this.enter(depth + 1, token);
|
|
495
|
-
left =
|
|
563
|
+
left = this.build({
|
|
496
564
|
kind: 'binary',
|
|
497
565
|
operator: BINARY_OPERATORS[token.value],
|
|
498
566
|
left,
|
|
499
567
|
right,
|
|
500
|
-
});
|
|
568
|
+
}, token);
|
|
501
569
|
}
|
|
502
570
|
return left;
|
|
503
571
|
}
|
|
@@ -514,6 +582,7 @@ class Parser {
|
|
|
514
582
|
fail('HQ_SQL_PORT_UNSUPPORTED_OPERATOR', 'Negation is only portable on numeric literals.', token.start, token.end);
|
|
515
583
|
}
|
|
516
584
|
this.next();
|
|
585
|
+
this.enter(depth, operand);
|
|
517
586
|
return this.numericLiteral(operand, true);
|
|
518
587
|
}
|
|
519
588
|
return this.parsePrimary(depth);
|
|
@@ -595,11 +664,11 @@ class Parser {
|
|
|
595
664
|
if (args.length < arity[0] || args.length > arity[1]) {
|
|
596
665
|
fail('HQ_SQL_PORT_SYNTAX', `Function "${canonical}" expects ${arity[0] === arity[1] ? arity[0] : `${arity[0]} to ${arity[1]}`} arguments.`, name.start, close.end);
|
|
597
666
|
}
|
|
598
|
-
return
|
|
667
|
+
return this.build({
|
|
599
668
|
kind: 'call',
|
|
600
669
|
function: canonical,
|
|
601
670
|
args: Object.freeze(args),
|
|
602
|
-
});
|
|
671
|
+
}, name);
|
|
603
672
|
}
|
|
604
673
|
numericLiteral(token, negated) {
|
|
605
674
|
const value = Number(token.value) * (negated ? -1 : 1);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collect-cloud-datasets.d.ts","sourceRoot":"","sources":["../../src/utils/collect-cloud-datasets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACzC,OAAO,EAAE,kBAAkB,GAC1B,IAAI,CAUN"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function collectCloudDatasets(datasets, dataset) {
|
|
2
|
+
const existing = datasets.get(dataset.name);
|
|
3
|
+
if (existing && existing !== dataset) {
|
|
4
|
+
throw new Error(`Multiple Dataset definitions use the name "${dataset.name}".`);
|
|
5
|
+
}
|
|
6
|
+
if (existing)
|
|
7
|
+
return;
|
|
8
|
+
datasets.set(dataset.name, dataset);
|
|
9
|
+
for (const relationship of Object.values(dataset.relationships)) {
|
|
10
|
+
collectCloudDatasets(datasets, relationship.target());
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filtered-aggregation-sql.d.ts","sourceRoot":"","sources":["../../src/utils/filtered-aggregation-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAgB,MAAM,aAAa,CAAC;AAErF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAEjF,KAAK,YAAY,GAAG,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"filtered-aggregation-sql.d.ts","sourceRoot":"","sources":["../../src/utils/filtered-aggregation-sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,kBAAkB,EAAgB,MAAM,aAAa,CAAC;AAErF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAEjF,KAAK,YAAY,GAAG,kBAAkB,CAAC;AAqEvC,wBAAgB,kCAAkC,CAChD,EAAE,EAAE,YAAY,EAChB,IAAI,EAAE,eAAe,EACrB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,0BAA0B,GACnC,MAAM,CA+BR"}
|
|
@@ -4,7 +4,10 @@ function renderMeasureFilterLiteral(value) {
|
|
|
4
4
|
return 'NULL';
|
|
5
5
|
}
|
|
6
6
|
if (typeof value === 'string') {
|
|
7
|
-
|
|
7
|
+
// ClickHouse also treats backslash as a string escape. Doubling quotes
|
|
8
|
+
// without escaping backslashes lets a value ending in \\ before a quote
|
|
9
|
+
// break out of the literal and change the predicate.
|
|
10
|
+
return `'${value.replace(/\\/g, '\\\\').replace(/'/g, "''")}'`;
|
|
8
11
|
}
|
|
9
12
|
if (typeof value === 'number') {
|
|
10
13
|
if (!Number.isFinite(value)) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-fields.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAgBD,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAM7E;AAED,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;IACrC,4CAA4C;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,mBAAmB,CAAC;IACrC,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,kBAAkB,EACtB,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"relationship-fields.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACvB,MAAM,aAAa,CAAC;AAErB;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAED;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,sBAAsB,GACnC,MAAM,EAAE,CAEV;AAgBD,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,KAAK,EAAE,MAAM,CAAC;CACf;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAM7E;AAED,MAAM,WAAW,sBAAsB;IACrC,8EAA8E;IAC9E,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,sBAAsB,CAAC;IACrC,4CAA4C;IAC5C,MAAM,EAAE,kBAAkB,CAAC;IAC3B,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,mBAAmB,CAAC;IACrC,oDAAoD;IACpD,YAAY,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,wBAAwB,GAChC;IAAE,QAAQ,EAAE,sBAAsB,CAAC;IAAC,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,GACvD;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,kBAAkB,EACtB,IAAI,EAAE,MAAM,GACX,wBAAwB,GAAG,IAAI,CA2DjC"}
|
|
@@ -64,7 +64,9 @@ export function resolveQualifiedField(ds, name) {
|
|
|
64
64
|
return null;
|
|
65
65
|
}
|
|
66
66
|
const { relationship: relationshipName, field } = parsed;
|
|
67
|
-
const relationship = ds.relationships
|
|
67
|
+
const relationship = Object.hasOwn(ds.relationships, relationshipName)
|
|
68
|
+
? ds.relationships[relationshipName]
|
|
69
|
+
: undefined;
|
|
68
70
|
if (!relationship) {
|
|
69
71
|
const available = Object.keys(ds.relationships);
|
|
70
72
|
return {
|
|
@@ -84,7 +86,9 @@ export function resolveQualifiedField(ds, name) {
|
|
|
84
86
|
};
|
|
85
87
|
}
|
|
86
88
|
const target = relationship.target();
|
|
87
|
-
const targetDimension = target.dimensions
|
|
89
|
+
const targetDimension = Object.hasOwn(target.dimensions, field)
|
|
90
|
+
? target.dimensions[field]
|
|
91
|
+
: undefined;
|
|
88
92
|
if (!targetDimension) {
|
|
89
93
|
const available = Object.keys(target.dimensions);
|
|
90
94
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"relationship-validation.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAWrB,KAAK,iBAAiB,GAAG,IAAI,CAC3B,YAAY,GAAG,WAAW,EAC1B,YAAY,GAAG,SAAS,GAAG,SAAS,CACrC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,iBAAiB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAmBpB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,kBAAkB,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"relationship-validation.d.ts","sourceRoot":"","sources":["../../src/utils/relationship-validation.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACZ,MAAM,aAAa,CAAC;AAWrB,KAAK,iBAAiB,GAAG,IAAI,CAC3B,YAAY,GAAG,WAAW,EAC1B,YAAY,GAAG,SAAS,GAAG,SAAS,CACrC,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,EAAE,EAAE,kBAAkB,EACtB,KAAK,EAAE,iBAAiB,EACxB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,SAAS,CAmBpB;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,EAAE,EAAE,kBAAkB,EACtB,MAAM,EAAE,YAAY,EACpB,OAAO,CAAC,EAAE,gBAAgB,GACzB,MAAM,GAAG,IAAI,CA4Bf"}
|
|
@@ -43,9 +43,20 @@ export function validateQualifiedFilter(ds, filter, context) {
|
|
|
43
43
|
if (!resolution.resolved) {
|
|
44
44
|
return resolution.error;
|
|
45
45
|
}
|
|
46
|
-
const { target, targetColumn, targetDimension } = resolution.resolved;
|
|
46
|
+
const { target, targetColumn, targetDimension, targetDimensionName } = resolution.resolved;
|
|
47
47
|
if (getRuntimeTenantPredicate(context) && target.tenantKey && targetColumn === target.tenantKey) {
|
|
48
48
|
return `Cannot filter on tenant field "${filter.field}" when runtime tenancy enforcement is active.`;
|
|
49
49
|
}
|
|
50
|
+
// A relationship does not turn every target dimension into an exposed
|
|
51
|
+
// filter. Apply the target's explicit or generated filter allowlist and
|
|
52
|
+
// operator policy. An explicit filter can override filterable: false.
|
|
53
|
+
const definition = target.filters[targetDimensionName];
|
|
54
|
+
if (!definition
|
|
55
|
+
|| definition.field !== targetDimensionName) {
|
|
56
|
+
return `Filter "${filter.field}" is not exposed by target dataset "${target.name}".`;
|
|
57
|
+
}
|
|
58
|
+
if (definition.operators && !definition.operators.includes(filter.operator)) {
|
|
59
|
+
return `Filter "${filter.field}" does not allow operator "${filter.operator}".`;
|
|
60
|
+
}
|
|
50
61
|
return validateFilterValue(filter, targetDimension.fieldType);
|
|
51
62
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hypequery/datasets",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Code-first TypeScript semantic layer for ClickHouse datasets, metrics, multi-tenancy, and AI agents",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"semantic-layer",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"dist"
|
|
43
43
|
],
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@hypequery/protocol": "^0.
|
|
45
|
+
"@hypequery/protocol": "^0.15.0",
|
|
46
46
|
"@noble/hashes": "^1.8.0",
|
|
47
47
|
"zod": "^3.22.4",
|
|
48
48
|
"zod-to-json-schema": "^3.23.5"
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"typescript": "^5.7.3",
|
|
61
61
|
"@vitest/coverage-v8": "^3.2.6",
|
|
62
62
|
"vitest": "^3.2.6",
|
|
63
|
-
"@hypequery/protocol-conformance": "^0.
|
|
63
|
+
"@hypequery/protocol-conformance": "^0.12.0"
|
|
64
64
|
},
|
|
65
65
|
"repository": {
|
|
66
66
|
"type": "git",
|