@malloydata/malloy 0.0.215 → 0.0.216-dev241118202522

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.
Files changed (72) hide show
  1. package/dist/dialect/functions/util.d.ts +8 -8
  2. package/dist/dialect/functions/util.js +15 -7
  3. package/dist/lang/ast/ast-utils.js +2 -0
  4. package/dist/lang/ast/expressions/expr-aggregate-function.js +1 -0
  5. package/dist/lang/ast/expressions/expr-compare.js +7 -14
  6. package/dist/lang/ast/expressions/expr-count-distinct.js +1 -0
  7. package/dist/lang/ast/expressions/expr-count.js +3 -0
  8. package/dist/lang/ast/expressions/expr-func.js +3 -0
  9. package/dist/lang/ast/expressions/expr-id-reference.js +10 -1
  10. package/dist/lang/ast/expressions/expr-now.js +2 -0
  11. package/dist/lang/ast/expressions/expr-ungroup.js +1 -0
  12. package/dist/lang/ast/field-space/dynamic-space.d.ts +1 -3
  13. package/dist/lang/ast/field-space/dynamic-space.js +11 -15
  14. package/dist/lang/ast/field-space/query-spaces.d.ts +19 -0
  15. package/dist/lang/ast/field-space/query-spaces.js +104 -12
  16. package/dist/lang/ast/field-space/reference-field.js +7 -1
  17. package/dist/lang/ast/field-space/refined-space.d.ts +1 -0
  18. package/dist/lang/ast/field-space/refined-space.js +9 -6
  19. package/dist/lang/ast/field-space/struct-space-field-base.js +4 -0
  20. package/dist/lang/ast/index.d.ts +1 -0
  21. package/dist/lang/ast/index.js +1 -0
  22. package/dist/lang/ast/query-builders/index-builder.js +1 -1
  23. package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -1
  24. package/dist/lang/ast/query-builders/reduce-builder.js +9 -5
  25. package/dist/lang/ast/query-elements/query-base.js +16 -3
  26. package/dist/lang/ast/query-items/field-declaration.js +3 -0
  27. package/dist/lang/ast/query-properties/filters.d.ts +3 -0
  28. package/dist/lang/ast/query-properties/filters.js +42 -26
  29. package/dist/lang/ast/query-properties/nest.d.ts +1 -0
  30. package/dist/lang/ast/query-properties/nest.js +5 -1
  31. package/dist/lang/ast/source-elements/composite-source.d.ts +16 -0
  32. package/dist/lang/ast/source-elements/composite-source.js +75 -0
  33. package/dist/lang/ast/source-elements/source.d.ts +2 -2
  34. package/dist/lang/ast/source-properties/join.js +2 -0
  35. package/dist/lang/ast/source-query-elements/sq-compose.d.ts +13 -0
  36. package/dist/lang/ast/source-query-elements/sq-compose.js +48 -0
  37. package/dist/lang/ast/typedesc-utils.js +7 -1
  38. package/dist/lang/ast/types/expr-value.js +4 -0
  39. package/dist/lang/ast/types/expression-def.js +4 -5
  40. package/dist/lang/ast/types/query-builder.d.ts +2 -1
  41. package/dist/lang/ast/types/space-field.js +14 -1
  42. package/dist/lang/ast/types/space-param.js +3 -0
  43. package/dist/lang/ast/view-elements/reference-view.js +1 -0
  44. package/dist/lang/ast/view-elements/refine-utils.js +2 -0
  45. package/dist/lang/lib/Malloy/MalloyLexer.d.ts +113 -112
  46. package/dist/lang/lib/Malloy/MalloyLexer.js +1149 -1143
  47. package/dist/lang/lib/Malloy/MalloyParser.d.ts +126 -112
  48. package/dist/lang/lib/Malloy/MalloyParser.js +1380 -1282
  49. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +13 -0
  50. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +8 -0
  51. package/dist/lang/malloy-to-ast.d.ts +1 -0
  52. package/dist/lang/malloy-to-ast.js +5 -0
  53. package/dist/lang/parse-log.d.ts +10 -1
  54. package/dist/lang/parse-log.js +7 -0
  55. package/dist/lang/test/composite-field-usage.spec.d.ts +1 -0
  56. package/dist/lang/test/composite-field-usage.spec.js +155 -0
  57. package/dist/lang/test/parse-expects.d.ts +2 -1
  58. package/dist/lang/test/parse-expects.js +36 -0
  59. package/dist/lang/test/source.spec.js +9 -0
  60. package/dist/lang/test/test-translator.d.ts +3 -1
  61. package/dist/lang/test/test-translator.js +15 -4
  62. package/dist/lang/utils.d.ts +1 -0
  63. package/dist/lang/utils.js +5 -1
  64. package/dist/model/composite_source_utils.d.ts +65 -0
  65. package/dist/model/composite_source_utils.js +279 -0
  66. package/dist/model/malloy_query.d.ts +3 -3
  67. package/dist/model/malloy_query.js +13 -6
  68. package/dist/model/malloy_types.d.ts +19 -2
  69. package/dist/model/malloy_types.js +15 -3
  70. package/dist/version.d.ts +1 -1
  71. package/dist/version.js +1 -1
  72. package/package.json +1 -1
