@malloydata/malloy 0.0.428 → 0.0.430
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/foundation/build_targets.d.ts +50 -0
- package/dist/api/foundation/build_targets.js +221 -0
- package/dist/api/foundation/core.d.ts +54 -10
- package/dist/api/foundation/core.js +80 -17
- package/dist/api/foundation/index.d.ts +1 -1
- package/dist/api/foundation/runtime.d.ts +18 -1
- package/dist/api/foundation/runtime.js +35 -0
- package/dist/api/foundation/types.d.ts +93 -6
- package/dist/index.d.ts +1 -1
- package/dist/lang/ast/field-space/dynamic-space.js +20 -3
- package/dist/lang/ast/query-elements/query-arrow.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-arrow.js +10 -4
- package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-base.js +1 -6
- package/dist/lang/ast/query-elements/query-raw.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-raw.js +4 -2
- package/dist/lang/ast/query-elements/query-reference.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-reference.js +9 -3
- package/dist/lang/ast/query-elements/query-refine.d.ts +1 -1
- package/dist/lang/ast/query-elements/query-refine.js +10 -4
- package/dist/lang/ast/source-elements/query-source.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +2 -2
- package/dist/lang/ast/source-query-elements/source-query-element.d.ts +27 -0
- package/dist/lang/ast/source-query-elements/source-query-element.js +33 -0
- package/dist/lang/ast/source-query-elements/sq-arrow.d.ts +2 -0
- package/dist/lang/ast/source-query-elements/sq-arrow.js +12 -6
- package/dist/lang/ast/source-query-elements/sq-refine.d.ts +2 -0
- package/dist/lang/ast/source-query-elements/sq-refine.js +12 -6
- package/dist/lang/ast/source-query-elements/sq-source.d.ts +1 -0
- package/dist/lang/ast/source-query-elements/sq-source.js +6 -3
- package/dist/lang/ast/sql-elements/sql-string.d.ts +1 -1
- package/dist/lang/ast/sql-elements/sql-string.js +25 -5
- package/dist/lang/ast/statements/import-statement.js +7 -10
- package/dist/lang/ast/types/query-element.d.ts +15 -1
- package/dist/lang/parse-log.d.ts +2 -0
- package/dist/lang/test/test-translator.d.ts +17 -0
- package/dist/lang/test/test-translator.js +46 -2
- package/dist/model/persist_utils.d.ts +61 -10
- package/dist/model/persist_utils.js +172 -63
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { EventStream } from '../../runtime_types';
|
|
2
|
-
import type { BuildManifest, GivenValue, VirtualMap } from '../../model';
|
|
2
|
+
import type { BuildID, BuildManifest, GivenValue, VirtualMap } from '../../model';
|
|
3
|
+
import type { LogMessage } from '../../lang';
|
|
4
|
+
import type { PersistSource } from './core';
|
|
3
5
|
/**
|
|
4
6
|
* An empty BuildManifest with no entries and strict mode off.
|
|
5
7
|
* Use this to explicitly suppress manifest substitution in a query:
|
|
@@ -57,6 +59,12 @@ export interface CompileQueryOptions {
|
|
|
57
59
|
/**
|
|
58
60
|
* A node in the build graph (recursive DAG structure).
|
|
59
61
|
* Uses sourceID (sourceName@modelURL) for identity.
|
|
62
|
+
*
|
|
63
|
+
* A node reached twice is the *same object* both times, so the graph shares
|
|
64
|
+
* nodes rather than copying them. Anything walking it needs a seen-set on node
|
|
65
|
+
* identity, or a chain of diamonds is exponential.
|
|
66
|
+
*
|
|
67
|
+
* @deprecated Produced only by {@link BuildPlan}. Use `BuildTarget`.
|
|
60
68
|
*/
|
|
61
69
|
export interface BuildNode {
|
|
62
70
|
/** Source identity: "sourceName@modelURL" */
|
|
@@ -67,15 +75,94 @@ export interface BuildNode {
|
|
|
67
75
|
/**
|
|
68
76
|
* An ordered build plan for sources on a single connection.
|
|
69
77
|
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
78
|
+
* `nodes` is typed for a leveled schedule that was never produced — it always
|
|
79
|
+
* holds exactly one entry, the roots, with the real ordering in each node's
|
|
80
|
+
* `dependsOn`.
|
|
72
81
|
*
|
|
73
|
-
*
|
|
74
|
-
* different database connections.
|
|
82
|
+
* @deprecated Use `BuildTargets`, which reports tables rather than sources.
|
|
75
83
|
*/
|
|
76
84
|
export interface BuildGraph {
|
|
77
85
|
/** The connection all sources in this graph run on */
|
|
78
86
|
connectionName: string;
|
|
79
|
-
/**
|
|
87
|
+
/** Always one entry: the root nodes */
|
|
80
88
|
nodes: BuildNode[][];
|
|
81
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* One artifact: a table to build, and every source that maps onto it.
|
|
92
|
+
*
|
|
93
|
+
* A `BuildNode` is a source; a `BuildTarget` is a table. The two are not the
|
|
94
|
+
* same count. `#@ persist` is an annotation, so extending or renaming a
|
|
95
|
+
* persisted source inherits it — while `extend` never changes the source's
|
|
96
|
+
* SQL — and several sources routinely name one table. The manifest is keyed by
|
|
97
|
+
* `BuildID`, so those sources share one entry no matter how many plan nodes
|
|
98
|
+
* they occupy. `Runtime.getBuildTargets()` does that merge once, in the core,
|
|
99
|
+
* instead of leaving each builder to discover it by hashing.
|
|
100
|
+
*/
|
|
101
|
+
export interface BuildTarget {
|
|
102
|
+
/** Manifest key for this artifact: a hash of the connection digest and `sql` */
|
|
103
|
+
buildId: BuildID;
|
|
104
|
+
/** The connection this artifact is built on */
|
|
105
|
+
connectionName: string;
|
|
106
|
+
/**
|
|
107
|
+
* The SQL the BuildID is computed from: fully inlined, no manifest
|
|
108
|
+
* substitution, so it is the same string whatever else has been built.
|
|
109
|
+
*
|
|
110
|
+
* It is not the SQL to execute. That one substitutes the tables built so far
|
|
111
|
+
* — `source.getSQL({buildManifest, connectionDigests})` — so it can only be
|
|
112
|
+
* computed as the build walks, and only by the builder holding the manifest.
|
|
113
|
+
*/
|
|
114
|
+
sql: string;
|
|
115
|
+
/** Targets that must exist before this one can be built */
|
|
116
|
+
dependsOn: BuildTarget[];
|
|
117
|
+
/** Every persist source in the model that maps onto this artifact */
|
|
118
|
+
sources: PersistSource[];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Everything one connection has to build.
|
|
122
|
+
*
|
|
123
|
+
* `targets` is in dependency order — everything a target depends on appears
|
|
124
|
+
* before it — so the simplest correct builder is a loop:
|
|
125
|
+
*
|
|
126
|
+
* ```typescript
|
|
127
|
+
* for (const target of targets) await build(target);
|
|
128
|
+
* ```
|
|
129
|
+
*
|
|
130
|
+
* A builder that wants concurrency uses `target.dependsOn` and starts each one
|
|
131
|
+
* the moment its own dependencies finish:
|
|
132
|
+
*
|
|
133
|
+
* ```typescript
|
|
134
|
+
* const done = new Map<BuildTarget, Promise<void>>();
|
|
135
|
+
* for (const target of targets) {
|
|
136
|
+
* done.set(target, (async () => {
|
|
137
|
+
* await Promise.all(target.dependsOn.map(d => done.get(d)));
|
|
138
|
+
* await build(target);
|
|
139
|
+
* })());
|
|
140
|
+
* }
|
|
141
|
+
* await Promise.all(done.values());
|
|
142
|
+
* ```
|
|
143
|
+
*
|
|
144
|
+
* That is the maximum available: nothing waits for anything it does not read.
|
|
145
|
+
* Batching the targets into rounds instead — everything at depth 0, then
|
|
146
|
+
* everything at depth 1 — is easier to write and strictly worse, because a
|
|
147
|
+
* target then waits on unrelated work that happens to share its depth.
|
|
148
|
+
*/
|
|
149
|
+
export interface ConnectionBuild {
|
|
150
|
+
/** The connection everything here is built on */
|
|
151
|
+
connectionName: string;
|
|
152
|
+
/** Every table to build, dependencies before dependents */
|
|
153
|
+
targets: BuildTarget[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* The build schedule for a model.
|
|
157
|
+
*
|
|
158
|
+
* Connections come first because they are the largest cut of parallelism: a
|
|
159
|
+
* query cannot cross a connection, so no dependency ever does either, and each
|
|
160
|
+
* entry here is a wholly independent build that needs no coordination with any
|
|
161
|
+
* other.
|
|
162
|
+
*/
|
|
163
|
+
export interface BuildTargets {
|
|
164
|
+
/** One per connection, mutually independent */
|
|
165
|
+
connections: ConnectionBuild[];
|
|
166
|
+
/** Errors and warnings from parsing `#@` annotations on persistable sources */
|
|
167
|
+
tagParseLog: LogMessage[];
|
|
168
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export { routeOf, payloadOf, annotationsForRoute, tagFromAnnotations, } from './
|
|
|
25
25
|
/** @deprecated — use the `.annotations` view on a Foundation entity
|
|
26
26
|
* (`entity.annotations.parseAsTag(route)` / `.texts(route)`). */
|
|
27
27
|
export { annotationToTag, annotationToTaglines, } from './api/foundation/annotation';
|
|
28
|
-
export type { BuildGraph, BuildNode, BuildPlan } from './api/foundation';
|
|
28
|
+
export type { BuildGraph, BuildNode, BuildPlan, BuildTarget, BuildTargets, ConnectionBuild, } from './api/foundation';
|
|
29
29
|
export { PersistSource, EMPTY_BUILD_MANIFEST } from './api/foundation';
|
|
30
30
|
export { Reference } from './api/foundation';
|
|
31
31
|
export type { ReferenceKind } from './api/foundation';
|
|
@@ -114,10 +114,17 @@ class DynamicSpace extends static_space_1.StaticSpace {
|
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
this.sourceDef = { ...this.fromSource, fields: [] };
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
//
|
|
117
|
+
// A freshly built (modified) source presents a new exported shape, so it
|
|
118
|
+
// is neither a reference to what it was built from nor that source's own
|
|
119
|
+
// definition. Both identities go; `DefineSource` stamps fresh ones if
|
|
120
|
+
// this ends up with a name, and an inline `extend` never does.
|
|
121
|
+
//
|
|
122
|
+
// Dropping `sourceID` also means the persistence walk descends *through*
|
|
123
|
+
// an unnamed modification rather than resolving it by id — which is how
|
|
124
|
+
// an inline `extend` used as a query's structRef stopped hiding the
|
|
125
|
+
// sources it added.
|
|
120
126
|
delete this.sourceDef.referenceID;
|
|
127
|
+
delete this.sourceDef.sourceID;
|
|
121
128
|
this.sourceDef.parameters = parameters;
|
|
122
129
|
const fieldIndices = new Map();
|
|
123
130
|
// Need to process the entities in specific order
|
|
@@ -141,6 +148,16 @@ class DynamicSpace extends static_space_1.StaticSpace {
|
|
|
141
148
|
if (field instanceof join_space_field_1.JoinSpaceField) {
|
|
142
149
|
const joinStruct = field.join.getStructDef(parameterSpace);
|
|
143
150
|
if (!error_factory_1.ErrorFactory.didCreate(joinStruct)) {
|
|
151
|
+
// A query runs on one connection, so everything reachable from
|
|
152
|
+
// the source it runs on must live on that connection. Record and
|
|
153
|
+
// array joins are part of the row, and so have no connection.
|
|
154
|
+
// A base whose schema could not be fetched has the error
|
|
155
|
+
// connection, which is not worth complaining about twice.
|
|
156
|
+
if (model.isSourceDef(joinStruct) &&
|
|
157
|
+
!error_factory_1.ErrorFactory.didCreate(this.fromSource) &&
|
|
158
|
+
joinStruct.connection !== this.connectionName()) {
|
|
159
|
+
field.join.sourceExpr.logError('join-connection-mismatch', `Cannot join '${name}', which is on connection '${joinStruct.connection}', into a source on connection '${this.connectionName()}'`);
|
|
160
|
+
}
|
|
144
161
|
fieldIndices.set(name, this.sourceDef.fields.length);
|
|
145
162
|
this.sourceDef.fields.push(joinStruct);
|
|
146
163
|
field.join.fixupJoinOn(this, joinStruct);
|
|
@@ -13,5 +13,5 @@ export declare class QueryArrow extends QueryBase implements QueryElement {
|
|
|
13
13
|
readonly view: View;
|
|
14
14
|
elementType: string;
|
|
15
15
|
constructor(source: Source | QueryElement, view: View);
|
|
16
|
-
queryComp(isRefOk: boolean): QueryComp;
|
|
16
|
+
queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
|
|
17
17
|
}
|
|
@@ -10,6 +10,7 @@ const source_1 = require("../source-elements/source");
|
|
|
10
10
|
const static_space_1 = require("../field-space/static-space");
|
|
11
11
|
const query_base_1 = require("./query-base");
|
|
12
12
|
const composite_source_utils_1 = require("../../composite-source-utils");
|
|
13
|
+
const query_utils_1 = require("../query-utils");
|
|
13
14
|
/**
|
|
14
15
|
* A query operation that adds segments to a LHS source or query.
|
|
15
16
|
*
|
|
@@ -22,7 +23,7 @@ class QueryArrow extends query_base_1.QueryBase {
|
|
|
22
23
|
this.view = view;
|
|
23
24
|
this.elementType = 'arrow';
|
|
24
25
|
}
|
|
25
|
-
queryComp(isRefOk) {
|
|
26
|
+
queryComp(isRefOk, isPartialOk) {
|
|
26
27
|
var _a;
|
|
27
28
|
let inputStruct;
|
|
28
29
|
let queryBase;
|
|
@@ -46,7 +47,9 @@ class QueryArrow extends query_base_1.QueryBase {
|
|
|
46
47
|
}
|
|
47
48
|
else {
|
|
48
49
|
// We are adding a second stage to the given "source" query; we get the query and add a segment
|
|
49
|
-
|
|
50
|
+
// The LHS stages are complete stages, nothing will refine them, so they
|
|
51
|
+
// are never allowed to be partial no matter what this caller accepts.
|
|
52
|
+
const lhsQuery = this.source.queryComp(isRefOk, false);
|
|
50
53
|
queryBase = lhsQuery.query;
|
|
51
54
|
inputStruct = lhsQuery.outputStruct;
|
|
52
55
|
fieldSpace = new static_space_1.StaticSourceSpace(lhsQuery.outputStruct, 'public');
|
|
@@ -77,12 +80,15 @@ class QueryArrow extends query_base_1.QueryBase {
|
|
|
77
80
|
: // Otherwise just use the `inputStruct`
|
|
78
81
|
inputStruct, rhsPipeline),
|
|
79
82
|
];
|
|
83
|
+
const finalPipeline = isPartialOk
|
|
84
|
+
? pipelineWithExpandedFieldUsage
|
|
85
|
+
: (0, query_utils_1.detectAndRemovePartialStages)(pipelineWithExpandedFieldUsage, this);
|
|
80
86
|
return {
|
|
81
87
|
query: {
|
|
82
88
|
...query,
|
|
83
89
|
compositeResolvedSourceDef,
|
|
84
|
-
pipeline:
|
|
85
|
-
givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(
|
|
90
|
+
pipeline: finalPipeline,
|
|
91
|
+
givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(finalPipeline),
|
|
86
92
|
},
|
|
87
93
|
outputStruct,
|
|
88
94
|
inputStruct,
|
|
@@ -2,7 +2,7 @@ import { type PipeSegment, type Query, type SourceDef } from '../../../model/mal
|
|
|
2
2
|
import { MalloyElement } from '../types/malloy-element';
|
|
3
3
|
import type { QueryComp } from '../types/query-comp';
|
|
4
4
|
export declare abstract class QueryBase extends MalloyElement {
|
|
5
|
-
abstract queryComp(isRefOk: boolean): QueryComp;
|
|
5
|
+
abstract queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
|
|
6
6
|
protected expandRefUsage(inputSource: SourceDef, pipeline: PipeSegment[]): PipeSegment[];
|
|
7
7
|
protected resolveCompositeSource(inputSource: SourceDef, pipeline: PipeSegment[]): SourceDef | undefined;
|
|
8
8
|
query(isRefOk?: boolean): Query;
|
|
@@ -8,7 +8,6 @@ exports.QueryBase = void 0;
|
|
|
8
8
|
const composite_source_utils_1 = require("../../composite-source-utils");
|
|
9
9
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
10
10
|
const error_factory_1 = require("../error-factory");
|
|
11
|
-
const query_utils_1 = require("../query-utils");
|
|
12
11
|
const malloy_element_1 = require("../types/malloy-element");
|
|
13
12
|
class QueryBase extends malloy_element_1.MalloyElement {
|
|
14
13
|
expandRefUsage(inputSource, pipeline) {
|
|
@@ -40,11 +39,7 @@ class QueryBase extends malloy_element_1.MalloyElement {
|
|
|
40
39
|
return undefined;
|
|
41
40
|
}
|
|
42
41
|
query(isRefOk = true) {
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
...query,
|
|
46
|
-
pipeline: (0, query_utils_1.detectAndRemovePartialStages)(query.pipeline, this),
|
|
47
|
-
};
|
|
42
|
+
return this.queryComp(isRefOk, false).query;
|
|
48
43
|
}
|
|
49
44
|
}
|
|
50
45
|
exports.QueryBase = QueryBase;
|
|
@@ -15,6 +15,6 @@ export declare class QueryRaw extends MalloyElement implements QueryElement {
|
|
|
15
15
|
readonly source: Source;
|
|
16
16
|
elementType: string;
|
|
17
17
|
constructor(source: Source);
|
|
18
|
-
queryComp(isRefOk: boolean): QueryComp;
|
|
18
|
+
queryComp(isRefOk: boolean, _isPartialOk: boolean): QueryComp;
|
|
19
19
|
query(isRefOk?: boolean): Query;
|
|
20
20
|
}
|
|
@@ -21,7 +21,9 @@ class QueryRaw extends malloy_element_1.MalloyElement {
|
|
|
21
21
|
this.source = source;
|
|
22
22
|
this.elementType = 'query-raw';
|
|
23
23
|
}
|
|
24
|
-
|
|
24
|
+
// A raw segment has no query class to decide, so it is never partial and
|
|
25
|
+
// `isPartialOk` has nothing to say here.
|
|
26
|
+
queryComp(isRefOk, _isPartialOk) {
|
|
25
27
|
const invoked = isRefOk
|
|
26
28
|
? this.source.structRef(undefined)
|
|
27
29
|
: { structRef: this.source.getSourceDef(undefined) };
|
|
@@ -40,7 +42,7 @@ class QueryRaw extends malloy_element_1.MalloyElement {
|
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
query(isRefOk = true) {
|
|
43
|
-
return this.queryComp(isRefOk).query;
|
|
45
|
+
return this.queryComp(isRefOk, false).query;
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
exports.QueryRaw = QueryRaw;
|
|
@@ -12,6 +12,6 @@ export declare class QueryReference extends MalloyElement implements QueryElemen
|
|
|
12
12
|
readonly name: ModelEntryReference;
|
|
13
13
|
elementType: string;
|
|
14
14
|
constructor(name: ModelEntryReference);
|
|
15
|
-
queryComp(isRefOk: boolean): QueryComp;
|
|
15
|
+
queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
|
|
16
16
|
query(isRefOk?: boolean): Query;
|
|
17
17
|
}
|
|
@@ -8,6 +8,7 @@ exports.QueryReference = void 0;
|
|
|
8
8
|
const error_factory_1 = require("../error-factory");
|
|
9
9
|
const malloy_element_1 = require("../types/malloy-element");
|
|
10
10
|
const query_head_struct_1 = require("./query-head-struct");
|
|
11
|
+
const query_utils_1 = require("../query-utils");
|
|
11
12
|
const malloy_types_1 = require("../../../model/malloy_types");
|
|
12
13
|
/**
|
|
13
14
|
* A query operation that is just a reference to an existing query.
|
|
@@ -20,7 +21,7 @@ class QueryReference extends malloy_element_1.MalloyElement {
|
|
|
20
21
|
this.name = name;
|
|
21
22
|
this.elementType = 'query-reference';
|
|
22
23
|
}
|
|
23
|
-
queryComp(isRefOk) {
|
|
24
|
+
queryComp(isRefOk, isPartialOk) {
|
|
24
25
|
const headEntry = this.modelEntry(this.name);
|
|
25
26
|
const query = headEntry === null || headEntry === void 0 ? void 0 : headEntry.entry;
|
|
26
27
|
const oops = function () {
|
|
@@ -45,7 +46,12 @@ class QueryReference extends malloy_element_1.MalloyElement {
|
|
|
45
46
|
? query
|
|
46
47
|
: { ...query, structRef: inputStruct };
|
|
47
48
|
return {
|
|
48
|
-
query:
|
|
49
|
+
query: isPartialOk
|
|
50
|
+
? unRefedQuery
|
|
51
|
+
: {
|
|
52
|
+
...unRefedQuery,
|
|
53
|
+
pipeline: (0, query_utils_1.detectAndRemovePartialStages)(unRefedQuery.pipeline, this),
|
|
54
|
+
},
|
|
49
55
|
outputStruct,
|
|
50
56
|
inputStruct,
|
|
51
57
|
};
|
|
@@ -54,7 +60,7 @@ class QueryReference extends malloy_element_1.MalloyElement {
|
|
|
54
60
|
return oops();
|
|
55
61
|
}
|
|
56
62
|
query(isRefOk = true) {
|
|
57
|
-
return this.queryComp(isRefOk).query;
|
|
63
|
+
return this.queryComp(isRefOk, false).query;
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
exports.QueryReference = QueryReference;
|
|
@@ -12,5 +12,5 @@ export declare class QueryRefine extends QueryBase implements QueryElement {
|
|
|
12
12
|
readonly refinement: View;
|
|
13
13
|
elementType: string;
|
|
14
14
|
constructor(base: QueryElement, refinement: View);
|
|
15
|
-
queryComp(isRefOk: boolean): QueryComp;
|
|
15
|
+
queryComp(isRefOk: boolean, isPartialOk: boolean): QueryComp;
|
|
16
16
|
}
|
|
@@ -8,6 +8,7 @@ exports.QueryRefine = void 0;
|
|
|
8
8
|
const static_space_1 = require("../field-space/static-space");
|
|
9
9
|
const query_base_1 = require("./query-base");
|
|
10
10
|
const composite_source_utils_1 = require("../../composite-source-utils");
|
|
11
|
+
const query_utils_1 = require("../query-utils");
|
|
11
12
|
/**
|
|
12
13
|
* A query operation that consists of an exisitng query with refinements.
|
|
13
14
|
*
|
|
@@ -20,8 +21,10 @@ class QueryRefine extends query_base_1.QueryBase {
|
|
|
20
21
|
this.refinement = refinement;
|
|
21
22
|
this.elementType = 'query-refine';
|
|
22
23
|
}
|
|
23
|
-
queryComp(isRefOk) {
|
|
24
|
-
|
|
24
|
+
queryComp(isRefOk, isPartialOk) {
|
|
25
|
+
// The refinement is what decides the query class, so the base is the one
|
|
26
|
+
// place a partial segment is expected rather than an error.
|
|
27
|
+
const q = this.base.queryComp(isRefOk, true);
|
|
25
28
|
const inputFS = new static_space_1.StaticSourceSpace(q.inputStruct, 'public');
|
|
26
29
|
const pipeline = this.refinement.refine(inputFS, q.query.pipeline, undefined);
|
|
27
30
|
const query = {
|
|
@@ -30,12 +33,15 @@ class QueryRefine extends query_base_1.QueryBase {
|
|
|
30
33
|
};
|
|
31
34
|
const compositeResolvedSourceDef = this.resolveCompositeSource(q.inputStruct, pipeline);
|
|
32
35
|
const pipelineWithExpandedFieldUsage = this.expandRefUsage(compositeResolvedSourceDef !== null && compositeResolvedSourceDef !== void 0 ? compositeResolvedSourceDef : q.inputStruct, pipeline);
|
|
36
|
+
const finalPipeline = isPartialOk
|
|
37
|
+
? pipelineWithExpandedFieldUsage
|
|
38
|
+
: (0, query_utils_1.detectAndRemovePartialStages)(pipelineWithExpandedFieldUsage, this);
|
|
33
39
|
return {
|
|
34
40
|
query: {
|
|
35
41
|
...query,
|
|
36
42
|
compositeResolvedSourceDef,
|
|
37
|
-
pipeline:
|
|
38
|
-
givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(
|
|
43
|
+
pipeline: finalPipeline,
|
|
44
|
+
givenUsage: (0, composite_source_utils_1.computeQueryGivenUsage)(finalPipeline),
|
|
39
45
|
},
|
|
40
46
|
// TODO bleh
|
|
41
47
|
outputStruct: pipeline[pipeline.length - 1].outputStruct,
|
|
@@ -18,7 +18,7 @@ class QuerySource extends source_1.Source {
|
|
|
18
18
|
return this.withParameters(parameterSpace, undefined);
|
|
19
19
|
}
|
|
20
20
|
withParameters(parameterSpace, pList) {
|
|
21
|
-
const comp = this.query.queryComp(false);
|
|
21
|
+
const comp = this.query.queryComp(false, false);
|
|
22
22
|
const queryStruct = (0, source_def_utils_1.mkQuerySourceDef)(comp.outputStruct, comp.query, `QuerySource-${(0, uuid_1.v4)()}`);
|
|
23
23
|
return {
|
|
24
24
|
...queryStruct,
|
|
@@ -22,7 +22,7 @@ class SQLSource extends source_1.Source {
|
|
|
22
22
|
const partialModel = this.select.containsQueries
|
|
23
23
|
? doc.modelDef()
|
|
24
24
|
: undefined;
|
|
25
|
-
const [valid, phrases] = this.select.sqlPhrases();
|
|
25
|
+
const [valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
|
|
26
26
|
if (valid) {
|
|
27
27
|
return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel);
|
|
28
28
|
}
|
|
@@ -123,7 +123,7 @@ class SQLSource extends source_1.Source {
|
|
|
123
123
|
location: this.location,
|
|
124
124
|
};
|
|
125
125
|
// Use factory to create SQLSourceDef without propagating sourceID/extends
|
|
126
|
-
const [_valid, phrases] = this.select.sqlPhrases();
|
|
126
|
+
const [_valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
|
|
127
127
|
const selectSegments = this.select.containsQueries ? phrases : undefined;
|
|
128
128
|
const locStruct = (0, source_def_utils_1.mkSQLSourceDef)(baseStruct, lookup.value.selectStr, selectSegments);
|
|
129
129
|
return locStruct;
|
|
@@ -9,10 +9,37 @@ import type { LogMessageOptions, MessageCode, MessageParameterType } from '../..
|
|
|
9
9
|
* a query.
|
|
10
10
|
*/
|
|
11
11
|
export declare abstract class SourceQueryElement extends MalloyElement {
|
|
12
|
+
/** Set once this element has reported why it could not produce a value. */
|
|
12
13
|
errored: boolean;
|
|
13
14
|
getSource(): Source | undefined;
|
|
14
15
|
getQuery(): QueryElement | undefined;
|
|
15
16
|
isSource(): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Report the one message which explains why this expression could not be
|
|
19
|
+
* turned into a source or a query.
|
|
20
|
+
*
|
|
21
|
+
* Every element in a source/query expression says something when it fails,
|
|
22
|
+
* so an outer element's generic complaint ("could not get source for
|
|
23
|
+
* query") would stack on top of the specific cause already reported
|
|
24
|
+
* beneath it. The message is therefore logged only when nothing in this
|
|
25
|
+
* subtree has spoken yet, and `errored` is set either way, which is what
|
|
26
|
+
* silences the elements above.
|
|
27
|
+
*
|
|
28
|
+
* This is not protection against logging the same message twice —
|
|
29
|
+
* `MalloyElement.log` already drops a repeat of one message at one
|
|
30
|
+
* location. It is one message per failed expression.
|
|
31
|
+
*
|
|
32
|
+
* The case it does not serve is two unrelated complaints about the same
|
|
33
|
+
* element: a second `sqLog` is dropped. Use `sqClaimError` for those.
|
|
34
|
+
*/
|
|
16
35
|
sqLog<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: LogMessageOptions): T;
|
|
36
|
+
/**
|
|
37
|
+
* Take the one error report this expression is allowed, for an element with
|
|
38
|
+
* more than one complaint to make. False means something below has already
|
|
39
|
+
* spoken. True means the caller owns the report and should `logError` each
|
|
40
|
+
* of its complaints; nothing above will speak after that.
|
|
41
|
+
*/
|
|
42
|
+
sqClaimError(): boolean;
|
|
43
|
+
/** True until this element, or anything below it, has reported a failure. */
|
|
17
44
|
isErrorFree(): boolean;
|
|
18
45
|
}
|
|
@@ -15,6 +15,7 @@ const malloy_element_1 = require("../types/malloy-element");
|
|
|
15
15
|
class SourceQueryElement extends malloy_element_1.MalloyElement {
|
|
16
16
|
constructor() {
|
|
17
17
|
super(...arguments);
|
|
18
|
+
/** Set once this element has reported why it could not produce a value. */
|
|
18
19
|
this.errored = false;
|
|
19
20
|
}
|
|
20
21
|
getSource() {
|
|
@@ -26,6 +27,24 @@ class SourceQueryElement extends malloy_element_1.MalloyElement {
|
|
|
26
27
|
isSource() {
|
|
27
28
|
return false;
|
|
28
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Report the one message which explains why this expression could not be
|
|
32
|
+
* turned into a source or a query.
|
|
33
|
+
*
|
|
34
|
+
* Every element in a source/query expression says something when it fails,
|
|
35
|
+
* so an outer element's generic complaint ("could not get source for
|
|
36
|
+
* query") would stack on top of the specific cause already reported
|
|
37
|
+
* beneath it. The message is therefore logged only when nothing in this
|
|
38
|
+
* subtree has spoken yet, and `errored` is set either way, which is what
|
|
39
|
+
* silences the elements above.
|
|
40
|
+
*
|
|
41
|
+
* This is not protection against logging the same message twice —
|
|
42
|
+
* `MalloyElement.log` already drops a repeat of one message at one
|
|
43
|
+
* location. It is one message per failed expression.
|
|
44
|
+
*
|
|
45
|
+
* The case it does not serve is two unrelated complaints about the same
|
|
46
|
+
* element: a second `sqLog` is dropped. Use `sqClaimError` for those.
|
|
47
|
+
*/
|
|
29
48
|
sqLog(code, parameters, options) {
|
|
30
49
|
if (this.isErrorFree()) {
|
|
31
50
|
this.logError(code, parameters, options);
|
|
@@ -33,6 +52,20 @@ class SourceQueryElement extends malloy_element_1.MalloyElement {
|
|
|
33
52
|
this.errored = true;
|
|
34
53
|
return code;
|
|
35
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Take the one error report this expression is allowed, for an element with
|
|
57
|
+
* more than one complaint to make. False means something below has already
|
|
58
|
+
* spoken. True means the caller owns the report and should `logError` each
|
|
59
|
+
* of its complaints; nothing above will speak after that.
|
|
60
|
+
*/
|
|
61
|
+
sqClaimError() {
|
|
62
|
+
if (!this.isErrorFree()) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
this.errored = true;
|
|
66
|
+
return true;
|
|
67
|
+
}
|
|
68
|
+
/** True until this element, or anything below it, has reported a failure. */
|
|
36
69
|
isErrorFree() {
|
|
37
70
|
if (this.errored) {
|
|
38
71
|
return false;
|
|
@@ -16,6 +16,8 @@ export declare class SQArrow extends SourceQueryElement {
|
|
|
16
16
|
readonly applyTo: SourceQueryElement;
|
|
17
17
|
readonly operation: View;
|
|
18
18
|
elementType: string;
|
|
19
|
+
asQuery?: QueryElement;
|
|
20
|
+
asSource?: Source;
|
|
19
21
|
constructor(applyTo: SourceQueryElement, operation: View);
|
|
20
22
|
getQuery(): QueryElement | undefined;
|
|
21
23
|
getSource(): Source | undefined;
|
|
@@ -26,6 +26,9 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
|
|
|
26
26
|
this.elementType = 'sq-arrow';
|
|
27
27
|
}
|
|
28
28
|
getQuery() {
|
|
29
|
+
if (this.asQuery) {
|
|
30
|
+
return this.asQuery;
|
|
31
|
+
}
|
|
29
32
|
const lhs = this.applyTo.isSource()
|
|
30
33
|
? this.applyTo.getSource()
|
|
31
34
|
: this.applyTo.getQuery();
|
|
@@ -33,19 +36,22 @@ class SQArrow extends source_query_element_1.SourceQueryElement {
|
|
|
33
36
|
this.sqLog('failed-to-compute-arrow-source', 'Could not get LHS of arrow operation');
|
|
34
37
|
return;
|
|
35
38
|
}
|
|
36
|
-
|
|
37
|
-
this.has({ query:
|
|
38
|
-
return
|
|
39
|
+
this.asQuery = new query_arrow_1.QueryArrow(lhs, this.operation);
|
|
40
|
+
this.has({ query: this.asQuery });
|
|
41
|
+
return this.asQuery;
|
|
39
42
|
}
|
|
40
43
|
getSource() {
|
|
44
|
+
if (this.asSource) {
|
|
45
|
+
return this.asSource;
|
|
46
|
+
}
|
|
41
47
|
const query = this.getQuery();
|
|
42
48
|
if (!query) {
|
|
43
49
|
this.sqLog('failed-to-compute-source-from-query', "Couldn't comprehend query well enough to make a source");
|
|
44
50
|
return;
|
|
45
51
|
}
|
|
46
|
-
|
|
47
|
-
this.has({ asSource });
|
|
48
|
-
return asSource;
|
|
52
|
+
this.asSource = new query_source_1.QuerySource(query);
|
|
53
|
+
this.has({ asSource: this.asSource });
|
|
54
|
+
return this.asSource;
|
|
49
55
|
}
|
|
50
56
|
}
|
|
51
57
|
exports.SQArrow = SQArrow;
|
|
@@ -12,6 +12,8 @@ export declare class SQRefine extends SourceQueryElement {
|
|
|
12
12
|
readonly toRefine: SourceQueryElement;
|
|
13
13
|
readonly refine: View;
|
|
14
14
|
elementType: string;
|
|
15
|
+
asQuery?: QueryRefine;
|
|
16
|
+
asSource?: QuerySource;
|
|
15
17
|
constructor(toRefine: SourceQueryElement, refine: View);
|
|
16
18
|
getQuery(): QueryRefine | undefined;
|
|
17
19
|
getSource(): QuerySource | undefined;
|
|
@@ -23,6 +23,9 @@ class SQRefine extends source_query_element_1.SourceQueryElement {
|
|
|
23
23
|
this.elementType = 'sq-refine';
|
|
24
24
|
}
|
|
25
25
|
getQuery() {
|
|
26
|
+
if (this.asQuery) {
|
|
27
|
+
return this.asQuery;
|
|
28
|
+
}
|
|
26
29
|
if (this.toRefine.isSource()) {
|
|
27
30
|
if (this.toRefine instanceof sq_reference_1.SQReference) {
|
|
28
31
|
this.sqLog('illegal-refinement-of-source', `Cannot add view refinements to '${this.toRefine.ref.refString}' because it is a source`);
|
|
@@ -34,17 +37,20 @@ class SQRefine extends source_query_element_1.SourceQueryElement {
|
|
|
34
37
|
}
|
|
35
38
|
const refinedQuery = this.toRefine.getQuery();
|
|
36
39
|
if (refinedQuery) {
|
|
37
|
-
|
|
38
|
-
this.has({ query:
|
|
39
|
-
return
|
|
40
|
+
this.asQuery = new query_refine_1.QueryRefine(refinedQuery, this.refine);
|
|
41
|
+
this.has({ query: this.asQuery });
|
|
42
|
+
return this.asQuery;
|
|
40
43
|
}
|
|
41
44
|
}
|
|
42
45
|
getSource() {
|
|
46
|
+
if (this.asSource) {
|
|
47
|
+
return this.asSource;
|
|
48
|
+
}
|
|
43
49
|
const query = this.getQuery();
|
|
44
50
|
if (query) {
|
|
45
|
-
|
|
46
|
-
this.has({ queryAsSource });
|
|
47
|
-
return
|
|
51
|
+
this.asSource = new query_source_1.QuerySource(query);
|
|
52
|
+
this.has({ queryAsSource: this.asSource });
|
|
53
|
+
return this.asSource;
|
|
48
54
|
}
|
|
49
55
|
}
|
|
50
56
|
}
|
|
@@ -10,6 +10,7 @@ import { QueryRaw } from '../query-elements/query-raw';
|
|
|
10
10
|
export declare class SQSource extends SourceQueryElement {
|
|
11
11
|
readonly theSource: Source;
|
|
12
12
|
elementType: string;
|
|
13
|
+
asQuery?: QueryRaw;
|
|
13
14
|
constructor(theSource: Source);
|
|
14
15
|
isSource(): boolean;
|
|
15
16
|
getSource(): Source;
|
|
@@ -27,10 +27,13 @@ class SQSource extends source_query_element_1.SourceQueryElement {
|
|
|
27
27
|
return this.theSource;
|
|
28
28
|
}
|
|
29
29
|
getQuery() {
|
|
30
|
+
if (this.asQuery) {
|
|
31
|
+
return this.asQuery;
|
|
32
|
+
}
|
|
30
33
|
if (this.theSource instanceof sql_source_1.SQLSource) {
|
|
31
|
-
|
|
32
|
-
this.has({ rawQuery });
|
|
33
|
-
return
|
|
34
|
+
this.asQuery = new query_raw_1.QueryRaw(this.theSource);
|
|
35
|
+
this.has({ rawQuery: this.asQuery });
|
|
36
|
+
return this.asQuery;
|
|
34
37
|
}
|
|
35
38
|
else {
|
|
36
39
|
this.sqLog('invalid-source-as-query', 'This source cannot be used as a query');
|
|
@@ -8,6 +8,6 @@ export declare class SQLString extends MalloyElement {
|
|
|
8
8
|
containsQueries: boolean;
|
|
9
9
|
complete(): void;
|
|
10
10
|
push(el: string | MalloyElement): void;
|
|
11
|
-
sqlPhrases(): [boolean, SQLPhraseSegment[]];
|
|
11
|
+
sqlPhrases(forConnection: string): [boolean, SQLPhraseSegment[]];
|
|
12
12
|
}
|
|
13
13
|
export {};
|