@malloydata/malloy 0.0.335 → 0.0.337
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/CONTEXT.md +4 -3
- package/MALLOY_API.md +129 -0
- package/dist/annotation.d.ts +0 -2
- package/dist/annotation.js +29 -23
- package/dist/api/asynchronous.d.ts +1 -1
- package/dist/api/foundation/cache.d.ts +32 -0
- package/dist/api/foundation/cache.js +92 -0
- package/dist/api/foundation/compile.d.ts +201 -0
- package/dist/api/foundation/compile.js +429 -0
- package/dist/api/foundation/core.d.ts +493 -0
- package/dist/api/foundation/core.js +1247 -0
- package/dist/api/foundation/document.d.ts +167 -0
- package/dist/api/foundation/document.js +206 -0
- package/dist/api/foundation/index.d.ts +10 -0
- package/dist/api/foundation/index.js +77 -0
- package/dist/api/foundation/readers.d.ts +53 -0
- package/dist/api/foundation/readers.js +134 -0
- package/dist/api/foundation/result.d.ts +185 -0
- package/dist/api/foundation/result.js +704 -0
- package/dist/api/foundation/runtime.d.ts +361 -0
- package/dist/api/foundation/runtime.js +733 -0
- package/dist/api/foundation/types.d.ts +54 -0
- package/dist/api/foundation/types.js +7 -0
- package/dist/api/foundation/writers.d.ts +42 -0
- package/dist/api/foundation/writers.js +230 -0
- package/dist/api/util.d.ts +1 -1
- package/dist/connection/base_connection.d.ts +5 -0
- package/dist/connection/index.d.ts +1 -0
- package/dist/connection/index.js +1 -0
- package/dist/connection/registry.d.ts +73 -0
- package/dist/connection/registry.js +106 -0
- package/dist/connection/types.d.ts +5 -15
- package/dist/dialect/duckdb/duckdb.js +2 -1
- package/dist/dialect/snowflake/snowflake.js +7 -1
- package/dist/dialect/trino/trino.js +7 -2
- package/dist/index.d.ts +9 -4
- package/dist/index.js +37 -26
- package/dist/lang/ast/error-factory.js +3 -5
- package/dist/lang/ast/source-elements/query-source.js +2 -7
- package/dist/lang/ast/source-elements/refined-source.js +11 -1
- package/dist/lang/ast/source-elements/sql-source.d.ts +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +18 -3
- package/dist/lang/ast/sql-elements/sql-string.d.ts +2 -2
- package/dist/lang/ast/sql-elements/sql-string.js +18 -1
- package/dist/lang/ast/statements/define-source.js +7 -2
- package/dist/lang/ast/statements/import-statement.js +53 -21
- package/dist/lang/ast/types/document-compile-result.d.ts +1 -0
- package/dist/lang/ast/types/malloy-element.d.ts +3 -1
- package/dist/lang/ast/types/malloy-element.js +23 -7
- package/dist/lang/malloy-to-ast.d.ts +1 -1
- package/dist/lang/malloy-to-ast.js +1 -1
- package/dist/lang/parse-malloy.d.ts +3 -2
- package/dist/lang/parse-malloy.js +14 -25
- package/dist/lang/test/test-translator.js +1 -0
- package/dist/lang/translate-response.d.ts +1 -0
- package/dist/model/constant_expression_compiler.js +6 -7
- package/dist/model/index.d.ts +3 -1
- package/dist/model/index.js +15 -9
- package/dist/model/malloy_types.d.ts +89 -15
- package/dist/model/malloy_types.js +12 -0
- package/dist/model/persist_utils.d.ts +47 -0
- package/dist/model/persist_utils.js +257 -0
- package/dist/model/query_model_impl.d.ts +2 -4
- package/dist/model/query_model_impl.js +5 -13
- package/dist/model/query_node.d.ts +1 -2
- package/dist/model/query_node.js +3 -13
- package/dist/model/query_query.d.ts +17 -1
- package/dist/model/query_query.js +81 -36
- package/dist/model/source_def_utils.d.ts +50 -0
- package/dist/model/source_def_utils.js +154 -0
- package/dist/model/sql_block.d.ts +5 -1
- package/dist/model/sql_block.js +29 -4
- package/dist/model/sql_compiled.d.ts +29 -0
- package/dist/model/sql_compiled.js +102 -0
- package/dist/model/stage_writer.d.ts +1 -3
- package/dist/model/stage_writer.js +7 -25
- package/dist/model/utils.d.ts +20 -1
- package/dist/model/utils.js +40 -0
- package/dist/run_sql_options.d.ts +0 -1
- package/dist/taggable.d.ts +10 -0
- package/dist/taggable.js +7 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -4
- package/dist/malloy.d.ts +0 -1365
- package/dist/malloy.js +0 -3421
- package/dist/model/materialization/utils.d.ts +0 -3
- package/dist/model/materialization/utils.js +0 -41
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.findPersistentDependencies = findPersistentDependencies;
|
|
8
|
+
exports.minimalBuildGraph = minimalBuildGraph;
|
|
9
|
+
const malloy_types_1 = require("./malloy_types");
|
|
10
|
+
const source_def_utils_1 = require("./source_def_utils");
|
|
11
|
+
const annotation_1 = require("../annotation");
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a source name to its definition from model contents.
|
|
14
|
+
*/
|
|
15
|
+
function resolveSource(modelDef, name) {
|
|
16
|
+
const obj = modelDef.contents[name];
|
|
17
|
+
return obj && (0, malloy_types_1.isSourceDef)(obj) ? obj : undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Check if a source has the #@ persist annotation.
|
|
21
|
+
*/
|
|
22
|
+
function checkPersistAnnotation(source) {
|
|
23
|
+
if (!source.annotation)
|
|
24
|
+
return false;
|
|
25
|
+
const { tag } = (0, annotation_1.annotationToTag)(source.annotation, { prefix: /^#@ / });
|
|
26
|
+
return tag.has('persist');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Check if a sourceID is persistent, using lazy evaluation and caching.
|
|
30
|
+
* Sets the persist flag on the registry entry as a side effect.
|
|
31
|
+
*/
|
|
32
|
+
function isPersistent(sourceID, modelDef) {
|
|
33
|
+
const value = modelDef.sourceRegistry[sourceID];
|
|
34
|
+
if (!value)
|
|
35
|
+
return false;
|
|
36
|
+
if (value.persist === undefined) {
|
|
37
|
+
const sourceDef = (0, source_def_utils_1.resolveSourceID)(modelDef, sourceID);
|
|
38
|
+
value.persist = sourceDef ? checkPersistAnnotation(sourceDef) : false;
|
|
39
|
+
}
|
|
40
|
+
return value.persist;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Find persistent dependencies for a source or query, returning a nested DAG.
|
|
44
|
+
*
|
|
45
|
+
* Walks the full dependency tree but only includes persistent sources in the
|
|
46
|
+
* result. Non-persistent sources are "flattened out" - their persistent
|
|
47
|
+
* dependencies bubble up to become direct dependencies of the caller.
|
|
48
|
+
*
|
|
49
|
+
* Example: source_c (persist) -> source_b (NOT persist) -> source_a (persist)
|
|
50
|
+
* Returns: [{sourceID: source_a, dependsOn: []}]
|
|
51
|
+
* (source_b is flattened out, source_a becomes direct dependency)
|
|
52
|
+
*
|
|
53
|
+
* ## The 6 Dependency Paths in the IR
|
|
54
|
+
*
|
|
55
|
+
* Starting from a Query or SourceDef, these are ALL the ways a SourceDef
|
|
56
|
+
* can be referenced (and thus must be walked for dependency tracking):
|
|
57
|
+
*
|
|
58
|
+
* 1. **Query.structRef** → SourceDef (the FROM clause)
|
|
59
|
+
* 2. **Query.pipeline[].extendSource[]** → JoinFieldDef (joins in extend blocks)
|
|
60
|
+
* 3. **SourceDef.fields[]** → JoinFieldDef (joins defined on a source)
|
|
61
|
+
* 4. **PersistableSourceDef.extends** → SourceID (extend chain reference)
|
|
62
|
+
* 5. **SQLSourceDef.selectSegments[]** → Query | PersistableSourceDef (SQL interpolation)
|
|
63
|
+
* 6. **QuerySourceDef.query** → Query (nested query in query_source)
|
|
64
|
+
*
|
|
65
|
+
* Note: CompositeSourceDef.sources[] is ignored - composite sources and
|
|
66
|
+
* persistence may be incompatible features.
|
|
67
|
+
*
|
|
68
|
+
* @param root The source or query to find dependencies for
|
|
69
|
+
* @param modelDef The model definition containing the source registry
|
|
70
|
+
* @returns Array of BuildNode representing the persistent dependency DAG
|
|
71
|
+
*/
|
|
72
|
+
function findPersistentDependencies(root, modelDef) {
|
|
73
|
+
const visited = new Set();
|
|
74
|
+
function processSourceID(sourceID) {
|
|
75
|
+
if (visited.has(sourceID)) {
|
|
76
|
+
return [];
|
|
77
|
+
}
|
|
78
|
+
visited.add(sourceID);
|
|
79
|
+
const sourceDef = (0, source_def_utils_1.resolveSourceID)(modelDef, sourceID);
|
|
80
|
+
if (!sourceDef) {
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
const childDeps = processSourceDef(sourceDef);
|
|
84
|
+
const persistent = isPersistent(sourceID, modelDef);
|
|
85
|
+
if (persistent) {
|
|
86
|
+
return [{ sourceID, dependsOn: childDeps }];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
return childDeps;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function processSourceDef(source) {
|
|
93
|
+
const results = [];
|
|
94
|
+
// Path 4: PersistableSourceDef.extends
|
|
95
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.extends) {
|
|
96
|
+
results.push(...processSourceID(source.extends));
|
|
97
|
+
}
|
|
98
|
+
// Path 6: QuerySourceDef.query
|
|
99
|
+
if (source.type === 'query_source') {
|
|
100
|
+
results.push(...processQuery(source.query));
|
|
101
|
+
}
|
|
102
|
+
// Path 5: SQLSourceDef.selectSegments[]
|
|
103
|
+
if (source.type === 'sql_select' && source.selectSegments) {
|
|
104
|
+
for (const segment of source.selectSegments) {
|
|
105
|
+
results.push(...processSQLSegment(segment));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Path 3: SourceDef.fields[] - joins defined on the source
|
|
109
|
+
for (const field of source.fields) {
|
|
110
|
+
if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field)) {
|
|
111
|
+
results.push(...processJoinedSource(field));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return results;
|
|
115
|
+
}
|
|
116
|
+
function processQuery(query) {
|
|
117
|
+
const results = [];
|
|
118
|
+
// Path 1: Query.structRef
|
|
119
|
+
results.push(...processStructRef(query.structRef));
|
|
120
|
+
// Path 2: Query.pipeline[].extendSource[]
|
|
121
|
+
for (const segment of query.pipeline) {
|
|
122
|
+
if (segment.type === 'reduce' ||
|
|
123
|
+
segment.type === 'project' ||
|
|
124
|
+
segment.type === 'partial') {
|
|
125
|
+
const querySegment = segment;
|
|
126
|
+
if (querySegment.extendSource) {
|
|
127
|
+
for (const field of querySegment.extendSource) {
|
|
128
|
+
if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field)) {
|
|
129
|
+
results.push(...processJoinedSource(field));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
function processJoinedSource(source) {
|
|
138
|
+
// If it has a sourceID, go through the registry
|
|
139
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.sourceID) {
|
|
140
|
+
return processSourceID(source.sourceID);
|
|
141
|
+
}
|
|
142
|
+
// Otherwise walk through it transparently
|
|
143
|
+
return processSourceDef(source);
|
|
144
|
+
}
|
|
145
|
+
function processStructRef(ref) {
|
|
146
|
+
if (typeof ref === 'string') {
|
|
147
|
+
const source = resolveSource(modelDef, ref);
|
|
148
|
+
if (!source)
|
|
149
|
+
return [];
|
|
150
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.sourceID) {
|
|
151
|
+
return processSourceID(source.sourceID);
|
|
152
|
+
}
|
|
153
|
+
return processSourceDef(source);
|
|
154
|
+
}
|
|
155
|
+
else if ((0, malloy_types_1.isSourceDef)(ref)) {
|
|
156
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(ref) && ref.sourceID) {
|
|
157
|
+
return processSourceID(ref.sourceID);
|
|
158
|
+
}
|
|
159
|
+
return processSourceDef(ref);
|
|
160
|
+
}
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
function processSQLSegment(segment) {
|
|
164
|
+
if ((0, malloy_types_1.isSegmentSQL)(segment)) {
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
else if ((0, malloy_types_1.isSegmentSource)(segment)) {
|
|
168
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(segment) && segment.sourceID) {
|
|
169
|
+
return processSourceID(segment.sourceID);
|
|
170
|
+
}
|
|
171
|
+
return processSourceDef(segment);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
// It's a Query
|
|
175
|
+
return processQuery(segment);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// Entry point: handle both SourceDef and Query
|
|
179
|
+
// Query has required 'structRef', SourceDef does not
|
|
180
|
+
if ('structRef' in root) {
|
|
181
|
+
return processQuery(root);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
// If the root source itself is persistable and has a sourceID, process it through
|
|
185
|
+
// processSourceID so it gets included in the result if persistent
|
|
186
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(root) && root.sourceID) {
|
|
187
|
+
return processSourceID(root.sourceID);
|
|
188
|
+
}
|
|
189
|
+
return processSourceDef(root);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Collect all sourceIDs from a BuildNode forest (for analysis only).
|
|
194
|
+
*/
|
|
195
|
+
function collectAllSourceIDs(nodes) {
|
|
196
|
+
const result = new Set();
|
|
197
|
+
for (const node of nodes) {
|
|
198
|
+
result.add(node.sourceID);
|
|
199
|
+
for (const id of collectAllSourceIDs(node.dependsOn)) {
|
|
200
|
+
result.add(id);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Collect all sourceIDs that appear in any dependsOn (for analysis only).
|
|
207
|
+
*/
|
|
208
|
+
function collectAllDependedOn(nodes) {
|
|
209
|
+
const result = new Set();
|
|
210
|
+
for (const node of nodes) {
|
|
211
|
+
for (const dep of node.dependsOn) {
|
|
212
|
+
result.add(dep.sourceID);
|
|
213
|
+
}
|
|
214
|
+
for (const id of collectAllDependedOn(node.dependsOn)) {
|
|
215
|
+
result.add(id);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return result;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Find the minimal set of root build graphs from a forest of BuildNodes.
|
|
222
|
+
*
|
|
223
|
+
* Uses flattening for ANALYSIS ONLY to identify unique nodes and find roots.
|
|
224
|
+
* Returns original graph structures (NOT flattened) - preserves branching
|
|
225
|
+
* for parallel builds.
|
|
226
|
+
*
|
|
227
|
+
* Roots are sourceIDs that exist but nothing depends on them - these are
|
|
228
|
+
* the entry points for building.
|
|
229
|
+
*
|
|
230
|
+
* @param deps Array of BuildNode trees (potentially overlapping)
|
|
231
|
+
* @returns Array of root BuildNode trees (deduplicated)
|
|
232
|
+
*/
|
|
233
|
+
function minimalBuildGraph(deps) {
|
|
234
|
+
if (deps.length === 0)
|
|
235
|
+
return [];
|
|
236
|
+
// Use flattening for analysis only
|
|
237
|
+
const allSourceIDs = collectAllSourceIDs(deps);
|
|
238
|
+
const dependedOn = collectAllDependedOn(deps);
|
|
239
|
+
// Roots are sourceIDs that exist but nothing depends on them
|
|
240
|
+
const rootIDs = new Set();
|
|
241
|
+
for (const id of allSourceIDs) {
|
|
242
|
+
if (!dependedOn.has(id)) {
|
|
243
|
+
rootIDs.add(id);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Return original graph structures for roots (deduplicated by sourceID)
|
|
247
|
+
const seen = new Set();
|
|
248
|
+
const roots = [];
|
|
249
|
+
for (const node of deps) {
|
|
250
|
+
if (rootIDs.has(node.sourceID) && !seen.has(node.sourceID)) {
|
|
251
|
+
seen.add(node.sourceID);
|
|
252
|
+
roots.push(node);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return roots;
|
|
256
|
+
}
|
|
257
|
+
//# sourceMappingURL=persist_utils.js.map
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import type { ModelDef, StructRef, Argument, PrepareResultOptions, Query, SourceDef, SearchIndexResult, CompiledQuery, TurtleDef } from './malloy_types';
|
|
2
2
|
import { StageWriter } from './stage_writer';
|
|
3
3
|
import { type Dialect } from '../dialect';
|
|
4
|
-
import type { EventStream } from '../runtime_types';
|
|
5
4
|
import type { Connection } from '../connection/types';
|
|
6
5
|
import type { ModelRootInterface } from './query_node';
|
|
7
6
|
import { QueryStruct } from './query_node';
|
|
8
7
|
import type { QueryModel, QueryResults } from './query_model_contract';
|
|
9
|
-
export declare function makeQueryModel(modelDef: ModelDef | undefined
|
|
8
|
+
export declare function makeQueryModel(modelDef: ModelDef | undefined): QueryModel;
|
|
10
9
|
export declare class QueryModelImpl implements QueryModel, ModelRootInterface {
|
|
11
|
-
readonly eventStream?: EventStream | undefined;
|
|
12
10
|
dialect: Dialect;
|
|
13
11
|
modelDef: ModelDef | undefined;
|
|
14
12
|
structs: Map<string, QueryStruct>;
|
|
15
|
-
constructor(modelDef: ModelDef | undefined
|
|
13
|
+
constructor(modelDef: ModelDef | undefined);
|
|
16
14
|
getFinalOutputStruct(query: Query, options: PrepareResultOptions | undefined): SourceDef | undefined;
|
|
17
15
|
loadModelFromDef(modelDef: ModelDef): void;
|
|
18
16
|
getStructByName(name: string): QueryStruct;
|
|
@@ -12,15 +12,13 @@ const query_query_1 = require("./query_query");
|
|
|
12
12
|
const malloy_types_1 = require("./malloy_types");
|
|
13
13
|
const stage_writer_1 = require("./stage_writer");
|
|
14
14
|
const dialect_1 = require("../dialect");
|
|
15
|
-
const utils_1 = require("./materialization/utils");
|
|
16
15
|
const query_node_1 = require("./query_node");
|
|
17
16
|
const row_data_utils_1 = require("../api/row_data_utils");
|
|
18
|
-
function makeQueryModel(modelDef
|
|
19
|
-
return new QueryModelImpl(modelDef
|
|
17
|
+
function makeQueryModel(modelDef) {
|
|
18
|
+
return new QueryModelImpl(modelDef);
|
|
20
19
|
}
|
|
21
20
|
class QueryModelImpl {
|
|
22
|
-
constructor(modelDef
|
|
23
|
-
this.eventStream = eventStream;
|
|
21
|
+
constructor(modelDef) {
|
|
24
22
|
this.dialect = new dialect_1.StandardSQLDialect();
|
|
25
23
|
// dialect: Dialect = new PostgresDialect();
|
|
26
24
|
this.modelDef = undefined;
|
|
@@ -141,13 +139,11 @@ class QueryModelImpl {
|
|
|
141
139
|
};
|
|
142
140
|
}
|
|
143
141
|
compileQuery(query, prepareResultOptions, finalize = true) {
|
|
144
|
-
var _a, _b
|
|
145
|
-
let newModel;
|
|
142
|
+
var _a, _b;
|
|
146
143
|
const addDefaultRowLimit = this.addDefaultRowLimit(query, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.defaultRowLimit);
|
|
147
144
|
query = addDefaultRowLimit.query;
|
|
148
145
|
const addedDefaultRowLimit = addDefaultRowLimit.addedDefaultRowLimit;
|
|
149
|
-
const
|
|
150
|
-
const ret = m.loadQuery(query, undefined, prepareResultOptions, finalize, false);
|
|
146
|
+
const ret = this.loadQuery(query, undefined, prepareResultOptions, finalize, false);
|
|
151
147
|
const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
|
|
152
148
|
const sourceExplore = typeof structRef === 'string'
|
|
153
149
|
? structRef
|
|
@@ -163,10 +159,6 @@ class QueryModelImpl {
|
|
|
163
159
|
lastStageName: ret.lastStageName,
|
|
164
160
|
malloy: ret.malloy,
|
|
165
161
|
sql: ret.stageWriter.generateSQLStages(),
|
|
166
|
-
dependenciesToMaterialize: ret.stageWriter.dependenciesToMaterialize,
|
|
167
|
-
materialization: (0, utils_1.shouldMaterialize)(query.annotation)
|
|
168
|
-
? (0, utils_1.buildQueryMaterializationSpec)((_c = query.location) === null || _c === void 0 ? void 0 : _c.url, query.name, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.materializedTablePrefix)
|
|
169
|
-
: undefined,
|
|
170
162
|
structs: ret.structs,
|
|
171
163
|
sourceExplore,
|
|
172
164
|
sourceFilters: query.filterList,
|
|
@@ -61,7 +61,7 @@ export interface ParentQueryStruct {
|
|
|
61
61
|
struct: QueryStruct;
|
|
62
62
|
}
|
|
63
63
|
export interface ModelRootInterface {
|
|
64
|
-
|
|
64
|
+
structs: Map<string, QueryStruct>;
|
|
65
65
|
}
|
|
66
66
|
export interface ParentQueryModel {
|
|
67
67
|
model: ModelRootInterface;
|
|
@@ -119,7 +119,6 @@ export declare class QueryStruct {
|
|
|
119
119
|
resolveQueryFields(finalOutputStruct: (query: Query, options: PrepareResultOptions | undefined) => SourceDef | undefined): void;
|
|
120
120
|
getModel(): ModelRootInterface;
|
|
121
121
|
get eventStream(): EventStream | undefined;
|
|
122
|
-
setParent(parent: ParentQueryStruct | ParentQueryModel): void;
|
|
123
122
|
/** makes a new queryable field object from a fieldDef */
|
|
124
123
|
makeQueryField(field: FieldDef, referenceId?: string): QueryField;
|
|
125
124
|
root(): QueryStruct;
|
package/dist/model/query_node.js
CHANGED
|
@@ -180,7 +180,6 @@ class QueryStruct {
|
|
|
180
180
|
this.nameMap = new Map();
|
|
181
181
|
this._modelTag = undefined;
|
|
182
182
|
this._arguments = undefined;
|
|
183
|
-
this.setParent(parent);
|
|
184
183
|
if ('model' in parent) {
|
|
185
184
|
this.model = parent.model;
|
|
186
185
|
this.pathAliasMap = new Map();
|
|
@@ -192,6 +191,7 @@ class QueryStruct {
|
|
|
192
191
|
}
|
|
193
192
|
}
|
|
194
193
|
else {
|
|
194
|
+
this.parent = parent.struct;
|
|
195
195
|
this.model = this.getModel();
|
|
196
196
|
this.pathAliasMap = this.root().pathAliasMap;
|
|
197
197
|
this.connectionName = this.root().connectionName;
|
|
@@ -471,18 +471,8 @@ class QueryStruct {
|
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
473
|
get eventStream() {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
setParent(parent) {
|
|
477
|
-
if ('struct' in parent) {
|
|
478
|
-
this.parent = parent.struct;
|
|
479
|
-
}
|
|
480
|
-
if ('model' in parent) {
|
|
481
|
-
this.model = parent.model;
|
|
482
|
-
}
|
|
483
|
-
else {
|
|
484
|
-
this.model = this.getModel();
|
|
485
|
-
}
|
|
474
|
+
var _a;
|
|
475
|
+
return (_a = this.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.eventStream;
|
|
486
476
|
}
|
|
487
477
|
/** makes a new queryable field object from a fieldDef */
|
|
488
478
|
makeQueryField(field, referenceId) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { DialectFieldList } from '../dialect';
|
|
2
|
-
import type { TurtleDef, QueryResultDef, ResultStructMetadataDef, ResultMetadataDef, PipeSegment, QuerySegment, QueryFieldDef, SegmentFieldDef } from './malloy_types';
|
|
2
|
+
import type { TurtleDef, QueryResultDef, ResultStructMetadataDef, ResultMetadataDef, PipeSegment, QuerySegment, QueryFieldDef, SegmentFieldDef, Query, PrepareResultOptions } from './malloy_types';
|
|
3
3
|
import { AndChain } from './utils';
|
|
4
4
|
import type { JoinInstance } from './join_instance';
|
|
5
5
|
import { QueryStruct, QueryField } from './query_node';
|
|
@@ -63,6 +63,22 @@ export declare class QueryQuery extends QueryField {
|
|
|
63
63
|
/** returns a fields and primary key of a struct for this query */
|
|
64
64
|
getResultStructDef(resultStruct?: FieldInstanceResult, isRoot?: boolean): QueryResultDef;
|
|
65
65
|
getStructSourceSQL(qs: QueryStruct, stageWriter: StageWriter): string;
|
|
66
|
+
/**
|
|
67
|
+
* Compile a Query into SQL stages. Used by both query_source compilation
|
|
68
|
+
* and getCompiledSQL for interpolated sources.
|
|
69
|
+
*
|
|
70
|
+
* @param query The query to compile
|
|
71
|
+
* @param prepareResultOptions Options including manifest for substitution
|
|
72
|
+
* @param stageWriter If provided, stages are added to this writer and lastStageName is returned.
|
|
73
|
+
* If undefined, a new isolated writer is created and full SQL is returned.
|
|
74
|
+
* @param isJoinedSubquery Whether this is a joined subquery
|
|
75
|
+
* @returns { lastStageName, stageWriter, sql } - sql is only set if stageWriter was undefined
|
|
76
|
+
*/
|
|
77
|
+
compileQueryToStages(query: Query, prepareResultOptions: PrepareResultOptions, stageWriter: StageWriter | undefined, isJoinedSubquery: boolean): {
|
|
78
|
+
lastStageName: string;
|
|
79
|
+
stageWriter: StageWriter;
|
|
80
|
+
sql?: string;
|
|
81
|
+
};
|
|
66
82
|
generateSQLJoinBlock(stageWriter: StageWriter, ji: JoinInstance, depth: number): string;
|
|
67
83
|
generateSQLPassthroughKeys(qs: QueryStruct): string;
|
|
68
84
|
generateSQLJoins(stageWriter: StageWriter): string;
|
|
@@ -11,7 +11,8 @@ const utils_1 = require("./utils");
|
|
|
11
11
|
const query_node_1 = require("./query_node");
|
|
12
12
|
const stage_writer_1 = require("./stage_writer");
|
|
13
13
|
const field_instance_1 = require("./field_instance");
|
|
14
|
-
const
|
|
14
|
+
const sql_compiled_1 = require("./sql_compiled");
|
|
15
|
+
const source_def_utils_1 = require("./source_def_utils");
|
|
15
16
|
function pathToCol(path) {
|
|
16
17
|
return path.map(el => encodeURIComponent(el)).join('/');
|
|
17
18
|
}
|
|
@@ -51,7 +52,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
51
52
|
parent = new query_node_1.QueryStruct({
|
|
52
53
|
...sourceDef,
|
|
53
54
|
fields: [...sourceDef.fields, ...firstStage.extendSource],
|
|
54
|
-
}, parentStruct.sourceArguments, parent.parent ? { struct: parent } : { model: parent.
|
|
55
|
+
}, parentStruct.sourceArguments, parent.parent ? { struct: parent } : { model: parent.getModel() }, parent.prepareResultOptions);
|
|
55
56
|
turtleWithFilters = {
|
|
56
57
|
...turtleWithFilters,
|
|
57
58
|
pipeline: [
|
|
@@ -514,7 +515,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
514
515
|
return outputStruct;
|
|
515
516
|
}
|
|
516
517
|
getStructSourceSQL(qs, stageWriter) {
|
|
517
|
-
var _a, _b
|
|
518
|
+
var _a, _b;
|
|
518
519
|
switch (qs.structDef.type) {
|
|
519
520
|
case 'table':
|
|
520
521
|
return this.parent.dialect.quoteTablePath(qs.structDef.tablePath);
|
|
@@ -525,47 +526,86 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
525
526
|
case 'finalize':
|
|
526
527
|
return qs.structDef.name;
|
|
527
528
|
case 'sql_select':
|
|
528
|
-
return `(${qs.structDef.
|
|
529
|
+
return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_a = qs.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, path => this.parent.dialect.quoteTablePath(path), (query, opts) => {
|
|
530
|
+
// Compile query to isolated SQL (not into parent's stageWriter)
|
|
531
|
+
const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
|
|
532
|
+
return ret.sql;
|
|
533
|
+
})})`;
|
|
529
534
|
case 'nest_source':
|
|
530
535
|
return qs.structDef.pipeSQL;
|
|
531
536
|
case 'query_source': {
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
const structRef = (_c = query.compositeResolvedSourceDef) !== null && _c !== void 0 ? _c : query.structRef;
|
|
548
|
-
let sourceStruct;
|
|
549
|
-
if (typeof structRef === 'string') {
|
|
550
|
-
const struct = this.structRefToQueryStruct(structRef);
|
|
551
|
-
if (!struct) {
|
|
552
|
-
throw new Error(`Unexpected reference to an undefined source '${structRef}'`);
|
|
537
|
+
const { buildManifest, connectionDigests, strictPersist } = (_b = qs.prepareResultOptions) !== null && _b !== void 0 ? _b : {};
|
|
538
|
+
// Check manifest for this source
|
|
539
|
+
if (buildManifest && connectionDigests) {
|
|
540
|
+
const connDigest = connectionDigests[qs.structDef.connection];
|
|
541
|
+
if (connDigest) {
|
|
542
|
+
// Compile with empty opts to get manifest-ignorant SQL for BuildID
|
|
543
|
+
const fullRet = this.compileQueryToStages(qs.structDef.query, {}, undefined, false);
|
|
544
|
+
const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, fullRet.sql);
|
|
545
|
+
const entry = buildManifest.buildEntries[buildId];
|
|
546
|
+
if (entry) {
|
|
547
|
+
// Found in manifest - use persisted table
|
|
548
|
+
return this.parent.dialect.quoteTablePath(entry.tableName);
|
|
549
|
+
}
|
|
550
|
+
if (strictPersist) {
|
|
551
|
+
throw new Error(`Persist source '${qs.structDef.sourceID}' not found in manifest (buildId: ${buildId})`);
|
|
553
552
|
}
|
|
554
|
-
sourceStruct = struct;
|
|
555
|
-
}
|
|
556
|
-
else {
|
|
557
|
-
sourceStruct = new query_node_1.QueryStruct(structRef, query.sourceArguments, { model: this.parent.getModel() }, qs.prepareResultOptions);
|
|
558
553
|
}
|
|
559
|
-
const q = QueryQuery.makeQuery(turtleDef, sourceStruct, stageWriter, qs.parent !== undefined, // isJoinedSubquery
|
|
560
|
-
this.structRefToQueryStruct);
|
|
561
|
-
const ret = q.generateSQLFromPipeline(stageWriter);
|
|
562
|
-
return ret.lastStageName;
|
|
563
554
|
}
|
|
555
|
+
// Not in manifest - compile normally
|
|
556
|
+
const ret = this.compileQueryToStages(qs.structDef.query, qs.prepareResultOptions, stageWriter, qs.parent !== undefined);
|
|
557
|
+
return ret.lastStageName;
|
|
564
558
|
}
|
|
565
559
|
default:
|
|
566
560
|
throw new Error(`Cannot create SQL StageWriter from '${(0, malloy_types_1.getIdentifier)(qs.structDef)}' type '${qs.structDef.type}`);
|
|
567
561
|
}
|
|
568
562
|
}
|
|
563
|
+
/**
|
|
564
|
+
* Compile a Query into SQL stages. Used by both query_source compilation
|
|
565
|
+
* and getCompiledSQL for interpolated sources.
|
|
566
|
+
*
|
|
567
|
+
* @param query The query to compile
|
|
568
|
+
* @param prepareResultOptions Options including manifest for substitution
|
|
569
|
+
* @param stageWriter If provided, stages are added to this writer and lastStageName is returned.
|
|
570
|
+
* If undefined, a new isolated writer is created and full SQL is returned.
|
|
571
|
+
* @param isJoinedSubquery Whether this is a joined subquery
|
|
572
|
+
* @returns { lastStageName, stageWriter, sql } - sql is only set if stageWriter was undefined
|
|
573
|
+
*/
|
|
574
|
+
compileQueryToStages(query, prepareResultOptions, stageWriter, isJoinedSubquery) {
|
|
575
|
+
var _a;
|
|
576
|
+
const turtleDef = {
|
|
577
|
+
type: 'turtle',
|
|
578
|
+
name: 'ignoreme',
|
|
579
|
+
pipeline: query.pipeline,
|
|
580
|
+
filterList: query.filterList,
|
|
581
|
+
};
|
|
582
|
+
const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
|
|
583
|
+
let sourceStruct;
|
|
584
|
+
if (typeof structRef === 'string') {
|
|
585
|
+
const struct = this.structRefToQueryStruct(structRef);
|
|
586
|
+
if (!struct) {
|
|
587
|
+
throw new Error(`Unexpected reference to an undefined source '${structRef}'`);
|
|
588
|
+
}
|
|
589
|
+
sourceStruct = struct;
|
|
590
|
+
}
|
|
591
|
+
else {
|
|
592
|
+
sourceStruct = new query_node_1.QueryStruct(structRef, query.sourceArguments, { model: this.parent.getModel() }, prepareResultOptions);
|
|
593
|
+
}
|
|
594
|
+
// Create isolated stageWriter if none provided
|
|
595
|
+
const isolated = stageWriter === undefined;
|
|
596
|
+
// When isPartialQuery is set (e.g., SQL interpolation), don't use CTEs
|
|
597
|
+
// for dialects that don't support them in subqueries
|
|
598
|
+
const noCTE = prepareResultOptions.isPartialQuery &&
|
|
599
|
+
!sourceStruct.dialect.supportsCTEinCoorelatedSubQueries;
|
|
600
|
+
const writer = stageWriter !== null && stageWriter !== void 0 ? stageWriter : new stage_writer_1.StageWriter(!noCTE, undefined);
|
|
601
|
+
const q = QueryQuery.makeQuery(turtleDef, sourceStruct, writer, isJoinedSubquery, this.structRefToQueryStruct);
|
|
602
|
+
const ret = q.generateSQLFromPipeline(writer);
|
|
603
|
+
return {
|
|
604
|
+
lastStageName: ret.lastStageName,
|
|
605
|
+
stageWriter: writer,
|
|
606
|
+
sql: isolated ? writer.generateSQLStages() : undefined,
|
|
607
|
+
};
|
|
608
|
+
}
|
|
569
609
|
generateSQLJoinBlock(stageWriter, ji, depth) {
|
|
570
610
|
var _a;
|
|
571
611
|
let s = '';
|
|
@@ -1490,10 +1530,10 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1490
1530
|
};
|
|
1491
1531
|
pipeline.shift();
|
|
1492
1532
|
for (const transform of pipeline) {
|
|
1493
|
-
const
|
|
1533
|
+
const parentArg = this.parent.parent
|
|
1494
1534
|
? { struct: this.parent.parent }
|
|
1495
1535
|
: { model: this.parent.getModel() };
|
|
1496
|
-
const s = new query_node_1.QueryStruct(structDef, undefined,
|
|
1536
|
+
const s = new query_node_1.QueryStruct(structDef, undefined, parentArg, this.parent.prepareResultOptions);
|
|
1497
1537
|
const q = QueryQuery.makeQuery({ type: 'turtle', name: '~computeLastStage~', pipeline: [transform] }, s, stageWriter, this.isJoinedSubquery, this.structRefToQueryStruct);
|
|
1498
1538
|
q.prepare(stageWriter);
|
|
1499
1539
|
lastStageName = q.generateSQL(stageWriter);
|
|
@@ -1727,10 +1767,15 @@ class QueryQueryProject extends QueryQuery {
|
|
|
1727
1767
|
}
|
|
1728
1768
|
class QueryQueryRaw extends QueryQuery {
|
|
1729
1769
|
generateSQL(stageWriter) {
|
|
1770
|
+
var _a;
|
|
1730
1771
|
if (this.parent.structDef.type !== 'sql_select') {
|
|
1731
1772
|
throw new Error('Invalid struct for QueryQueryRaw, currently only supports SQL');
|
|
1732
1773
|
}
|
|
1733
|
-
return stageWriter.addStage(this.parent.structDef.
|
|
1774
|
+
return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, path => this.parent.dialect.quoteTablePath(path), (query, opts) => {
|
|
1775
|
+
// Compile query to isolated SQL (not into parent's stageWriter)
|
|
1776
|
+
const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
|
|
1777
|
+
return ret.sql;
|
|
1778
|
+
}));
|
|
1734
1779
|
}
|
|
1735
1780
|
prepare() {
|
|
1736
1781
|
// Do nothing!
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SourceDef Utilities for Persistence
|
|
3
|
+
*
|
|
4
|
+
* Key invariant: sourceID is ONLY assigned in DefineSource when a source
|
|
5
|
+
* gets a name. Factory functions explicitly copy only the fields they need,
|
|
6
|
+
* never using spread, to prevent accidental propagation of sourceID/extends.
|
|
7
|
+
*
|
|
8
|
+
* The `extends` property is set by callers when processing extend blocks.
|
|
9
|
+
*/
|
|
10
|
+
import type { BuildID, FieldDef, ModelDef, PersistableSourceDef, Query, QuerySourceDef, SourceDef, SourceID, SourceRegistryEntry, SourceRegistryValue, SQLPhraseSegment, SQLSourceDef, TableSourceDef } from './malloy_types';
|
|
11
|
+
export declare function mkSourceID(name: string, url: string | undefined): SourceID;
|
|
12
|
+
/**
|
|
13
|
+
* Create a BuildID from connection digest and SQL.
|
|
14
|
+
* BuildID is a hash that uniquely identifies a build artifact.
|
|
15
|
+
*/
|
|
16
|
+
export declare function mkBuildID(connectionDigest: string, sql: string): BuildID;
|
|
17
|
+
/**
|
|
18
|
+
* Create a QuerySourceDef from query compilation output.
|
|
19
|
+
* Explicitly copies SourceDefBase fields - no spread.
|
|
20
|
+
*/
|
|
21
|
+
export declare function mkQuerySourceDef(base: SourceDef, query: Query, name: string): QuerySourceDef;
|
|
22
|
+
/**
|
|
23
|
+
* Create an SQLSourceDef from schema lookup result.
|
|
24
|
+
* Explicitly copies SourceDefBase fields - no spread.
|
|
25
|
+
*/
|
|
26
|
+
export declare function mkSQLSourceDef(base: SourceDef, selectStr: string, selectSegments?: SQLPhraseSegment[]): SQLSourceDef;
|
|
27
|
+
/**
|
|
28
|
+
* Create a TableSourceDef. All fields specified, no base to copy from.
|
|
29
|
+
*/
|
|
30
|
+
export declare function mkTableSourceDef(name: string, connection: string, tablePath: string, dialect: string, fields: FieldDef[]): TableSourceDef;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve a sourceID to a SourceDef using the sourceRegistry.
|
|
33
|
+
*
|
|
34
|
+
* @param modelDef The model definition containing the registry
|
|
35
|
+
* @param sourceID The sourceID to resolve
|
|
36
|
+
* @returns The SourceDef if found, undefined otherwise
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveSourceID(modelDef: ModelDef, sourceID: SourceID): PersistableSourceDef | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Add an entry to the sourceRegistry.
|
|
41
|
+
*
|
|
42
|
+
* @param registry The sourceRegistry to modify (from ModelDef or Document)
|
|
43
|
+
* @param sourceID The sourceID to register
|
|
44
|
+
* @param entry Either a SourceRegistryReference (for namespace sources) or a PersistableSourceDef (for hidden deps)
|
|
45
|
+
*/
|
|
46
|
+
export declare function registerSource(registry: Record<SourceID, SourceRegistryValue>, sourceID: SourceID, entry: SourceRegistryEntry): void;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a sourceID is already in the registry.
|
|
49
|
+
*/
|
|
50
|
+
export declare function hasSourceRegistryEntry(modelDef: ModelDef, sourceID: SourceID): boolean;
|