@@ -0,0 +1,279 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
4
+ *
5
+ * This source code is licensed under the MIT license found in the
6
+ * LICENSE file in the root directory of this source tree.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.compositeFieldUsageJoinPaths = exports.joinedCompositeFieldUsage = exports.compositeFieldUsageDifference = exports.emptyCompositeFieldUsage = exports.mergeCompositeFieldUsage = exports.unique = exports.formatCompositeFieldUsage = exports.compositeFieldUsageIsPlural = exports.isEmptyCompositeFieldUsage = exports.formatCompositeFieldUsages = exports.compositeFieldUsagePaths = exports.resolveCompositeSources = exports.narrowCompositeFieldResolution = exports.emptyNarrowedCompositeFieldResolution = void 0;
10
+ const utils_1 = require("../lang/utils");
11
+ const malloy_types_1 = require("./malloy_types");
12
+ function _resolveCompositeSources(path, source, compositeFieldUsage, narrowedCompositeFieldResolution = undefined) {
13
+ var _a, _b, _c, _d, _e, _f, _g;
14
+ let base = { ...source };
15
+ let narrowedSources = undefined;
16
+ if (compositeFieldUsage.fields.length > 0) {
17
+ if (source.type === 'composite') {
18
+ let found = false;
19
+ // The narrowed source list is either the one given when this function was called,
20
+ // or we construct a new one from the given composite source's input sources.
21
+ narrowedSources =
22
+ (_a = narrowedCompositeFieldResolution === null || narrowedCompositeFieldResolution === void 0 ? void 0 : narrowedCompositeFieldResolution.source) !== null && _a !== void 0 ? _a : source.sources.map(s => ({ source: s, nested: undefined }));
23
+ // Make a copy, which we will mutate: if a source is invalid, we remove it from the list
24
+ // and move on; if the source is a nested composite source, we narrow the resolution of
25
+ // the inner sources and update the element in the list
26
+ const newNarrowedSources = [...narrowedSources];
27
+ // We iterate over the list of narrowed sources;
28
+ overSources: for (const { source: inputSource, nested, } of narrowedSources) {
29
+ const fieldNames = new Set();
30
+ for (const field of inputSource.fields) {
31
+ fieldNames.add((_b = field.as) !== null && _b !== void 0 ? _b : field.name);
32
+ }
33
+ for (const usage of compositeFieldUsage.fields) {
34
+ if (!fieldNames.has(usage)) {
35
+ newNarrowedSources.shift();
36
+ continue overSources;
37
+ }
38
+ }
39
+ const nonCompositeFields = getNonCompositeFields(source);
40
+ if (inputSource.type === 'composite') {
41
+ const resolveInner = _resolveCompositeSources(path, inputSource, compositeFieldUsageWithoutNonCompositeFields(compositeFieldUsage, inputSource),
42
+ // This looks wonky, but what we're doing is taking the nested sources
43
+ // and "promoting" them to look like they're top level sources; we will
44
+ // then reverse this when we update the real narrowed resolution.
45
+ {
46
+ source: nested !== null && nested !== void 0 ? nested : inputSource.sources.map(s => ({ source: s, nested: [] })),
47
+ // Composite source inputs cannot have joins, so we don't need to
48
+ // pass in the narrowed resolution
49
+ joined: {},
50
+ });
51
+ if ('error' in resolveInner) {
52
+ newNarrowedSources.shift();
53
+ continue overSources;
54
+ }
55
+ base = { ...resolveInner.success };
56
+ newNarrowedSources[0] = {
57
+ source: inputSource,
58
+ nested: resolveInner.narrowedCompositeFieldResolution.source,
59
+ };
60
+ }
61
+ else {
62
+ base = { ...inputSource };
63
+ }
64
+ found = true;
65
+ base = {
66
+ ...base,
67
+ fields: [...nonCompositeFields, ...base.fields],
68
+ filterList: [
69
+ ...((_c = source.filterList) !== null && _c !== void 0 ? _c : []),
70
+ ...((_d = base.filterList) !== null && _d !== void 0 ? _d : []),
71
+ ],
72
+ };
73
+ break;
74
+ }
75
+ if (!found) {
76
+ return {
77
+ error: {
78
+ code: 'no_suitable_composite_source_input',
79
+ data: { fields: compositeFieldUsage.fields, path },
80
+ },
81
+ };
82
+ }
83
+ narrowedSources = newNarrowedSources;
84
+ }
85
+ else {
86
+ return { error: { code: 'not_a_composite_source', data: { path } } };
87
+ }
88
+ }
89
+ const fieldsByName = {};
90
+ const narrowedJoinedSources = (_e = narrowedCompositeFieldResolution === null || narrowedCompositeFieldResolution === void 0 ? void 0 : narrowedCompositeFieldResolution.joined) !== null && _e !== void 0 ? _e : {};
91
+ for (const field of base.fields) {
92
+ fieldsByName[(_f = field.as) !== null && _f !== void 0 ? _f : field.name] = field;
93
+ }
94
+ for (const [joinName, joinedUsage] of Object.entries(compositeFieldUsage.joinedUsage)) {
95
+ const join = fieldsByName[joinName];
96
+ const newPath = [...path, joinName];
97
+ if (join === undefined) {
98
+ return {
99
+ error: { code: 'composite_source_not_defined', data: { path: newPath } },
100
+ };
101
+ }
102
+ if (!(0, malloy_types_1.isJoined)(join) || !(0, malloy_types_1.isSourceDef)(join)) {
103
+ return {
104
+ error: { code: 'composite_source_not_a_join', data: { path: newPath } },
105
+ };
106
+ }
107
+ const resolved = _resolveCompositeSources(newPath, join, joinedUsage, narrowedJoinedSources[joinName]);
108
+ if ('error' in resolved) {
109
+ return resolved;
110
+ }
111
+ if (!(0, malloy_types_1.isJoinable)(resolved.success)) {
112
+ return {
113
+ error: {
114
+ code: 'composite_source_is_not_joinable',
115
+ data: { path: newPath },
116
+ },
117
+ };
118
+ }
119
+ fieldsByName[joinName] = {
120
+ ...resolved.success,
121
+ join: join.join,
122
+ as: (_g = join.as) !== null && _g !== void 0 ? _g : join.name,
123
+ onExpression: join.onExpression,
124
+ };
125
+ narrowedJoinedSources[joinName] = resolved.narrowedCompositeFieldResolution;
126
+ }
127
+ return {
128
+ success: { ...base, fields: Object.values(fieldsByName) },
129
+ narrowedCompositeFieldResolution: {
130
+ source: narrowedSources,
131
+ joined: narrowedJoinedSources,
132
+ },
133
+ };
134
+ }
135
+ function emptyNarrowedCompositeFieldResolution() {
136
+ return { source: undefined, joined: {} };
137
+ }
138
+ exports.emptyNarrowedCompositeFieldResolution = emptyNarrowedCompositeFieldResolution;
139
+ // Should always give the _full_ `compositeFieldUsage`, because we only
140
+ // cross off sources until we find one that works, but that does not
141
+ // guarantee that all the remaining sources will work.
142
+ function narrowCompositeFieldResolution(source, compositeFieldUsage, narrowedCompositeFieldResolution) {
143
+ const result = _resolveCompositeSources([], source, compositeFieldUsage, narrowedCompositeFieldResolution);
144
+ if ('success' in result) {
145
+ return {
146
+ narrowedCompositeFieldResolution: result.narrowedCompositeFieldResolution,
147
+ error: undefined,
148
+ };
149
+ }
150
+ return { narrowedCompositeFieldResolution: undefined, error: result.error };
151
+ }
152
+ exports.narrowCompositeFieldResolution = narrowCompositeFieldResolution;
153
+ function resolveCompositeSources(source, compositeFieldUsage) {
154
+ const result = _resolveCompositeSources([], source, compositeFieldUsage);
155
+ if ('success' in result) {
156
+ return { sourceDef: result.success, error: undefined };
157
+ }
158
+ return { sourceDef: undefined, error: result.error };
159
+ }
160
+ exports.resolveCompositeSources = resolveCompositeSources;
161
+ function compositeFieldUsagePaths(compositeFieldUsage) {
162
+ return [
163
+ ...compositeFieldUsage.fields.map(f => [f]),
164
+ ...Object.entries(compositeFieldUsage.joinedUsage)
165
+ .map(([joinName, joinedUsage]) => compositeFieldUsagePaths(joinedUsage).map(path => [joinName, ...path]))
166
+ .flat(),
167
+ ];
168
+ }
169
+ exports.compositeFieldUsagePaths = compositeFieldUsagePaths;
170
+ function formatCompositeFieldUsages(compositeFieldUsage) {
171
+ return compositeFieldUsagePaths(compositeFieldUsage)
172
+ .map(compositeFieldUsage => formatCompositeFieldUsage(compositeFieldUsage))
173
+ .join(', ');
174
+ }
175
+ exports.formatCompositeFieldUsages = formatCompositeFieldUsages;
176
+ function countCompositeFieldUsage(compositeFieldUsage) {
177
+ return Object.values(compositeFieldUsage.joinedUsage).reduce((a, b) => a + countCompositeFieldUsage(b), compositeFieldUsage.fields.length);
178
+ }
179
+ function isEmptyCompositeFieldUsage(compositeFieldUsage) {
180
+ return countCompositeFieldUsage(compositeFieldUsage) === 0;
181
+ }
182
+ exports.isEmptyCompositeFieldUsage = isEmptyCompositeFieldUsage;
183
+ function compositeFieldUsageIsPlural(compositeFieldUsage) {
184
+ return countCompositeFieldUsage(compositeFieldUsage) > 1;
185
+ }
186
+ exports.compositeFieldUsageIsPlural = compositeFieldUsageIsPlural;
187
+ function formatCompositeFieldUsage(compositeFieldUsage) {
188
+ return `\`${compositeFieldUsage.join('.')}\``;
189
+ }
190
+ exports.formatCompositeFieldUsage = formatCompositeFieldUsage;
191
+ function unique(values) {
192
+ return Array.from(new Set(values));
193
+ }
194
+ exports.unique = unique;
195
+ function mergeCompositeFieldUsage(...usages) {
196
+ const nonEmptyUsages = usages.filter(utils_1.isNotUndefined);
197
+ const joinNames = new Set(nonEmptyUsages.map(u => Object.keys(u.joinedUsage)).flat());
198
+ const joinedUsage = {};
199
+ for (const joinName of joinNames) {
200
+ joinedUsage[joinName] = mergeCompositeFieldUsage(...nonEmptyUsages
201
+ .map(u => u === null || u === void 0 ? void 0 : u.joinedUsage[joinName])
202
+ .filter(utils_1.isNotUndefined));
203
+ }
204
+ return {
205
+ fields: unique(nonEmptyUsages.map(u => u.fields).flat()),
206
+ joinedUsage,
207
+ };
208
+ }
209
+ exports.mergeCompositeFieldUsage = mergeCompositeFieldUsage;
210
+ function emptyCompositeFieldUsage() {
211
+ return { fields: [], joinedUsage: {} };
212
+ }
213
+ exports.emptyCompositeFieldUsage = emptyCompositeFieldUsage;
214
+ function arrayDifference(a, b) {
215
+ const bSet = new Set(b);
216
+ const ret = [];
217
+ for (const value of a) {
218
+ if (bSet.has(value))
219
+ continue;
220
+ ret.push(value);
221
+ }
222
+ return ret;
223
+ }
224
+ // Return all of `a`'s usage without any of `b`'s usage
225
+ function compositeFieldUsageDifference(a, b) {
226
+ return {
227
+ fields: arrayDifference(a.fields, b.fields),
228
+ joinedUsage: Object.fromEntries(Object.entries(a.joinedUsage)
229
+ .map(([joinName, joinedUsage]) => [
230
+ joinName,
231
+ joinName in b.joinedUsage
232
+ ? compositeFieldUsageDifference(joinedUsage, b.joinedUsage[joinName])
233
+ : joinedUsage,
234
+ ])
235
+ .filter(([_, joinedUsage]) => countCompositeFieldUsage(joinedUsage) > 0)),
236
+ };
237
+ }
238
+ exports.compositeFieldUsageDifference = compositeFieldUsageDifference;
239
+ function joinedCompositeFieldUsage(joinPath, compositeFieldUsage) {
240
+ if (joinPath.length === 0)
241
+ return compositeFieldUsage;
242
+ return joinedCompositeFieldUsage(joinPath.slice(0, -1), {
243
+ fields: [],
244
+ joinedUsage: { [joinPath[joinPath.length - 1]]: compositeFieldUsage },
245
+ });
246
+ }
247
+ exports.joinedCompositeFieldUsage = joinedCompositeFieldUsage;
248
+ function compositeFieldUsageJoinPaths(compositeFieldUsage) {
249
+ const joinsUsed = Object.keys(compositeFieldUsage.joinedUsage);
250
+ return [
251
+ ...joinsUsed.map(joinName => [joinName]),
252
+ ...joinsUsed
253
+ .map(joinName => compositeFieldUsageJoinPaths(compositeFieldUsage.joinedUsage[joinName]).map(path => [joinName, ...path]))
254
+ .flat(),
255
+ ];
256
+ }
257
+ exports.compositeFieldUsageJoinPaths = compositeFieldUsageJoinPaths;
258
+ function isCompositeField(fieldDef) {
259
+ var _a;
260
+ return 'e' in fieldDef && ((_a = fieldDef.e) === null || _a === void 0 ? void 0 : _a.node) === 'compositeField';
261
+ }
262
+ function getNonCompositeFields(source) {
263
+ return source.fields.filter(f => !isCompositeField(f));
264
+ }
265
+ // This is specifically for the case where the source `source` is a composite and the chosen input
266
+ // source is also a composite; if the `source` defines some fields outright, when resolving the inner
267
+ // composite, we don't want to include those fields.
268
+ function compositeFieldUsageWithoutNonCompositeFields(compositeFieldUsage, source) {
269
+ var _a;
270
+ const sourceFieldsByName = {};
271
+ for (const field of source.fields) {
272
+ sourceFieldsByName[(_a = field.as) !== null && _a !== void 0 ? _a : field.name] = field;
273
+ }
274
+ return {
275
+ fields: compositeFieldUsage.fields.filter(f => isCompositeField(sourceFieldsByName[f])),
276
+ joinedUsage: compositeFieldUsage.joinedUsage,
277
+ };
278
+ }
279
+ //# sourceMappingURL=composite_source_utils.js.map
@@ -1,5 +1,5 @@
1
1
  import { Dialect, DialectFieldList } from '../dialect';
