@malloydata/malloy 0.0.412 → 0.0.414
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/core.d.ts +21 -0
- package/dist/api/foundation/core.js +30 -0
- package/dist/dialect/databricks/databricks.d.ts +2 -1
- package/dist/dialect/databricks/databricks.js +12 -6
- package/dist/dialect/dialect.d.ts +12 -1
- package/dist/dialect/dialect.js +25 -0
- package/dist/dialect/duckdb/duckdb.d.ts +2 -1
- package/dist/dialect/duckdb/duckdb.js +9 -2
- package/dist/dialect/mysql/mysql.d.ts +2 -1
- package/dist/dialect/mysql/mysql.js +14 -5
- package/dist/dialect/postgres/postgres.d.ts +2 -1
- package/dist/dialect/postgres/postgres.js +9 -2
- package/dist/dialect/snowflake/snowflake.d.ts +2 -1
- package/dist/dialect/snowflake/snowflake.js +11 -3
- package/dist/dialect/standardsql/standardsql.d.ts +2 -1
- package/dist/dialect/standardsql/standardsql.js +8 -2
- package/dist/dialect/trino/trino.d.ts +2 -1
- package/dist/dialect/trino/trino.js +8 -2
- package/dist/lang/ast/field-space/dynamic-space.js +14 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +1 -0
- package/dist/lang/ast/field-space/query-spaces.js +5 -0
- package/dist/lang/ast/query-items/field-references.d.ts +10 -1
- package/dist/lang/ast/query-items/field-references.js +67 -4
- package/dist/lang/ast/query-properties/nest.js +15 -0
- package/dist/lang/ast/source-elements/named-source.js +4 -1
- package/dist/lang/ast/statements/define-source.js +9 -2
- package/dist/lang/ast/types/malloy-element.js +3 -4
- package/dist/lang/parse-log.d.ts +10 -0
- package/dist/lang/parse-log.js +3 -0
- package/dist/model/expression_compiler.js +2 -2
- package/dist/model/field_instance.d.ts +1 -0
- package/dist/model/field_instance.js +14 -0
- package/dist/model/index.d.ts +2 -1
- package/dist/model/index.js +3 -1
- package/dist/model/malloy_types.d.ts +25 -6
- package/dist/model/nest-capability.d.ts +37 -0
- package/dist/model/nest-capability.js +31 -0
- package/dist/model/persist_utils.js +1 -1
- package/dist/model/query_query.d.ts +26 -5
- package/dist/model/query_query.js +256 -88
- package/dist/model/source_def_utils.d.ts +31 -7
- package/dist/model/source_def_utils.js +39 -9
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -7,8 +7,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.FieldReferences = exports.WildcardFieldReference = exports.ViewOrScalarFieldReference = exports.DimensionFieldReference = exports.MeasureFieldReference = exports.DeclareFieldReference = exports.ProjectFieldReference = exports.GroupByFieldReference = exports.AggregateFieldReference = exports.IndexFieldReference = exports.CalculateFieldReference = exports.DrillFieldReference = exports.ParameterFieldReference = exports.GroupedByReference = exports.PartitionByFieldReference = exports.RefineFromFieldReference = exports.ExpressionFieldReference = exports.AccessModifierFieldReference = exports.AcceptExceptFieldReference = exports.FieldReference = void 0;
|
|
8
8
|
const reference_field_1 = require("../field-space/reference-field");
|
|
9
9
|
const definition_list_1 = require("../types/definition-list");
|
|
10
|
+
const field_space_1 = require("../types/field-space");
|
|
10
11
|
const malloy_element_1 = require("../types/malloy-element");
|
|
11
12
|
const noteable_1 = require("../types/noteable");
|
|
13
|
+
const expr_id_reference_1 = require("../expressions/expr-id-reference");
|
|
14
|
+
const field_declaration_1 = require("./field-declaration");
|
|
12
15
|
const typecheck_utils_1 = require("./typecheck_utils");
|
|
13
16
|
class FieldReference extends malloy_element_1.ListOf {
|
|
14
17
|
constructor(names) {
|
|
@@ -16,16 +19,64 @@ class FieldReference extends malloy_element_1.ListOf {
|
|
|
16
19
|
this.isNoteableObj = true;
|
|
17
20
|
this.extendNote = noteable_1.extendNoteMethod;
|
|
18
21
|
}
|
|
22
|
+
// The `name is path` declaration constructor used to auto-resolve an output
|
|
23
|
+
// name collision for this kind of reference, or undefined if this reference
|
|
24
|
+
// does not produce an output column named for its last path element (e.g.
|
|
25
|
+
// index references, view references). Overridden by the references that do.
|
|
26
|
+
autoRenameDeclarationCtor() {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
19
29
|
makeEntry(fs) {
|
|
20
30
|
const refName = this.outputName;
|
|
21
|
-
if (fs.entry(refName)) {
|
|
22
|
-
this.logError('output-name-conflict', `Output already has a field named '${refName}'`);
|
|
23
|
-
}
|
|
24
|
-
else {
|
|
31
|
+
if (fs.entry(refName) === undefined) {
|
|
25
32
|
// In a QuerySpace, this needs to be able to find the thing to which it refers
|
|
26
33
|
const fromFS = fs.isQueryFieldSpace() ? fs.inputSpace() : fs;
|
|
27
34
|
fs.newEntry(refName, this, new reference_field_1.ReferenceField(this, fromFS));
|
|
35
|
+
return;
|
|
28
36
|
}
|
|
37
|
+
// The natural (last-segment) name is taken. A dotted reference can be
|
|
38
|
+
// auto-renamed using its full path; a single-name reference has no prefix
|
|
39
|
+
// to disambiguate with, so it stays a hard error.
|
|
40
|
+
const declCtor = this.autoRenameDeclarationCtor();
|
|
41
|
+
if (declCtor && this.list.length > 1 && fs.isQueryFieldSpace()) {
|
|
42
|
+
this.autoRename(fs, fs.outputSpace().synthesizedNames, declCtor, refName);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (fs.isQueryFieldSpace() &&
|
|
46
|
+
fs.outputSpace().synthesizedNames.has(refName)) {
|
|
47
|
+
this.logError('output-name-conflict', `Output already has a field named '${refName}', which was ` +
|
|
48
|
+
'auto-generated by renaming an ambiguous reference; name one of the ' +
|
|
49
|
+
"conflicting references explicitly with 'is' to resolve this");
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.logError('output-name-conflict', `Output already has a field named '${refName}'`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Rewrite `path.to.field` into the equivalent `autoName is path.to.field`
|
|
56
|
+
// declaration — the same AST the user would write to fix it by hand — and
|
|
57
|
+
// let its normal makeEntry produce the (renamed) output field.
|
|
58
|
+
autoRename(fs, synthesizedNames, declCtor, refName) {
|
|
59
|
+
// `path.join('_')`, then `_2`, `_3`, ... until free. The synthetic name
|
|
60
|
+
// yields to whatever is already in the output, so a user-written name wins.
|
|
61
|
+
const base = this.path.join('_');
|
|
62
|
+
let autoName = base;
|
|
63
|
+
let n = 2;
|
|
64
|
+
while (fs.entry(autoName) !== undefined) {
|
|
65
|
+
autoName = `${base}_${n++}`;
|
|
66
|
+
}
|
|
67
|
+
synthesizedNames.add(autoName);
|
|
68
|
+
this.logWarning('output-field-auto-renamed', `Output already has a field named '${refName}', so '${this.refString}' ` +
|
|
69
|
+
`was renamed to '${autoName}'. Write '${autoName} is ${this.refString}' ` +
|
|
70
|
+
'to set the name explicitly and silence this warning.');
|
|
71
|
+
const renamedRef = new ExpressionFieldReference(this.list.map(n => new field_space_1.FieldName(n.refString)));
|
|
72
|
+
const decl = new declCtor(new expr_id_reference_1.ExprIdReference(renamedRef), autoName);
|
|
73
|
+
if (this.note) {
|
|
74
|
+
decl.note = this.note;
|
|
75
|
+
}
|
|
76
|
+
// Attach under this reference so the synthetic nodes inherit its location
|
|
77
|
+
// and resolve back to the translator for logging.
|
|
78
|
+
this.has({ autoRenamed: decl });
|
|
79
|
+
decl.makeEntry(fs);
|
|
29
80
|
}
|
|
30
81
|
getName() {
|
|
31
82
|
return this.nameString;
|
|
@@ -161,6 +212,9 @@ class CalculateFieldReference extends FieldReference {
|
|
|
161
212
|
super(...arguments);
|
|
162
213
|
this.elementType = 'calculateFieldReference';
|
|
163
214
|
}
|
|
215
|
+
autoRenameDeclarationCtor() {
|
|
216
|
+
return field_declaration_1.CalculateFieldDeclaration;
|
|
217
|
+
}
|
|
164
218
|
typecheck(type) {
|
|
165
219
|
(0, typecheck_utils_1.typecheckCalculate)(type, this);
|
|
166
220
|
}
|
|
@@ -188,6 +242,9 @@ class AggregateFieldReference extends FieldReference {
|
|
|
188
242
|
super(...arguments);
|
|
189
243
|
this.elementType = 'aggregateFieldReference';
|
|
190
244
|
}
|
|
245
|
+
autoRenameDeclarationCtor() {
|
|
246
|
+
return field_declaration_1.AggregateFieldDeclaration;
|
|
247
|
+
}
|
|
191
248
|
typecheck(type) {
|
|
192
249
|
(0, typecheck_utils_1.typecheckAggregate)(type, this);
|
|
193
250
|
}
|
|
@@ -198,6 +255,9 @@ class GroupByFieldReference extends FieldReference {
|
|
|
198
255
|
super(...arguments);
|
|
199
256
|
this.elementType = 'groupByFieldReference';
|
|
200
257
|
}
|
|
258
|
+
autoRenameDeclarationCtor() {
|
|
259
|
+
return field_declaration_1.GroupByFieldDeclaration;
|
|
260
|
+
}
|
|
201
261
|
typecheck(type) {
|
|
202
262
|
(0, typecheck_utils_1.typecheckGroupBy)(type, this);
|
|
203
263
|
}
|
|
@@ -208,6 +268,9 @@ class ProjectFieldReference extends FieldReference {
|
|
|
208
268
|
super(...arguments);
|
|
209
269
|
this.elementType = 'projectFieldReference';
|
|
210
270
|
}
|
|
271
|
+
autoRenameDeclarationCtor() {
|
|
272
|
+
return field_declaration_1.ProjectFieldDeclaration;
|
|
273
|
+
}
|
|
211
274
|
typecheck(type) {
|
|
212
275
|
(0, typecheck_utils_1.typecheckProject)(type, this);
|
|
213
276
|
}
|
|
@@ -39,6 +39,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
exports.NestFieldDeclaration = void 0;
|
|
41
41
|
const model = __importStar(require("../../../model/malloy_types"));
|
|
42
|
+
const nest_capability_1 = require("../../../model/nest-capability");
|
|
42
43
|
const query_utils_1 = require("../query-utils");
|
|
43
44
|
const view_field_declaration_1 = require("../source-properties/view-field-declaration");
|
|
44
45
|
const query_property_interface_1 = require("../types/query-property-interface");
|
|
@@ -63,6 +64,20 @@ class NestFieldDeclaration extends view_field_declaration_1.ViewFieldDeclaration
|
|
|
63
64
|
? pipeline[0].refSummary
|
|
64
65
|
: undefined;
|
|
65
66
|
const checkedPipeline = (0, query_utils_1.detectAndRemovePartialStages)(pipeline, this);
|
|
67
|
+
// Reject — at translate time, with a located message — any stage of this
|
|
68
|
+
// nest the compiler can't emit for this dialect, rather than producing
|
|
69
|
+
// broken SQL. The compiler asks segment-at-a-time as it recurses; the
|
|
70
|
+
// translator holds the whole pipeline, so it asks every stage.
|
|
71
|
+
const dialect = fs.dialectObj();
|
|
72
|
+
if (dialect) {
|
|
73
|
+
for (let i = 0; i < checkedPipeline.length; i++) {
|
|
74
|
+
const strategy = (0, nest_capability_1.nestStrategy)(checkedPipeline[i], dialect, i < checkedPipeline.length - 1);
|
|
75
|
+
if (strategy.kind === 'unsupported') {
|
|
76
|
+
this.logError(strategy.reason, { dialect: dialect.name });
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
66
81
|
const pipelineWithDrillPaths = (0, drill_1.attachDrillPaths)(checkedPipeline, this.name);
|
|
67
82
|
this.turtleDef = {
|
|
68
83
|
type: 'turtle',
|
|
@@ -91,7 +91,10 @@ class NamedSource extends source_1.Source {
|
|
|
91
91
|
else {
|
|
92
92
|
(_a = this.document()) === null || _a === void 0 ? void 0 : _a.checkExperimentalDialect(this, entry.dialect);
|
|
93
93
|
if ((0, malloy_types_1.isSourceDef)(entry)) {
|
|
94
|
-
|
|
94
|
+
// This struct is created as an unmodified reference to `entry`. Mark it
|
|
95
|
+
// with entry's own identity; the modification path (DynamicSpace) clears
|
|
96
|
+
// this so only true references carry a referenceID.
|
|
97
|
+
return { ...entry, referenceID: entry.sourceID };
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
// I think this is now a never
|
|
@@ -58,9 +58,16 @@ class DefineSource extends malloy_element_1.MalloyElement {
|
|
|
58
58
|
}
|
|
59
59
|
: { ...this.note };
|
|
60
60
|
}
|
|
61
|
-
if ((0, malloy_types_1.
|
|
61
|
+
if ((0, malloy_types_1.isSourceDef)(entry)) {
|
|
62
|
+
// Every source gets a stable identity for its own definition. referenceID
|
|
63
|
+
// is left as it arrived: set to the referenced source's sourceID for an
|
|
64
|
+
// unmodified rename (`source: a is b`), and absent for a source that
|
|
65
|
+
// defines its own shape (table/sql/query, or a modified/extended source,
|
|
66
|
+
// which DynamicSpace cleared).
|
|
62
67
|
entry.sourceID = (0, source_def_utils_1.mkSourceID)(this.name, (_b = this.location) === null || _b === void 0 ? void 0 : _b.url);
|
|
63
|
-
|
|
68
|
+
if ((0, malloy_types_1.isPersistableSourceDef)(entry)) {
|
|
69
|
+
entry.persistent = (0, persist_utils_1.checkPersistAnnotation)(entry).persist;
|
|
70
|
+
}
|
|
64
71
|
}
|
|
65
72
|
entry.partitionComposite =
|
|
66
73
|
(_d = (0, composite_source_utils_1.getPartitionCompositeDesc)(this.note, structDef, (_c = this.sourceExpr) !== null && _c !== void 0 ? _c : this)) !== null && _d !== void 0 ? _d : structDef.partitionComposite;
|
|
@@ -665,10 +665,9 @@ class Document extends MalloyElement {
|
|
|
665
665
|
this.modelWasModified = true;
|
|
666
666
|
}
|
|
667
667
|
this.documentModel.set(str, ent);
|
|
668
|
-
// Maintain
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
ent.entry.sourceID) {
|
|
668
|
+
// Maintain the source-id table so sourceID/referenceID values resolve to
|
|
669
|
+
// their SourceDef. Every named source is registered by its sourceID.
|
|
670
|
+
if ((0, malloy_types_1.isSourceDef)(ent.entry) && ent.entry.sourceID) {
|
|
672
671
|
this.documentSrcRegistry[ent.entry.sourceID] = {
|
|
673
672
|
entry: {
|
|
674
673
|
type: 'source_registry_reference',
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -61,6 +61,15 @@ type MessageParameterTypes = {
|
|
|
61
61
|
'experimental-dialect-not-enabled': {
|
|
62
62
|
dialect: string;
|
|
63
63
|
};
|
|
64
|
+
'nesting-unsupported': {
|
|
65
|
+
dialect: string;
|
|
66
|
+
};
|
|
67
|
+
'nested-multi-stage-unsupported': {
|
|
68
|
+
dialect: string;
|
|
69
|
+
};
|
|
70
|
+
'nested-projection-limit-unsupported': {
|
|
71
|
+
dialect: string;
|
|
72
|
+
};
|
|
64
73
|
'sql-native-not-allowed-in-expression': {
|
|
65
74
|
rawType: string | undefined;
|
|
66
75
|
};
|
|
@@ -185,6 +194,7 @@ type MessageParameterTypes = {
|
|
|
185
194
|
'invalid-type-for-field-definition': string;
|
|
186
195
|
'circular-reference-in-field-definition': string;
|
|
187
196
|
'output-name-conflict': string;
|
|
197
|
+
'output-field-auto-renamed': string;
|
|
188
198
|
'select-of-view': string;
|
|
189
199
|
'select-of-analytic': string;
|
|
190
200
|
'select-of-aggregate': string;
|
package/dist/lang/parse-log.js
CHANGED
|
@@ -55,6 +55,9 @@ exports.MESSAGE_FORMATTERS = {
|
|
|
55
55
|
tag: 'pick-values-must-match',
|
|
56
56
|
}),
|
|
57
57
|
'experimental-dialect-not-enabled': e => `Requires compiler flag '##! experimental.dialect.${e.dialect}'`,
|
|
58
|
+
'nesting-unsupported': e => `'${e.dialect}' does not support nested queries`,
|
|
59
|
+
'nested-multi-stage-unsupported': e => `'${e.dialect}' does not support a multi-stage pipeline ('->') in a nested query`,
|
|
60
|
+
'nested-projection-limit-unsupported': e => `'${e.dialect}' does not support 'limit:' on a nested 'select:'`,
|
|
58
61
|
'pick-missing-else': "pick incomplete, missing 'else'",
|
|
59
62
|
'pick-missing-value': 'pick with no value can only be used with apply',
|
|
60
63
|
'pick-illegal-partial': 'pick with partial when can only be used with apply',
|
|
@@ -143,7 +143,7 @@ function compileExpr(resultSet, context, expr, state = new utils_1.GenerateState
|
|
|
143
143
|
else {
|
|
144
144
|
throw new Error(`Internal Error: Unknown aggregate function ${expr.function}`);
|
|
145
145
|
}
|
|
146
|
-
if (resultSet.root().isComplexQuery) {
|
|
146
|
+
if (resultSet.root().isComplexQuery && resultSet.root().emitsGroupSet) {
|
|
147
147
|
let groupSet = resultSet.groupSet;
|
|
148
148
|
if (state.totalGroupSet !== -1) {
|
|
149
149
|
groupSet = state.totalGroupSet;
|
|
@@ -411,7 +411,7 @@ function generateAsymmetricStringAggExpression(resultSet, context, value, separa
|
|
|
411
411
|
return context.dialect.sqlStringAggDistinct(distinctKey, valueSQL, separatorSQL);
|
|
412
412
|
}
|
|
413
413
|
function generateAnalyticFragment(dialect, resultStruct, context, expr, overload, state, args, partitionByFields, funcOrdering) {
|
|
414
|
-
const isComplex = resultStruct.root().isComplexQuery;
|
|
414
|
+
const isComplex = resultStruct.root().isComplexQuery && resultStruct.root().emitsGroupSet;
|
|
415
415
|
const partitionFields = getAnalyticPartitions(resultStruct, partitionByFields);
|
|
416
416
|
const allPartitions = [
|
|
417
417
|
...(isComplex ? ['group_set'] : []),
|
|
@@ -96,6 +96,7 @@ export declare class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
96
96
|
joins: Map<string, JoinInstance>;
|
|
97
97
|
havings: AndChain;
|
|
98
98
|
isComplexQuery: boolean;
|
|
99
|
+
emitsGroupSet: boolean;
|
|
99
100
|
queryUsesPartitioning: boolean;
|
|
100
101
|
computeOnlyGroups: number[];
|
|
101
102
|
elimatedComputeGroups: boolean;
|
|
@@ -222,6 +222,15 @@ class FieldInstanceResult {
|
|
|
222
222
|
maxDepth = r.maxDepth;
|
|
223
223
|
}
|
|
224
224
|
}
|
|
225
|
+
else if (fir.firstSegment.type === 'project') {
|
|
226
|
+
// A projection nest is grain-preserving (one array element per
|
|
227
|
+
// in-scope row), so it rides on the enclosing scope's group_set
|
|
228
|
+
// rather than getting its own. Without this it pins to group_set 0,
|
|
229
|
+
// and inside a deeper nest its array-agg FILTER never lines up with
|
|
230
|
+
// the enclosing group_set, producing empty arrays. (A first-stage
|
|
231
|
+
// projection; any following stages recurse in their own scope.)
|
|
232
|
+
fir.groupSet = this.groupSet;
|
|
233
|
+
}
|
|
225
234
|
}
|
|
226
235
|
}
|
|
227
236
|
this.childGroups = children;
|
|
@@ -401,6 +410,11 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
401
410
|
this.joins = new Map();
|
|
402
411
|
this.havings = new utils_1.AndChain();
|
|
403
412
|
this.isComplexQuery = false;
|
|
413
|
+
// True when the query emits a `group_set` column to demux fanned-out rows
|
|
414
|
+
// (the general path). The single-group-set fast path (generateSingleGroupSetSQL)
|
|
415
|
+
// sets this false: there is no group_set column, so aggregate and window
|
|
416
|
+
// expressions must not wrap themselves in `CASE WHEN group_set=N`.
|
|
417
|
+
this.emitsGroupSet = true;
|
|
404
418
|
this.queryUsesPartitioning = false;
|
|
405
419
|
this.computeOnlyGroups = [];
|
|
406
420
|
this.elimatedComputeGroups = false;
|
package/dist/model/index.d.ts
CHANGED
|
@@ -9,4 +9,5 @@ export { getModelAnnotations } from './annotation_utils';
|
|
|
9
9
|
export { constantExprToSQL } from './constant_expression_compiler';
|
|
10
10
|
export { getCompiledSQL } from './sql_compiled';
|
|
11
11
|
export { MalloyCompileError } from './malloy_compile_error';
|
|
12
|
-
export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
|
|
12
|
+
export { mkSourceID, mkBuildID, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, resolveSourceID, resolveSourceRef, sourceNamespaceReference, registerSource, hasSourceRegistryEntry, } from './source_def_utils';
|
|
13
|
+
export type { NamespaceReference } from './source_def_utils';
|
package/dist/model/index.js
CHANGED
|
@@ -18,7 +18,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
18
18
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
19
19
|
};
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
-
exports.hasSourceRegistryEntry = exports.registerSource = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.getCompiledSQL = exports.constantExprToSQL = exports.getModelAnnotations = exports.typeDefToString = exports.pathToKey = exports.mkModelID = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
|
|
21
|
+
exports.hasSourceRegistryEntry = exports.registerSource = exports.sourceNamespaceReference = exports.resolveSourceRef = exports.resolveSourceID = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkBuildID = exports.mkSourceID = exports.MalloyCompileError = exports.getCompiledSQL = exports.constantExprToSQL = exports.getModelAnnotations = exports.typeDefToString = exports.pathToKey = exports.mkModelID = exports.mkModelDef = exports.makeDigest = exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
|
|
22
22
|
__exportStar(require("./malloy_types"), exports);
|
|
23
23
|
const query_node_1 = require("./query_node");
|
|
24
24
|
Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
|
|
@@ -60,6 +60,8 @@ Object.defineProperty(exports, "mkQuerySourceDef", { enumerable: true, get: func
|
|
|
60
60
|
Object.defineProperty(exports, "mkSQLSourceDef", { enumerable: true, get: function () { return source_def_utils_1.mkSQLSourceDef; } });
|
|
61
61
|
Object.defineProperty(exports, "mkTableSourceDef", { enumerable: true, get: function () { return source_def_utils_1.mkTableSourceDef; } });
|
|
62
62
|
Object.defineProperty(exports, "resolveSourceID", { enumerable: true, get: function () { return source_def_utils_1.resolveSourceID; } });
|
|
63
|
+
Object.defineProperty(exports, "resolveSourceRef", { enumerable: true, get: function () { return source_def_utils_1.resolveSourceRef; } });
|
|
64
|
+
Object.defineProperty(exports, "sourceNamespaceReference", { enumerable: true, get: function () { return source_def_utils_1.sourceNamespaceReference; } });
|
|
63
65
|
Object.defineProperty(exports, "registerSource", { enumerable: true, get: function () { return source_def_utils_1.registerSource; } });
|
|
64
66
|
Object.defineProperty(exports, "hasSourceRegistryEntry", { enumerable: true, get: function () { return source_def_utils_1.hasSourceRegistryEntry; } });
|
|
65
67
|
//# sourceMappingURL=index.js.map
|
|
@@ -858,6 +858,22 @@ interface SourceDefBase extends StructDefBase, Filtered, ResultStructMetadata {
|
|
|
858
858
|
primaryKey?: PrimaryKeyRef;
|
|
859
859
|
dialect: string;
|
|
860
860
|
partitionComposite?: PartitionCompositeDesc;
|
|
861
|
+
/**
|
|
862
|
+
* Identity of this source's own definition (`name@modelURL`). Set for every
|
|
863
|
+
* source when it is defined. The persistence machinery reads this (gated by
|
|
864
|
+
* `isPersistableSourceDef`).
|
|
865
|
+
*/
|
|
866
|
+
sourceID?: SourceID;
|
|
867
|
+
/**
|
|
868
|
+
* Set only when this source was created as an unmodified reference to another
|
|
869
|
+
* source (`source: a is b`, or a plain join) — then it holds the `sourceID`
|
|
870
|
+
* of the *immediately* referenced source. Absent when the source defines its
|
|
871
|
+
* own shape (table/sql/query, or a modified/extended source). So
|
|
872
|
+
* `referenceID !== undefined` means "created as a reference", and the value
|
|
873
|
+
* resolves through `ModelDef.sourceRegistry` to the referenced source and its
|
|
874
|
+
* namespace name (see `sourceNamespaceReference`).
|
|
875
|
+
*/
|
|
876
|
+
referenceID?: SourceID;
|
|
861
877
|
}
|
|
862
878
|
/** which field is the primary key in this struct */
|
|
863
879
|
export type PrimaryKeyRef = string;
|
|
@@ -878,7 +894,7 @@ export interface SQLStringSegment {
|
|
|
878
894
|
export type SQLPhraseSegment = Query | PersistableSourceDef | SQLStringSegment;
|
|
879
895
|
export declare function isSegmentSQL(f: SQLPhraseSegment): f is SQLStringSegment;
|
|
880
896
|
export declare function isSegmentSource(f: SQLPhraseSegment): f is PersistableSourceDef;
|
|
881
|
-
/** Format: "name@modelUrl" - uniquely identifies a source
|
|
897
|
+
/** Format: "name@modelUrl" - uniquely identifies a defined source */
|
|
882
898
|
export type SourceID = string;
|
|
883
899
|
/** Created with `mkGivenID`. */
|
|
884
900
|
export type GivenID = string;
|
|
@@ -915,7 +931,6 @@ export interface SourceRegistryValue {
|
|
|
915
931
|
}
|
|
916
932
|
export declare function isSourceRegistryReference(entry: SourceRegistryEntry): entry is SourceRegistryReference;
|
|
917
933
|
export interface PersistableSourceProperties {
|
|
918
|
-
sourceID?: SourceID;
|
|
919
934
|
extends?: SourceID;
|
|
920
935
|
persistent?: boolean;
|
|
921
936
|
}
|
|
@@ -943,13 +958,17 @@ export declare function isSourceDef(sd: NamedModelObject | FieldDef): sd is Sour
|
|
|
943
958
|
/**
|
|
944
959
|
* Union of all source definition types.
|
|
945
960
|
*
|
|
946
|
-
*
|
|
947
|
-
* methods in source_def_utils.ts
|
|
961
|
+
* When building a *persisted/derived* source in the compiler, use the factory
|
|
962
|
+
* methods in source_def_utils.ts rather than object spread:
|
|
948
963
|
* - mkSQLSourceDef(base, ...) - create SQLSourceDef from base
|
|
949
964
|
* - mkQuerySourceDef(base, ...) - create QuerySourceDef from base
|
|
950
965
|
*
|
|
951
|
-
* These factories explicitly copy only safe fields,
|
|
952
|
-
*
|
|
966
|
+
* These factories explicitly copy only safe fields, dropping the identity
|
|
967
|
+
* fields (sourceID/referenceID/extends/persistent) so they are not propagated
|
|
968
|
+
* onto a freshly built source. In the translator, by contrast, object spread is
|
|
969
|
+
* used deliberately to *carry* sourceID/referenceID through an unmodified
|
|
970
|
+
* reference; DefineSource then sets them, and DynamicSpace clears referenceID
|
|
971
|
+
* on the modification path.
|
|
953
972
|
*/
|
|
954
973
|
export type SourceDef = TableSourceDef | SQLSourceDef | QuerySourceDef | QueryResultDef | FinalizeSourceDef | NestSourceDef | VirtualSourceDef | CompositeSourceDef;
|
|
955
974
|
/** Sources that can be persisted (materialized to tables) */
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Dialect } from '../dialect/dialect';
|
|
2
|
+
import type { PipeSegment } from './malloy_types';
|
|
3
|
+
/**
|
|
4
|
+
* Why the compiler can't emit a segment for a dialect. The translator maps each
|
|
5
|
+
* reason to a located, user-facing message (these values are also parse-log
|
|
6
|
+
* codes); the compiler throws a `MalloyCompileError` with the reason.
|
|
7
|
+
*/
|
|
8
|
+
export type NestUnsupported = 'nesting-unsupported' | 'nested-multi-stage-unsupported' | 'nested-projection-limit-unsupported';
|
|
9
|
+
/**
|
|
10
|
+
* How the compiler should emit one stage of a nest's pipeline — or, if the
|
|
11
|
+
* dialect can't, why not. This is THE single decision shared by the two callers
|
|
12
|
+
* that hold a dialect: the compiler (`generateTurtleSQL`) dispatches on `kind`
|
|
13
|
+
* and reads `limit`; the translator (`nest.ts`) turns `unsupported` into a
|
|
14
|
+
* located error. "How do I emit this" and "can I emit this" are one answer, so
|
|
15
|
+
* neither side can drift from the other.
|
|
16
|
+
*
|
|
17
|
+
* It is a *segment* question, not a pipeline one. The compiler compiles the
|
|
18
|
+
* first stage of a pipeline and recurses on the rest, so every stage is asked
|
|
19
|
+
* in turn as "the first stage" — pipeline length is irrelevant. `hasSuccessor`
|
|
20
|
+
* (is a stage materialized after this one?) is the only positional input, and
|
|
21
|
+
* it is what the multi-stage gate keys on.
|
|
22
|
+
*
|
|
23
|
+
* The dialect-free half of this — "is this stage an inline projection?" — is a
|
|
24
|
+
* plain `segment.type === 'project'`, used directly by the structural callers
|
|
25
|
+
* (group_set assignment, where-clause placement) that legitimately have no
|
|
26
|
+
* dialect; group_set numbering is not dialect-dependent.
|
|
27
|
+
*/
|
|
28
|
+
export type NestStrategy = {
|
|
29
|
+
kind: 'reduce';
|
|
30
|
+
} | {
|
|
31
|
+
kind: 'project';
|
|
32
|
+
limit: number | undefined;
|
|
33
|
+
} | {
|
|
34
|
+
kind: 'unsupported';
|
|
35
|
+
reason: NestUnsupported;
|
|
36
|
+
};
|
|
37
|
+
export declare function nestStrategy(segment: PipeSegment, dialect: Dialect, hasSuccessor: boolean): NestStrategy;
|
|
@@ -0,0 +1,31 @@
|
|
|
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.nestStrategy = nestStrategy;
|
|
8
|
+
const malloy_types_1 = require("./malloy_types");
|
|
9
|
+
function nestStrategy(segment, dialect, hasSuccessor) {
|
|
10
|
+
if (!dialect.supportsNesting) {
|
|
11
|
+
return { kind: 'unsupported', reason: 'nesting-unsupported' };
|
|
12
|
+
}
|
|
13
|
+
// A stage with another stage after it must be materialized as a nested
|
|
14
|
+
// pipeline stage, which not every dialect can do inside a view.
|
|
15
|
+
if (hasSuccessor && !dialect.supportsPipelinesInViews) {
|
|
16
|
+
return { kind: 'unsupported', reason: 'nested-multi-stage-unsupported' };
|
|
17
|
+
}
|
|
18
|
+
if ((0, malloy_types_1.isProjectSegment)(segment)) {
|
|
19
|
+
// A projection applies `limit:` by slicing its aggregated array; some
|
|
20
|
+
// dialects can't slice an aggregate (see sqlAggregateTurtle).
|
|
21
|
+
if (segment.limit !== undefined && !dialect.supportsNestedProjectionLimit) {
|
|
22
|
+
return {
|
|
23
|
+
kind: 'unsupported',
|
|
24
|
+
reason: 'nested-projection-limit-unsupported',
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return { kind: 'project', limit: segment.limit };
|
|
28
|
+
}
|
|
29
|
+
return { kind: 'reduce' };
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=nest-capability.js.map
|
|
@@ -145,7 +145,7 @@ function findPersistentDependencies(root, modelDef, tagParseLog = []) {
|
|
|
145
145
|
return results;
|
|
146
146
|
}
|
|
147
147
|
function processJoinedSource(source) {
|
|
148
|
-
// If it has
|
|
148
|
+
// If it has an sourceID, go through the registry
|
|
149
149
|
if ((0, malloy_types_1.isPersistableSourceDef)(source) && source.sourceID) {
|
|
150
150
|
return processSourceID(source.sourceID);
|
|
151
151
|
}
|
|
@@ -14,11 +14,14 @@ type StageGroupMaping = {
|
|
|
14
14
|
fromGroup: number;
|
|
15
15
|
toGroup: number;
|
|
16
16
|
};
|
|
17
|
+
interface StageOutputColumn {
|
|
18
|
+
sql: string;
|
|
19
|
+
name: string;
|
|
20
|
+
isDimension: boolean;
|
|
21
|
+
}
|
|
17
22
|
type StageOutputContext = {
|
|
18
|
-
|
|
23
|
+
columns: StageOutputColumn[];
|
|
19
24
|
lateralJoinSQLExpressions: LateralJoinExpression[];
|
|
20
|
-
dimensionIndexes: number[];
|
|
21
|
-
fieldIndex: number;
|
|
22
25
|
groupsAggregated: StageGroupMaping[];
|
|
23
26
|
outputPipelinedSQL: OutputPipelinedSQL[];
|
|
24
27
|
};
|
|
@@ -88,8 +91,26 @@ export declare class QueryQuery extends QueryField {
|
|
|
88
91
|
*/
|
|
89
92
|
collectArrayJoins(ji: JoinInstance): JoinInstance[];
|
|
90
93
|
genereateSQLOrderBy(queryDef: QuerySegment, resultStruct: FieldInstanceResult): string;
|
|
94
|
+
/**
|
|
95
|
+
* The single-scan form, with no `group_set` fan-out: one `SELECT` over the
|
|
96
|
+
* scan, grouped by the dimensions. Used both for a query with no nests and --
|
|
97
|
+
* via canUseSingleGroupSetSQL -- for one whose only nests are single-stage
|
|
98
|
+
* grain-preserving projections, which become an array-agg in the same SELECT.
|
|
99
|
+
* That array-agg needs no `FILTER (WHERE group_set=N)` (every row is in the
|
|
100
|
+
* one group), and aggregate/window expressions must not gate themselves by
|
|
101
|
+
* group_set either, so emitsGroupSet is cleared (see FieldInstanceResultRoot).
|
|
102
|
+
*/
|
|
91
103
|
generateSimpleSQL(stageWriter: StageWriter): string;
|
|
92
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Can this query take the single-group-set fast path? True when the query
|
|
106
|
+
* needs no group-set fan-out: it has nests (so it's "complex") but every one
|
|
107
|
+
* is a single-stage grain-preserving projection riding group_set 0, and there
|
|
108
|
+
* are no reduce nests, ungroupings, or totals (all of which would mint a
|
|
109
|
+
* second group set and push maxGroupSet above 0). Anything outside that shape
|
|
110
|
+
* falls back to the general demux path, which is correct for every case.
|
|
111
|
+
*/
|
|
112
|
+
canUseSingleGroupSetSQL(): boolean;
|
|
113
|
+
generatePipelinedStages(outputPipelinedSQL: OutputPipelinedSQL[], lastStageName: string, stageWriter: StageWriter, priorStageColumns: StageOutputColumn[]): string;
|
|
93
114
|
generateStage0Fields(resultSet: FieldInstanceResult, output: StageOutputContext, stageWriter: StageWriter): void;
|
|
94
115
|
generateSQLWhereChildren(resultStruct: FieldInstanceResult): AndChain;
|
|
95
116
|
generateSQLWhereTurtled(): string;
|
|
@@ -99,7 +120,7 @@ export declare class QueryQuery extends QueryField {
|
|
|
99
120
|
generateSQLDepthN(depth: number, stageWriter: StageWriter, stageName: string): string;
|
|
100
121
|
genereateSQLCombineTurtles(stageWriter: StageWriter, stage0Name: string): string;
|
|
101
122
|
buildDialectFieldList(resultStruct: FieldInstanceResult): DialectFieldList;
|
|
102
|
-
generateTurtleSQL(resultStruct: FieldInstanceResult, stageWriter: StageWriter, sqlFieldName: string, outputPipelinedSQL: OutputPipelinedSQL[]): string;
|
|
123
|
+
generateTurtleSQL(resultStruct: FieldInstanceResult, stageWriter: StageWriter, sqlFieldName: string, outputPipelinedSQL: OutputPipelinedSQL[], omitGroupSetFilter?: boolean): string;
|
|
103
124
|
generateTurtlePipelineSQL(fi: FieldInstanceResult, stageWriter: StageWriter, sourceSQLExpression: string): {
|
|
104
125
|
structDef: QueryResultDef;
|
|
105
126
|
pipeOut: any;
|