@malloydata/malloy 0.0.307 → 0.0.308

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.
@@ -47,18 +47,6 @@ class FieldInstanceField {
47
47
  if ((0, malloy_types_1.hasExpression)(this.f.fieldDef)) {
48
48
  return FieldInstanceField.exprCompiler(this.parent, this.f.parent, this.f.fieldDef.e);
49
49
  }
50
- // Walk the tree and compute record aliases if needed
51
- for (let ancestor = this.f.parent; ancestor !== undefined; ancestor = ancestor.parent) {
52
- if (ancestor.structDef.type === 'record' &&
53
- (0, malloy_types_1.hasExpression)(ancestor.structDef) &&
54
- ancestor.recordAlias === undefined) {
55
- if (!ancestor.parent) {
56
- throw new Error('Inconceivable record ancestor with expression but no parent');
57
- }
58
- const aliasValue = FieldInstanceField.exprCompiler(this.parent, ancestor.parent, ancestor.structDef.e);
59
- ancestor.informOfAliasValue(aliasValue);
60
- }
61
- }
62
50
  return sqlFullChildReference(this.f.parent, this.f.fieldDef.name, this.f.parent.structDef.type === 'record'
63
51
  ? {
64
52
  result: this.parent,
@@ -77,14 +77,22 @@ export declare class QueryStruct {
77
77
  pathAliasMap: Map<string, string>;
78
78
  dialect: Dialect;
79
79
  connectionName: string;
80
- recordAlias?: string;
80
+ /**
81
+ * For fields which are a record, but the value is an expression
82
+ * we capture the context needed to generate the expression in
83
+ * QueryQuery.expandFields. Later in the compilation if a
84
+ * reference passes through this struct, this will call
85
+ * the expression compiler with the correct context
86
+ * to compute the record value.
87
+ */
88
+ computeRecordExpression?: () => string;
89
+ recordValue?: string;
81
90
  constructor(structDef: StructDef, sourceArguments: Record<string, Argument> | undefined, parent: ParentQueryStruct | ParentQueryModel, prepareResultOptions: PrepareResultOptions);
82
91
  private static turtleFieldMaker;
83
92
  static registerTurtleFieldMaker(maker: (field: TurtleDef, parent: QueryStruct) => QueryField): void;
84
93
  private _modelTag;
85
94
  modelCompilerFlags(): Tag;
86
95
  protected findFirstDialect(): string;
87
- informOfAliasValue(av: string): void;
88
96
  maybeEmitParameterizedSourceUsage(): void;
89
97
  private resolveParentParameterReferences;
90
98
  private _arguments;
@@ -219,9 +219,6 @@ class QueryStruct {
219
219
  }
220
220
  throw new Error('Cannot create QueryStruct from record with model parent');
221
221
  }
222
- informOfAliasValue(av) {
223
- this.recordAlias = av;
224
- }
225
222
  maybeEmitParameterizedSourceUsage() {
226
223
  var _a;
227
224
  if ((0, malloy_types_1.isSourceDef)(this.structDef)) {
@@ -373,8 +370,11 @@ class QueryStruct {
373
370
  // will have joins and thus be in the namespace. We can't compute it here
374
371
  // because we don't have access to the Query to call exprToSQL.
375
372
  if (this.structDef.type === 'record' && (0, malloy_types_1.hasExpression)(this.structDef)) {
376
- if (this.recordAlias) {
377
- return this.recordAlias;
373
+ if (this.computeRecordExpression) {
374
+ if (!this.recordValue) {
375
+ this.recordValue = this.computeRecordExpression();
376
+ }
377
+ return this.recordValue;
378
378
  }
379
379
  throw new Error('INTERNAL ERROR, record field alias not pre-computed');
380
380
  }
@@ -43,10 +43,18 @@ export declare class QueryQuery extends QueryField {
43
43
  };
44
44
  private addDependantPath;
45
45
  private dependenciesFromFieldUsage;
46
- findRecordAliases(context: QueryStruct, path: string[]): void;
47
46
  getSegmentFields(resultStruct: FieldInstanceResult): SegmentFieldDef[];
48
47
  private getDrillExpression;
49
48
  expandFields(resultStruct: FieldInstanceResult): void;
49
+ /**
50
+ * Recursively walks the input QueryStruct tree and sets up lazy expression
51
+ * compilation for all records with computed expressions, so that records with
52
+ * expression values have the correct context for evaluating them if needed.
53
+ *
54
+ * @param resultStruct - The FieldInstanceResult containing compilation context
55
+ * @param source - The QueryStruct to traverse (initially the query's parent/input)
56
+ */
57
+ expandSource(resultStruct: FieldInstanceResult, source: QueryStruct): void;
50
58
  generateSQLFilters(resultStruct: FieldInstanceResult, which: 'where' | 'having'): AndChain;
51
59
  prepare(_stageWriter: StageWriter | undefined): void;
52
60
  private findJoins;
@@ -157,14 +157,10 @@ class QueryQuery extends query_node_1.QueryField {
157
157
  resultStruct.addStructToJoin(this.parent, usage.uniqueKeyRequirement);
158
158
  }
159
159
  else {
160
- this.findRecordAliases(this.parent, usage.path);
161
160
  this.addDependantPath(resultStruct, this.parent, usage.path, usage.uniqueKeyRequirement);
162
161
  }
163
162
  continue;
164
163
  }
165
- if (usage.path.length > 1) {
166
- this.findRecordAliases(this.parent, usage.path);
167
- }
168
164
  }
169
165
  const expandedUngroupings = 'expandedUngroupings' in this.firstSegment
170
166
  ? this.firstSegment.expandedUngroupings || []
@@ -207,30 +203,6 @@ class QueryQuery extends query_node_1.QueryField {
207
203
  }
208
204
  }
209
205
  }
210
- /*
211
- ** Later on, when a record is referenced, the context needed to translate the
212
- ** reference won't exist, so we translate them all in prepare. The better fix
213
- ** involves understanding more about what a "translation state" is and how
214
- ** to create it at the moment when a field is referenced, but I couldn't do
215
- ** that at the time I did this work. TODO come back and do that.
216
- */
217
- findRecordAliases(context, path) {
218
- for (const seg of path) {
219
- const field = context.getChildByName(seg);
220
- if (!field) {
221
- throw new Error('findRecordAliases: field not found: ' + path.join('.'));
222
- }
223
- if (field instanceof query_node_1.QueryFieldStruct) {
224
- const qs = field.queryStruct;
225
- if (qs.structDef.type === 'record' &&
226
- (0, malloy_types_1.hasExpression)(qs.structDef) &&
227
- qs.parent) {
228
- qs.informOfAliasValue((0, expression_compiler_1.exprToSQL)(this.rootResult, qs.parent, qs.structDef.e));
229
- }
230
- context = qs;
231
- }
232
- }
233
- }
234
206
  getSegmentFields(resultStruct) {
235
207
  const fs = resultStruct.firstSegment;
236
208
  return fs.type === 'index'
@@ -284,6 +256,31 @@ class QueryQuery extends query_node_1.QueryField {
284
256
  resultIndex++;
285
257
  }
286
258
  }
259
+ /**
260
+ * Recursively walks the input QueryStruct tree and sets up lazy expression
261
+ * compilation for all records with computed expressions, so that records with
262
+ * expression values have the correct context for evaluating them if needed.
263
+ *
264
+ * @param resultStruct - The FieldInstanceResult containing compilation context
265
+ * @param source - The QueryStruct to traverse (initially the query's parent/input)
266
+ */
267
+ expandSource(resultStruct, source) {
268
+ for (const field of source.nameMap.values()) {
269
+ if (field instanceof query_node_1.QueryFieldStruct) {
270
+ const qs = field.queryStruct;
271
+ // Set up closure if this is a record with expression
272
+ if (qs.structDef.type === 'record' &&
273
+ (0, malloy_types_1.hasExpression)(qs.structDef) &&
274
+ qs.parent) {
275
+ const parent = qs.parent;
276
+ const e = qs.structDef.e;
277
+ qs.computeRecordExpression = () => (0, expression_compiler_1.exprToSQL)(resultStruct, parent, e);
278
+ }
279
+ // Recurse into this structure
280
+ this.expandSource(resultStruct, qs);
281
+ }
282
+ }
283
+ }
287
284
  generateSQLFilters(resultStruct, which
288
285
  // filterList: FilterCondition[] | undefined = undefined
289
286
  ) {
@@ -307,6 +304,7 @@ class QueryQuery extends query_node_1.QueryField {
307
304
  }
308
305
  prepare(_stageWriter) {
309
306
  if (!this.prepared) {
307
+ this.expandSource(this.rootResult, this.parent);
310
308
  // Add the root base join to the joins map
311
309
  this.rootResult.addStructToJoin(this.parent, undefined);
312
310
  // Expand fields (just adds them to result, no dependency tracking)
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.307";
1
+ export declare const MALLOY_VERSION = "0.0.308";
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.307';
5
+ exports.MALLOY_VERSION = '0.0.308';
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.307",
3
+ "version": "0.0.308",
4
4
  "license": "MIT",
5
5
  "exports": {
6
6
  ".": "./dist/index.js",
@@ -41,9 +41,9 @@
41
41
  "generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
42
42
  },
43
43
  "dependencies": {
44
- "@malloydata/malloy-filter": "0.0.307",
45
- "@malloydata/malloy-interfaces": "0.0.307",
46
- "@malloydata/malloy-tag": "0.0.307",
44
+ "@malloydata/malloy-filter": "0.0.308",
45
+ "@malloydata/malloy-interfaces": "0.0.308",
46
+ "@malloydata/malloy-tag": "0.0.308",
47
47
  "antlr4ts": "^0.5.0-alpha.4",
48
48
  "assert": "^2.0.0",
49
49
  "jaro-winkler": "^0.2.8",