2
- import { AggregateFunctionType, Annotation, CompiledQuery, Expr, FieldDef, Filtered, FunctionOverloadDef, FunctionParameterDef, JoinRelationship, ModelDef, OrderBy, OutputFieldNode, Parameter, ParameterNode, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SegmentFieldDef, StructDef, StructRef, TurtleDef, FunctionOrderBy, Argument, AggregateExpr, FilterCondition, GenericSQLExpr, FieldnameNode, FunctionCallNode, UngroupNode, SourceReferenceNode, SpreadExpr, FilteredExpr, SourceDef, AtomicFieldDef, BooleanFieldDef, QueryToMaterialize, PrepareResultOptions, CaseExpr } from './malloy_types';
2
+ import { AggregateFunctionType, Annotation, CompiledQuery, Expr, FieldDef, Filtered, FunctionOverloadDef, FunctionParameterDef, JoinRelationship, ModelDef, OrderBy, OutputFieldNode, ParameterNode, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, SegmentFieldDef, StructDef, StructRef, TurtleDef, FunctionOrderBy, Argument, AggregateExpr, FilterCondition, GenericSQLExpr, FieldnameNode, FunctionCallNode, UngroupNode, SourceReferenceNode, SpreadExpr, FilteredExpr, SourceDef, AtomicFieldDef, BooleanFieldDef, QueryToMaterialize, PrepareResultOptions, CaseExpr } from './malloy_types';
3
3
  import { Connection } from '../connection/types';
4
4
  import { AndChain } from './utils';
5
5
  import { QueryInfo } from '../dialect/dialect';
@@ -308,10 +308,10 @@ declare class QueryStruct extends QueryNode {
308
308
  connectionName: string;
309
309
  constructor(structDef: StructDef, sourceArguments: Record<string, Argument> | undefined, parent: ParentQueryStruct | ParentQueryModel, prepareResultOptions: PrepareResultOptions, referenceId?: string);
310
310
  maybeEmitParameterizedSourceUsage(): void;
311
- resolveParentParameterReferences(param: Parameter): Parameter;
311
+ private resolveParentParameterReferences;
312
312
  private _arguments;
313
313
  arguments(): Record<string, Argument>;
314
- addFieldsFromFieldList(fields: FieldDef[]): void;
314
+ private addFieldsFromFieldList;
315
315
  getAliasIdentifier(): string;
316
316
  getSQLIdentifier(): string;
317
317
  getIdentifier(): string;
@@ -878,6 +878,8 @@ class QueryField extends QueryNode {
878
878
  case 'functionDefaultOrderBy':
879
879
  case 'functionOrderBy':
880
880
  return '';
881
+ case 'compositeField':
882
+ return '{COMPOSITE_FIELD}';
881
883
  default:
882
884
  throw new Error(`Internal Error: Unknown expression node '${expr.node}' ${JSON.stringify(expr, undefined, 2)}`);
883
885
  }
@@ -3279,6 +3281,8 @@ class QueryStruct extends QueryNode {
3279
3281
  switch (this.structDef.type) {
3280
3282
  case 'table':
3281
3283
  return this.dialect.quoteTablePath(this.structDef.tablePath);
3284
+ case 'composite':
3285
+ return '{COMPOSITE SOURCE}';
3282
3286
  case 'finalize':
3283
3287
  return this.structDef.name;
3284
3288
  case 'sql_select':
@@ -3457,6 +3461,7 @@ class QueryModel {
3457
3461
  return new QueryStruct(structRef, sourceArguments, { model: this }, prepareResultOptions);
3458
3462
  }
3459
3463
  loadQuery(query, stageWriter, prepareResultOptions, emitFinalStage = false, isJoinedSubquery = false) {
3464
+ var _a;
3460
3465
  const malloy = '';
3461
3466
  if (!stageWriter) {
3462
3467
  stageWriter = new StageWriter(true, undefined);
@@ -3467,7 +3472,8 @@ class QueryModel {
3467
3472
  pipeline: query.pipeline,
3468
3473
  filterList: query.filterList,
3469
3474
  };
3470
- const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef, query.sourceArguments, prepareResultOptions), stageWriter, isJoinedSubquery);
3475
+ const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
3476
+ const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(structRef, query.sourceArguments, prepareResultOptions), stageWriter, isJoinedSubquery);
3471
3477
  const ret = q.generateSQLFromPipeline(stageWriter);
3472
3478
  if (emitFinalStage && q.parent.dialect.hasFinalStage) {
3473
3479
  // const fieldNames: string[] = [];
@@ -3486,13 +3492,14 @@ class QueryModel {
3486
3492
  };
3487
3493
  }
3488
3494
  compileQuery(query, prepareResultOptions, finalize = true) {
3489
- var _a;
3495
+ var _a, _b;
3490
3496
  let newModel;
3491
3497
  const m = newModel || this;
3492
3498
  const ret = m.loadQuery(query, undefined, prepareResultOptions, finalize, false);
3493
- const sourceExplore = typeof query.structRef === 'string'
3494
- ? query.structRef
3495
- : query.structRef.as || query.structRef.name;
3499
+ const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
3500
+ const sourceExplore = typeof structRef === 'string'
3501
+ ? structRef
3502
+ : structRef.as || structRef.name;
3496
3503
  // LTNote: I don't understand why this might be here. It should have happened in loadQuery...
3497
3504
  if (finalize && this.dialect.hasFinalStage) {
3498
3505
  ret.lastStageName = ret.stageWriter.addStage(
@@ -3505,7 +3512,7 @@ class QueryModel {
3505
3512
  sql: ret.stageWriter.generateSQLStages(),
3506
3513
  dependenciesToMaterialize: ret.stageWriter.dependenciesToMaterialize,
3507
3514
  materialization: (0, utils_2.shouldMaterialize)(query.annotation)
3508
- ? (0, utils_2.buildQueryMaterializationSpec)((_a = query.location) === null || _a === void 0 ? void 0 : _a.url, query.name, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.materializedTablePrefix)
3515
+ ? (0, utils_2.buildQueryMaterializationSpec)((_b = query.location) === null || _b === void 0 ? void 0 : _b.url, query.name, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.materializedTablePrefix)
3509
3516
  : undefined,
3510
3517
  structs: ret.structs,
3511
3518
  sourceExplore,
@@ -24,7 +24,7 @@ export type AnyExpr = ExprE | ExprOptionalE | ExprWithKids | ExprLeaf;
24
24
  export declare function exprHasKids(e: AnyExpr): e is ExprWithKids;
25
25
  export declare function exprHasE(e: AnyExpr): e is ExprE;
26
26
  export declare function exprIsLeaf(e: AnyExpr): boolean;
27
- export type Expr = BinaryExpr | UnaryExpr | FunctionCallNode | OutputFieldNode | FilterCondition | FilteredExpr | AggregateExpr | EmptyExpr | UngroupNode | FunctionParameterNode | SpreadExpr | AggregateOrderByNode | AggregateLimitNode | FieldnameNode | SourceReferenceNode | ParameterNode | NowNode | MeasureTimeExpr | TimeExtractExpr | TimeDeltaExpr | TimeTruncExpr | TimeLiteralNode | TypecastExpr | RegexMatchExpr | RegexLiteralNode | StringLiteralNode | NumberLiteralNode | BooleanLiteralNode | RecordLiteralNode | ArrayLiteralNode | FunctionOrderBy | GenericSQLExpr | NullNode | CaseExpr | InCompareExpr | ErrorNode;
27
+ export type Expr = BinaryExpr | UnaryExpr | FunctionCallNode | OutputFieldNode | FilterCondition | FilteredExpr | AggregateExpr | EmptyExpr | UngroupNode | FunctionParameterNode | SpreadExpr | AggregateOrderByNode | AggregateLimitNode | FieldnameNode | SourceReferenceNode | ParameterNode | NowNode | MeasureTimeExpr | TimeExtractExpr | TimeDeltaExpr | TimeTruncExpr | TimeLiteralNode | TypecastExpr | RegexMatchExpr | RegexLiteralNode | StringLiteralNode | NumberLiteralNode | BooleanLiteralNode | RecordLiteralNode | ArrayLiteralNode | FunctionOrderBy | GenericSQLExpr | NullNode | CaseExpr | InCompareExpr | CompositeFieldExpr | ErrorNode;
28
28
  interface HasTypeDef {
29
29
  typeDef: AtomicTypeDef;
30
30
  }
@@ -63,6 +63,7 @@ export interface FilterCondition extends ExprE {
63
63
  node: 'filterCondition';
64
64
  code: string;
65
65
  expressionType: ExpressionType;
66
+ compositeFieldUsage?: CompositeFieldUsage;
66
67
  }
67
68
  export interface FilteredExpr extends ExprWithKids {
68
69
  node: 'filteredExpr';
@@ -227,6 +228,9 @@ export interface CaseExpr extends ExprWithKids {
227
228
  caseElse?: Expr;
228
229
  };
229
230
  }
231
+ export interface CompositeFieldExpr extends ExprLeaf {
232
+ node: 'compositeField';
233
+ }
230
234
  export interface InCompareExpr extends ExprWithKids {
231
235
  node: 'in';
232
236
  not: boolean;
@@ -238,6 +242,7 @@ export interface InCompareExpr extends ExprWithKids {
238
242
  export type ExpressionType = 'scalar' | 'aggregate' | 'scalar_analytic' | 'aggregate_analytic' | 'ungrouped_aggregate';
239
243
  export interface Expression {
240
244
  e?: Expr;
245
+ compositeFieldUsage?: CompositeFieldUsage;
241
246
  expressionType?: ExpressionType;
242
247
  code?: string;
243
248
  }
@@ -410,6 +415,7 @@ export interface JoinBase {
410
415
  join: JoinType;
411
416
  matrixOperation?: MatrixOperation;
412
417
  onExpression?: Expr;
418
+ onCompositeFieldUsage?: CompositeFieldUsage;
413
419
  }
414
420
  export type Joinable = TableSourceDef | SQLSourceDef | QuerySourceDef | RecordFieldDef | ArrayDef;
415
421
  export type JoinFieldDef = JoinBase & Joinable;
@@ -492,6 +498,7 @@ export interface Query extends Pipeline, Filtered, HasLocation {
492
498
  sourceArguments?: Record<string, Argument>;
493
499
  annotation?: Annotation;
494
500
  modelAnnotation?: Annotation;
501
+ compositeResolvedSourceDef?: SourceDef;
495
502
  }
496
503
  export type NamedQuery = Query & NamedObject;
497
504
  export type PipeSegment = QuerySegment | IndexSegment | RawSegment;
@@ -539,6 +546,10 @@ export interface IndexSegment extends Filtered {
539
546
  alwaysJoins?: string[];
540
547
  }
541
548
  export declare function isIndexSegment(pe: PipeSegment): pe is IndexSegment;
549
+ export interface CompositeFieldUsage {
550
+ fields: string[];
551
+ joinedUsage: Record<string, CompositeFieldUsage>;
552
+ }
542
553
  export interface QuerySegment extends Filtered {
543
554
  type: 'reduce' | 'project' | 'partial';
544
555
  queryFields: QueryFieldDef[];
@@ -548,6 +559,7 @@ export interface QuerySegment extends Filtered {
548
559
  orderBy?: OrderBy[];
549
560
  queryTimezone?: string;
550
561
  alwaysJoins?: string[];
562
+ compositeFieldUsage?: CompositeFieldUsage;
551
563
  }
552
564
  export interface TurtleDef extends NamedObject, Pipeline {
553
565
  type: 'turtle';
@@ -573,6 +585,10 @@ export interface TableSourceDef extends SourceDefBase {
573
585
  type: 'table';
574
586
  tablePath: string;
575
587
  }
588
+ export interface CompositeSourceDef extends SourceDefBase {
589
+ type: 'composite';
590
+ sources: SourceDef[];
591
+ }
576
592
  export interface SQLStringSegment {
577
593
  sql: string;
578
594
  }
@@ -603,7 +619,7 @@ export interface FinalizeSourceDef extends SourceDefBase {
603
619
  }
604
620
  export declare function sourceBase(sd: SourceDefBase): SourceDefBase;
605
621
  export declare function isSourceDef(sd: NamedModelObject | FieldDef): sd is SourceDef;
606
- export type SourceDef = TableSourceDef | SQLSourceDef | QuerySourceDef | QueryResultDef | FinalizeSourceDef | NestSourceDef;
622
+ export type SourceDef = TableSourceDef | SQLSourceDef | QuerySourceDef | QueryResultDef | FinalizeSourceDef | NestSourceDef | CompositeSourceDef;
607
623
  /** Is this the "FROM" table of a query tree */
608
624
  export declare function isBaseTable(def: StructDef): def is SourceDef;
609
625
  export declare function isScalarArray(def: FieldDef | StructDef): boolean;
@@ -618,6 +634,7 @@ export type LeafExpressionType = Exclude<ExpressionValueType, JoinElementType |
618
634
  export type TypeInfo = {
619
635
  expressionType: ExpressionType;
620
636
  evalSpace: EvalSpace;
637
+ compositeFieldUsage: CompositeFieldUsage;
621
638
  };
622
639
  export type TypeDesc = ExpressionValueTypeDef & TypeInfo;
623
640
  export type FunctionParamType = ExpressionValueTypeDef | {
@@ -178,7 +178,11 @@ function fieldIsIntrinsic(f) {
178
178
  exports.fieldIsIntrinsic = fieldIsIntrinsic;
179
179
  function arrayEachFields(arrayOf) {
180
180
  return [
181
- { name: 'each', ...arrayOf, e: { node: 'field', path: ['value'] } },
181
+ {
182
+ name: 'each',
183
+ ...arrayOf,
184
+ e: { node: 'field', path: ['value'] },
185
+ },
182
186
  { name: 'value', ...arrayOf },
183
187
  ];
184
188
  }
@@ -192,7 +196,14 @@ function isMatrixOperation(x) {
192
196
  }
193
197
  exports.isMatrixOperation = isMatrixOperation;
194
198
  function isJoinable(sd) {
195
- return ['table', 'sql_select', 'query_source', 'array', 'record'].includes(sd.type);
199
+ return [
200
+ 'table',
201
+ 'sql_select',
202
+ 'query_source',
203
+ 'array',
204
+ 'record',
205
+ 'composite',
206
+ ].includes(sd.type);
196
207
  }
197
208
  exports.isJoinable = isJoinable;
198
209
  function isJoined(fd) {
@@ -314,7 +325,8 @@ function isSourceDef(sd) {
314
325
  sd.type === 'query_source' ||
315
326
  sd.type === 'query_result' ||
316
327
  sd.type === 'finalize' ||
317
- sd.type === 'nest_source');
328
+ sd.type === 'nest_source' ||
329
+ sd.type === 'composite');
318
330
  }
319
331
  exports.isSourceDef = isSourceDef;
320
332
  /** Is this the "FROM" table of a query tree */
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.215";
1
+ export declare const MALLOY_VERSION = "0.0.216";
package/dist/version.js CHANGED
@@ -2,5 +2,5 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MALLOY_VERSION = void 0;
4
4
  // generated with 'generate-version-file' script; do not edit manually
5
- exports.MALLOY_VERSION = '0.0.215';
5
+ exports.MALLOY_VERSION = '0.0.216';
6
6
  //# sourceMappingURL=version.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@malloydata/malloy",
3
- "version": "0.0.215",
3
+ "version": "0.0.216-dev241118202522",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",