@malloydata/malloy 0.0.390 → 0.0.392

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 (127) hide show
  1. package/dist/api/asynchronous.js +0 -3
  2. package/dist/api/foundation/compile.d.ts +1 -1
  3. package/dist/api/foundation/config.d.ts +80 -8
  4. package/dist/api/foundation/config.js +151 -69
  5. package/dist/api/foundation/config_compile.js +27 -35
  6. package/dist/api/foundation/config_discover.js +5 -9
  7. package/dist/api/foundation/config_overlays.d.ts +6 -0
  8. package/dist/api/foundation/config_overlays.js +12 -0
  9. package/dist/api/foundation/config_resolve.d.ts +4 -1
  10. package/dist/api/foundation/config_resolve.js +64 -4
  11. package/dist/api/foundation/core.d.ts +75 -2
  12. package/dist/api/foundation/core.js +104 -6
  13. package/dist/api/foundation/index.d.ts +2 -0
  14. package/dist/api/foundation/readers.js +1 -1
  15. package/dist/api/foundation/runtime.d.ts +68 -2
  16. package/dist/api/foundation/runtime.js +212 -10
  17. package/dist/api/foundation/types.d.ts +2 -1
  18. package/dist/index.d.ts +3 -1
  19. package/dist/lang/ast/ast-utils.js +0 -1
  20. package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +1 -1
  21. package/dist/lang/ast/expressions/expr-aggregate-function.js +9 -8
  22. package/dist/lang/ast/expressions/expr-coalesce.d.ts +1 -1
  23. package/dist/lang/ast/expressions/expr-coalesce.js +2 -3
  24. package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
  25. package/dist/lang/ast/expressions/expr-count.js +6 -4
  26. package/dist/lang/ast/expressions/expr-filter-expr.js +0 -1
  27. package/dist/lang/ast/expressions/expr-func.js +9 -4
  28. package/dist/lang/ast/expressions/expr-given.d.ts +18 -0
  29. package/dist/lang/ast/expressions/expr-given.js +69 -0
  30. package/dist/lang/ast/expressions/expr-granular-time.d.ts +1 -1
  31. package/dist/lang/ast/expressions/expr-id-reference.js +3 -2
  32. package/dist/lang/ast/expressions/expr-now.js +0 -1
  33. package/dist/lang/ast/expressions/expr-props.d.ts +132 -132
  34. package/dist/lang/ast/expressions/expr-props.js +2 -2
  35. package/dist/lang/ast/expressions/expr-ungroup.d.ts +1 -1
  36. package/dist/lang/ast/expressions/expr-ungroup.js +4 -4
  37. package/dist/lang/ast/expressions/for-range.d.ts +1 -1
  38. package/dist/lang/ast/expressions/function-ordering.d.ts +1 -1
  39. package/dist/lang/ast/expressions/function-ordering.js +2 -2
  40. package/dist/lang/ast/expressions/time-literal.d.ts +3 -3
  41. package/dist/lang/ast/field-space/include-utils.js +2 -2
  42. package/dist/lang/ast/field-space/index-field-space.js +18 -23
  43. package/dist/lang/ast/field-space/passthrough-space.d.ts +1 -1
  44. package/dist/lang/ast/field-space/query-spaces.d.ts +6 -2
  45. package/dist/lang/ast/field-space/query-spaces.js +29 -19
  46. package/dist/lang/ast/field-space/reference-field.js +1 -1
  47. package/dist/lang/ast/field-space/rename-space-field.d.ts +1 -1
  48. package/dist/lang/ast/field-space/rename-space-field.js +2 -2
  49. package/dist/lang/ast/field-space/struct-space-field-base.js +2 -3
  50. package/dist/lang/ast/index.d.ts +2 -0
  51. package/dist/lang/ast/index.js +2 -0
  52. package/dist/lang/ast/query-builders/index-builder.d.ts +1 -1
  53. package/dist/lang/ast/query-builders/index-builder.js +4 -3
  54. package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
  55. package/dist/lang/ast/query-builders/reduce-builder.js +4 -5
  56. package/dist/lang/ast/query-elements/query-arrow.js +3 -2
  57. package/dist/lang/ast/query-elements/query-base.d.ts +1 -1
  58. package/dist/lang/ast/query-elements/query-base.js +1 -1
  59. package/dist/lang/ast/query-elements/query-refine.js +3 -1
  60. package/dist/lang/ast/query-items/field-declaration.js +2 -2
  61. package/dist/lang/ast/query-properties/drill.js +6 -6
  62. package/dist/lang/ast/query-properties/filters.js +2 -2
  63. package/dist/lang/ast/query-properties/nest.js +3 -3
  64. package/dist/lang/ast/source-elements/composite-source.js +5 -3
  65. package/dist/lang/ast/source-elements/named-source.js +4 -0
  66. package/dist/lang/ast/source-elements/sql-source.js +2 -2
  67. package/dist/lang/ast/source-elements/table-source.js +3 -1
  68. package/dist/lang/ast/source-properties/join.js +4 -4
  69. package/dist/lang/ast/source-query-elements/sq-reference.js +2 -1
  70. package/dist/lang/ast/statements/define-given.d.ts +29 -0
  71. package/dist/lang/ast/statements/define-given.js +163 -0
  72. package/dist/lang/ast/statements/import-statement.js +72 -9
  73. package/dist/lang/ast/typedesc-utils.d.ts +3 -1
  74. package/dist/lang/ast/typedesc-utils.js +4 -47
  75. package/dist/lang/ast/types/expr-value.js +2 -3
  76. package/dist/lang/ast/types/expression-def.d.ts +2 -2
  77. package/dist/lang/ast/types/expression-def.js +2 -2
  78. package/dist/lang/ast/types/malloy-element.d.ts +5 -15
  79. package/dist/lang/ast/types/malloy-element.js +113 -1
  80. package/dist/lang/ast/types/space-field.js +7 -9
  81. package/dist/lang/ast/view-elements/reference-view.js +6 -5
  82. package/dist/lang/ast/view-elements/refine-utils.js +1 -1
  83. package/dist/lang/composite-source-utils.d.ts +30 -15
  84. package/dist/lang/composite-source-utils.js +234 -64
  85. package/dist/lang/lib/Malloy/MalloyLexer.d.ts +171 -169
  86. package/dist/lang/lib/Malloy/MalloyLexer.js +1194 -1178
  87. package/dist/lang/lib/Malloy/MalloyParser.d.ts +408 -334
  88. package/dist/lang/lib/Malloy/MalloyParser.js +3062 -2561
  89. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +68 -0
  90. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +43 -0
  91. package/dist/lang/malloy-to-ast.d.ts +13 -1
  92. package/dist/lang/malloy-to-ast.js +90 -11
  93. package/dist/lang/parse-log.d.ts +8 -0
  94. package/dist/lang/prettify/filter-type.d.ts +3 -0
  95. package/dist/lang/prettify/filter-type.js +38 -0
  96. package/dist/lang/prettify/formatter.js +6 -0
  97. package/dist/lang/prettify/inline-renderer.js +20 -0
  98. package/dist/lang/prettify/rules.d.ts +1 -1
  99. package/dist/lang/prettify/rules.js +1 -0
  100. package/dist/lang/prettify/sections.js +2 -0
  101. package/dist/lang/prettify/tokens.js +2 -0
  102. package/dist/lang/test/expr-to-str.js +2 -0
  103. package/dist/lang/test/parse-expects.d.ts +1 -0
  104. package/dist/lang/test/parse-expects.js +27 -10
  105. package/dist/model/constant_expression_compiler.js +1 -0
  106. package/dist/model/expression_compiler.d.ts +2 -1
  107. package/dist/model/expression_compiler.js +41 -1
  108. package/dist/model/given_binding.d.ts +2 -0
  109. package/dist/model/given_binding.js +204 -0
  110. package/dist/model/index.d.ts +1 -1
  111. package/dist/model/index.js +2 -1
  112. package/dist/model/malloy_types.d.ts +163 -36
  113. package/dist/model/malloy_types.js +97 -0
  114. package/dist/model/query_model_contract.d.ts +2 -1
  115. package/dist/model/query_model_impl.d.ts +2 -1
  116. package/dist/model/query_model_impl.js +7 -0
  117. package/dist/model/query_node.d.ts +2 -1
  118. package/dist/model/source_def_utils.d.ts +2 -1
  119. package/dist/model/source_def_utils.js +4 -0
  120. package/dist/model/utils.d.ts +14 -1
  121. package/dist/model/utils.js +41 -0
  122. package/dist/to_stable.js +1 -1
  123. package/dist/util/closest_match.d.ts +9 -0
  124. package/dist/util/closest_match.js +47 -0
  125. package/dist/version.d.ts +1 -1
  126. package/dist/version.js +1 -1
  127. package/package.json +4 -4
@@ -36,25 +36,23 @@ class SpaceField extends space_entry_1.SpaceEntry {
36
36
  ...def,
37
37
  expressionType,
38
38
  evalSpace: 'input',
39
- fieldUsage:
40
- // Use the composite field usage in the def if it exists, otherwise, if the
41
- // field has an e whic is a composite field, then the composite field usage
42
- // should be just the name of the field.
43
- (_a = def.fieldUsage) !== null && _a !== void 0 ? _a : (((_b = def.e) === null || _b === void 0 ? void 0 : _b.node) === 'compositeField'
44
- ? [{ path: [def.name], at: def.location }]
45
- : []),
39
+ // Use the composite field usage in the def if it exists, otherwise, if
40
+ // the field has an e which is a composite field, then the composite
41
+ // field usage should be just the name of the field.
42
+ refSummary: (_a = def.refSummary) !== null && _a !== void 0 ? _a : (((_b = def.e) === null || _b === void 0 ? void 0 : _b.node) === 'compositeField'
43
+ ? { fieldUsage: [{ path: [def.name], at: def.location }] }
44
+ : undefined),
46
45
  };
47
46
  return ref;
48
47
  }
49
48
  turtleTypeFromTurtleDef(def) {
50
- var _a;
51
49
  const turtleTypeDef = {
52
50
  type: 'turtle',
53
51
  pipeline: def.pipeline,
54
52
  };
55
53
  return {
56
54
  ...turtleTypeDef,
57
- fieldUsage: (_a = def.fieldUsage) !== null && _a !== void 0 ? _a : [],
55
+ refSummary: def.refSummary,
58
56
  // TODO these are sorta weird for a turtle...
59
57
  expressionType: 'scalar',
60
58
  evalSpace: 'constant',
@@ -83,9 +83,11 @@ class ReferenceView extends view_1.View {
83
83
  const newSegment = {
84
84
  type: 'reduce',
85
85
  queryFields: [this.reference.refToField],
86
- fieldUsage: (0, composite_source_utils_1.mergeFieldUsage)(fieldDef.fieldUsage, [
87
- { path: this.reference.refToField.path, at: this.reference.location },
88
- ]),
86
+ refSummary: (0, composite_source_utils_1.mergeRefSummaries)(fieldDef.refSummary, {
87
+ fieldUsage: [
88
+ { path: this.reference.refToField.path, at: this.reference.location },
89
+ ],
90
+ }),
89
91
  outputStruct,
90
92
  // An atomic lens results in a array segment if it is a scalar
91
93
  isRepeated: (0, malloy_types_1.expressionIsScalar)(fieldDef.expressionType),
@@ -136,7 +138,6 @@ class ReferenceView extends view_1.View {
136
138
  }
137
139
  }
138
140
  getRefinement(inputFS) {
139
- var _a;
140
141
  const { pipeline, error } = this._pipelineComp(inputFS, {
141
142
  forRefinement: true,
142
143
  });
@@ -151,7 +152,7 @@ class ReferenceView extends view_1.View {
151
152
  return segment;
152
153
  return {
153
154
  ...segment,
154
- fieldUsage: (_a = segment.fieldUsage) === null || _a === void 0 ? void 0 : _a.map(u => ({
155
+ refSummary: (0, malloy_types_1.mapFieldUsage)(segment.refSummary, u => ({
155
156
  ...u,
156
157
  at: this.reference.location,
157
158
  })),
@@ -102,7 +102,7 @@ function refine(logTo, refineTo, refineFrom) {
102
102
  if (missingOut.length > 0) {
103
103
  logTo.logError('name-conflict-in-refinement', `missing output fields in refinement: ${missingOut.join(', ')}`);
104
104
  }
105
- to.fieldUsage = (0, composite_source_utils_1.mergeFieldUsage)(to.fieldUsage, from.fieldUsage);
105
+ to.refSummary = (0, composite_source_utils_1.mergeRefSummaries)(to.refSummary, from.refSummary);
106
106
  }
107
107
  else if (from.type === 'index' && to.type === 'index') {
108
108
  to.indexFields = [...from.indexFields, ...to.indexFields];
@@ -1,9 +1,9 @@
1
1
  import type { MalloyElement } from './ast';
2
- import type { FieldUsage, PipeSegment, SourceDef, RequiredGroupBy, Annotation, PartitionCompositeDesc, StructDef } from '../model/malloy_types';
2
+ import type { FieldUsage, FieldUsageEntry, GivenUsage, PipeSegment, RefSummary, SourceDef, RequiredGroupBy, Annotation, PartitionCompositeDesc, StructDef } from '../model/malloy_types';
3
3
  type CompositeCouldNotFindFieldError = {
4
4
  code: 'could_not_find_field';
5
5
  data: {
6
- field: FieldUsage;
6
+ field: FieldUsageEntry;
7
7
  };
8
8
  };
9
9
  type CompositeError = {
@@ -31,17 +31,17 @@ type CompositeError = {
31
31
  data: {
32
32
  failures: CompositeFailure[];
33
33
  path: string[];
34
- usage: FieldUsage[];
34
+ usage: FieldUsage;
35
35
  };
36
36
  };
37
37
  type CompositeIssue = {
38
38
  type: 'join-failed';
39
39
  failures: CompositeFailure[];
40
40
  path: string[];
41
- firstUsage: FieldUsage;
41
+ firstUsage: FieldUsageEntry;
42
42
  } | {
43
43
  type: 'missing-field';
44
- field: FieldUsage;
44
+ field: FieldUsageEntry;
45
45
  } | {
46
46
  type: 'missing-required-group-by';
47
47
  requiredGroupBy: RequiredGroupBy;
@@ -70,18 +70,33 @@ export declare function resolveCompositeSources(source: SourceDef, segment: Pipe
70
70
  error: CompositeError;
71
71
  sourceDef: undefined;
72
72
  };
73
- export declare function fieldUsagePaths(fieldUsage: FieldUsage[]): string[][];
74
- export declare function formatFieldUsages(fieldUsage: FieldUsage[]): string;
75
- export declare function isEmptyFieldUsage(fieldUsage: FieldUsage[]): boolean;
76
- export declare function fieldUsageIsPlural(fieldUsage: FieldUsage[]): boolean;
73
+ export declare function fieldUsagePaths(fieldUsage: FieldUsage): string[][];
74
+ export declare function formatFieldUsages(fieldUsage: FieldUsage): string;
75
+ export declare function isEmptyFieldUsage(fieldUsage: FieldUsage): boolean;
76
+ export declare function fieldUsageIsPlural(fieldUsage: FieldUsage): boolean;
77
77
  export declare function formatFieldUsage(fieldUsage: string[]): string;
78
78
  export declare function unique<T>(values: T[]): T[];
79
- export declare function mergeFieldUsage(...usages: FieldUsage[][]): FieldUsage[];
80
- export declare function mergeFieldUsage(...usages: (FieldUsage[] | undefined)[]): FieldUsage[] | undefined;
81
- export declare function fieldUsageDifference(a: FieldUsage[], b: FieldUsage[]): FieldUsage[];
82
- export declare function emptyFieldUsage(): FieldUsage[];
83
- export declare function joinedFieldUsage(joinPath: string[], fieldUsage: FieldUsage[]): FieldUsage[];
84
- export declare function fieldUsageJoinPaths(fieldUsage: FieldUsage[]): string[][];
79
+ export declare function mergeFieldUsage(...usages: FieldUsage[]): FieldUsage;
80
+ export declare function mergeFieldUsage(...usages: (FieldUsage | undefined)[]): FieldUsage | undefined;
81
+ /**
82
+ * Concat-merge of `GivenUsage` arrays. Same shape as `mergeFieldUsage`:
83
+ * undefined inputs are skipped, all-empty result returns undefined,
84
+ * duplicates by id are kept (each carries its own `at` for diagnostics).
85
+ */
86
+ export declare function mergeGivenUsage(...usages: (GivenUsage | undefined)[]): GivenUsage | undefined;
87
+ /**
88
+ * Combine N `RefSummary` values into one. Merges both `fieldUsage` and
89
+ * `givenUsage`; when more reference-tracking slots land on `RefSummary`,
90
+ * their merge logic lands here instead of being duplicated at every call
91
+ * site.
92
+ */
93
+ export declare function mergeRefSummaries(...rss: (RefSummary | undefined)[]): RefSummary | undefined;
94
+ export declare function fieldUsageDifference(a: FieldUsage, b: FieldUsage): FieldUsageEntry[];
95
+ export declare function emptyFieldUsage(): FieldUsage;
96
+ export declare function joinedFieldUsage(joinPath: string[], fieldUsage: FieldUsage): FieldUsage;
97
+ export declare function fieldUsageJoinPaths(fieldUsage: FieldUsage): string[][];
98
+ export declare function computeQueryGivenUsage(pipeline: PipeSegment[]): GivenUsage;
99
+ export declare function givenUsageOfSource(sd: SourceDef): GivenUsage;
85
100
  export declare function checkRequiredGroupBys(compositeResolvedSourceDef: SourceDef, segment: PipeSegment): RequiredGroupBy[];
86
101
  export declare function pathEq(a: string[], b: string[]): boolean;
87
102
  export declare function pathBegins(path: string[], prefix: string[]): boolean;
@@ -16,10 +16,14 @@ exports.fieldUsageIsPlural = fieldUsageIsPlural;
16
16
  exports.formatFieldUsage = formatFieldUsage;
17
17
  exports.unique = unique;
18
18
  exports.mergeFieldUsage = mergeFieldUsage;
19
+ exports.mergeGivenUsage = mergeGivenUsage;
20
+ exports.mergeRefSummaries = mergeRefSummaries;
19
21
  exports.fieldUsageDifference = fieldUsageDifference;
20
22
  exports.emptyFieldUsage = emptyFieldUsage;
21
23
  exports.joinedFieldUsage = joinedFieldUsage;
22
24
  exports.fieldUsageJoinPaths = fieldUsageJoinPaths;
25
+ exports.computeQueryGivenUsage = computeQueryGivenUsage;
26
+ exports.givenUsageOfSource = givenUsageOfSource;
23
27
  exports.checkRequiredGroupBys = checkRequiredGroupBys;
24
28
  exports.pathEq = pathEq;
25
29
  exports.pathBegins = pathBegins;
@@ -39,7 +43,7 @@ sources) {
39
43
  let anyComposites = false;
40
44
  let joinsProcessed = false;
41
45
  const nonCompositeFields = getNonCompositeFields(source);
42
- const expandedForError = onlyCompositeUsage(expandFieldUsage(fieldUsage, rootFields).result, source.fields);
46
+ const expandedForError = onlyCompositeUsage(expandRefUsage({ fieldUsage }, rootFields).fieldUsage, source.fields);
43
47
  if (source.type === 'composite') {
44
48
  let found = false;
45
49
  anyComposites = true;
@@ -66,7 +70,7 @@ sources) {
66
70
  }
67
71
  const fieldUsageWithWheres = (_b = mergeFieldUsage(getFieldUsageFromFilterList(inputSource), fieldUsage)) !== null && _b !== void 0 ? _b : [];
68
72
  const fieldsForLookup = [...nonCompositeFields, ...inputSource.fields];
69
- const expanded = expandFieldUsage(fieldUsageWithWheres, fieldsForLookup);
73
+ const expanded = expandRefUsage({ fieldUsage: fieldUsageWithWheres }, fieldsForLookup);
70
74
  if (expanded.missingFields.length > 0) {
71
75
  // A lookup failed while expanding, which means this source certainly won't work
72
76
  for (const missingField of expanded.missingFields) {
@@ -81,7 +85,7 @@ sources) {
81
85
  abort();
82
86
  continue overSources;
83
87
  }
84
- const expandedCategorized = categorizeFieldUsage(expanded.result);
88
+ const expandedCategorized = categorizeFieldUsage(expanded.fieldUsage);
85
89
  const compositeUsageInThisSource = onlyCompositeUsage(expandedCategorized.sourceUsage, source.fields);
86
90
  for (const usage of compositeUsageInThisSource) {
87
91
  if (usage.path.length > 0 && !fieldNames.has(usage.path[0])) {
@@ -177,7 +181,7 @@ sources) {
177
181
  }
178
182
  else if (source.partitionComposite !== undefined) {
179
183
  anyComposites = true;
180
- const expanded = expandFieldUsage(fieldUsage, rootFields).result;
184
+ const expanded = expandRefUsage({ fieldUsage }, rootFields).fieldUsage;
181
185
  // TODO possibly abort if expanded has missing fields...
182
186
  const expandedCategorized = categorizeFieldUsage(expanded);
183
187
  const { partitionFilter, issues } = getPartitionCompositeFilter(source.partitionComposite, expandedCategorized.sourceUsage);
@@ -199,7 +203,7 @@ sources) {
199
203
  };
200
204
  }
201
205
  if (!joinsProcessed) {
202
- const expanded = expandFieldUsage(fieldUsage, getJoinFields(rootFields, path));
206
+ const expanded = expandRefUsage({ fieldUsage }, getJoinFields(rootFields, path));
203
207
  if (expanded.missingFields.length > 0) {
204
208
  return {
205
209
  error: {
@@ -208,7 +212,7 @@ sources) {
208
212
  },
209
213
  };
210
214
  }
211
- const joinResult = processJoins(path, base, rootFields, nests, categorizeFieldUsage(expanded.result));
215
+ const joinResult = processJoins(path, base, rootFields, nests, categorizeFieldUsage(expanded.fieldUsage));
212
216
  if (joinResult.errors.length > 0) {
213
217
  return { error: joinResult.errors[0].error };
214
218
  }
@@ -230,11 +234,11 @@ function onlyCompositeUsage(fieldUsage, fields) {
230
234
  });
231
235
  }
232
236
  function getExpandedSegment(segment, inputSource) {
233
- var _a;
237
+ var _a, _b;
234
238
  if (segment.type === 'raw')
235
239
  return segment;
236
240
  const sourceExtensions = (0, malloy_types_1.isQuerySegment)(segment)
237
- ? (_a = segment.extendSource) !== null && _a !== void 0 ? _a : []
241
+ ? ((_a = segment.extendSource) !== null && _a !== void 0 ? _a : [])
238
242
  : [];
239
243
  const fields = mergeFields(inputSource.fields, sourceExtensions);
240
244
  const collectedUngroupings = [];
@@ -277,13 +281,30 @@ function getExpandedSegment(segment, inputSource) {
277
281
  });
278
282
  updatedSegment = { ...segment, queryFields: updatedQueryFields };
279
283
  }
280
- const allFieldUsage = mergeFieldUsage(getFieldUsageFromFilterList(inputSource), segment.fieldUsage);
281
- const expanded = expandFieldUsage(allFieldUsage || [], fields);
284
+ // Seed for the walker:
285
+ // - segment.refSummary: everything the segment names directly.
286
+ // - getFieldUsageFromFilterList(inputSource): field paths in the input
287
+ // source's `where:` clauses, so the walker can expand them through joins.
288
+ // - givenUsageOfSource(inputSource): every given the input source needs,
289
+ // including any hiding inside an embedded Query / SQL segments /
290
+ // composite branches.
291
+ // Plus segment.alwaysJoins for the walker to unconditionally activate
292
+ // (segment-level direct `join_one:` etc., where activation isn't driven
293
+ // by any field reference).
294
+ const seed = mergeRefSummaries(segment.refSummary, {
295
+ fieldUsage: getFieldUsageFromFilterList(inputSource),
296
+ givenUsage: givenUsageOfSource(inputSource),
297
+ });
298
+ const alwaysJoinNames = (0, malloy_types_1.isQuerySegment)(segment) || (0, malloy_types_1.isIndexSegment)(segment)
299
+ ? ((_b = segment.alwaysJoins) !== null && _b !== void 0 ? _b : [])
300
+ : [];
301
+ const expanded = expandRefUsage(seed, fields, alwaysJoinNames);
282
302
  // Merge ungroupings from direct collection and field expansion
283
303
  const allUngroupings = [...collectedUngroupings, ...expanded.ungroupings];
284
304
  return {
285
305
  ...updatedSegment,
286
- expandedFieldUsage: expanded.result,
306
+ expandedFieldUsage: expanded.fieldUsage,
307
+ expandedGivenUsage: expanded.givenUsage,
287
308
  activeJoins: expanded.activeJoins,
288
309
  expandedUngroupings: allUngroupings,
289
310
  };
@@ -326,21 +347,37 @@ function findActiveJoins(dependencies) {
326
347
  return sorted;
327
348
  }
328
349
  /**
329
- * Given a list of field usage requests, expand to include all the join on and join filter expressions
330
- * needed to be able to reference those fields.
350
+ * Given a seed `RefSummary` (field usage + given usage referenced directly by
351
+ * the caller), expand to include everything reachable through the join graph:
352
+ *
353
+ * - field usage: the seed fields plus any field usage on `on:` and `where:`
354
+ * clauses of joins activated to reach them, recursively.
355
+ * - given usage: the seed given usage plus the given usage of every
356
+ * atomic-field def the walker dequeues and every join it activates.
331
357
  *
332
358
  * @returns An object containing:
333
- * - `result`: The expanded field usage, including usages from necessary joins
359
+ * - `fieldUsage`: The expanded field usage, including usages from necessary joins
360
+ * - `givenUsage`: The expanded given usage, seed plus every traversed def/join
334
361
  * - `missingFields`: References to fields which could not be resolved
335
362
  * - `activeJoins`: Topologically sorted list of joins needed to resolve these uses
336
363
  */
337
- function expandFieldUsage(fieldUsage, fields) {
338
- var _a;
364
+ function expandRefUsage(seed, fields, alwaysJoinNames = []) {
339
365
  const seen = {};
340
366
  const missingFields = [];
341
367
  const toProcess = [];
342
368
  const activeJoinGraph = {};
343
369
  const ungroupings = [];
370
+ const givenUsage = [];
371
+ const seenGivens = new Set();
372
+ function addGivens(items) {
373
+ for (const g of items) {
374
+ if (seenGivens.has(g.id))
375
+ continue;
376
+ seenGivens.add(g.id);
377
+ givenUsage.push(g);
378
+ }
379
+ }
380
+ addGivens((0, malloy_types_1.givenUsageFrom)(seed));
344
381
  function mergeIntoSeen(usage) {
345
382
  var _a;
346
383
  const key = (0, utils_2.pathToKey)('field', usage.path);
@@ -361,12 +398,49 @@ function expandFieldUsage(fieldUsage, fields) {
361
398
  return false;
362
399
  }
363
400
  // Initialize: mark original inputs and add them to processing queue
364
- for (const usage of fieldUsage) {
401
+ for (const usage of (0, malloy_types_1.fieldUsageFrom)(seed)) {
365
402
  mergeIntoSeen(usage);
366
403
  toProcess.push(usage);
367
404
  }
368
405
  // Process the expanding queue
369
406
  const fieldNameSpace = buildNamespace(fields);
407
+ // Mark `joinDef` as activated and harvest everything it brings in: its
408
+ // on-clause field usage onto the queue, and its on-clause + filterList
409
+ // given usage into the accumulator. Used both in-loop (joins reached
410
+ // via reference paths) and upfront (always-on segment-level joins).
411
+ function activateJoin(joinDef, joinPath) {
412
+ const joinKey = (0, utils_2.pathToKey)('join', joinPath);
413
+ const thisDep = getJoin(activeJoinGraph, joinKey, joinPath);
414
+ if (!(0, malloy_types_1.isJoined)(joinDef) || thisDep.checked)
415
+ return;
416
+ thisDep.checked = true;
417
+ const joinFieldUsage = getJoinFieldUsage(joinDef, joinPath);
418
+ addGivens(getJoinGivenUsage(joinDef));
419
+ for (const usage of joinFieldUsage) {
420
+ const key = (0, utils_2.pathToKey)('field', usage.path);
421
+ if (!seen[key]) {
422
+ seen[key] = usage;
423
+ toProcess.push(usage);
424
+ }
425
+ const isInternalReference = usage.path.length === joinPath.length + 1 &&
426
+ pathBegins(usage.path, joinPath);
427
+ if (!isInternalReference && usage.path.length > 1) {
428
+ const dependencyPath = usage.path.slice(0, -1);
429
+ const dependencyKey = (0, utils_2.pathToKey)('join', dependencyPath);
430
+ getJoin(activeJoinGraph, dependencyKey, dependencyPath);
431
+ thisDep.dependsOn.add(dependencyKey);
432
+ }
433
+ }
434
+ }
435
+ // Always-on joins (segment-level direct `join_one:` etc.) are activated
436
+ // unconditionally — no field reference is needed. Compiler-side activation
437
+ // happens via `addAlwaysJoins` in query_query.ts; here we ensure their
438
+ // on-clause field/given usage flows into the expanded summaries.
439
+ for (const joinName of alwaysJoinNames) {
440
+ const joinDef = inNamespace([joinName], fieldNameSpace);
441
+ if (joinDef)
442
+ activateJoin(joinDef, [joinName]);
443
+ }
370
444
  for (let i = 0; i < toProcess.length; i++) {
371
445
  const reference = toProcess[i];
372
446
  if (reference.path.length === 0)
@@ -377,7 +451,8 @@ function expandFieldUsage(fieldUsage, fields) {
377
451
  continue;
378
452
  }
379
453
  if ((0, malloy_types_1.isAtomic)(def)) {
380
- const fieldUsage = (_a = def.fieldUsage) !== null && _a !== void 0 ? _a : [];
454
+ const fieldUsage = (0, malloy_types_1.fieldUsageFrom)(def.refSummary);
455
+ addGivens((0, malloy_types_1.givenUsageFrom)(def.refSummary));
381
456
  // Add the atomic field's dependencies to the queue
382
457
  const refPath = reference.path.slice(0, -1);
383
458
  for (const usage of joinedFieldUsage(refPath, fieldUsage)) {
@@ -395,33 +470,12 @@ function expandFieldUsage(fieldUsage, fields) {
395
470
  const joinDef = inNamespace(joinPath, fieldNameSpace);
396
471
  if (!joinDef)
397
472
  break;
398
- const joinKey = (0, utils_2.pathToKey)('join', joinPath);
399
- const thisDep = getJoin(activeJoinGraph, joinKey, joinPath);
400
- if ((0, malloy_types_1.isJoined)(joinDef) && !thisDep.checked) {
401
- thisDep.checked = true;
402
- const joinFieldUsage = getJoinFieldUsage(joinDef, joinPath);
403
- // Add join's field dependencies to the queue
404
- for (const usage of joinFieldUsage) {
405
- const key = (0, utils_2.pathToKey)('field', usage.path);
406
- if (!seen[key]) {
407
- seen[key] = usage;
408
- toProcess.push(usage);
409
- }
410
- // Track join-to-join dependencies
411
- const isInternalReference = usage.path.length === joinPath.length + 1 &&
412
- pathBegins(usage.path, joinPath);
413
- if (!isInternalReference && usage.path.length > 1) {
414
- const dependencyPath = usage.path.slice(0, -1);
415
- const dependencyKey = (0, utils_2.pathToKey)('join', dependencyPath);
416
- getJoin(activeJoinGraph, dependencyKey, dependencyPath);
417
- thisDep.dependsOn.add(dependencyKey);
418
- }
419
- }
420
- }
473
+ activateJoin(joinDef, joinPath);
421
474
  }
422
475
  }
423
476
  return {
424
- result: Object.values(seen),
477
+ fieldUsage: Object.values(seen),
478
+ givenUsage,
425
479
  missingFields,
426
480
  activeJoins: findActiveJoins(activeJoinGraph),
427
481
  ungroupings,
@@ -662,20 +716,19 @@ function processJoins(path, base, rootFields, nests, categorizedFieldUsage) {
662
716
  return { anyComposites, errors };
663
717
  }
664
718
  function segmentFieldUsage(segment) {
665
- var _a;
666
- return ((_a = ((0, malloy_types_1.isQuerySegment)(segment) || (0, malloy_types_1.isIndexSegment)(segment)
667
- ? segment.fieldUsage
668
- : undefined)) !== null && _a !== void 0 ? _a : emptyFieldUsage());
719
+ return (0, malloy_types_1.isQuerySegment)(segment) || (0, malloy_types_1.isIndexSegment)(segment)
720
+ ? (0, malloy_types_1.fieldUsageFrom)(segment.refSummary)
721
+ : emptyFieldUsage();
669
722
  }
670
723
  function getFieldUsageFromFilterList(source) {
671
724
  var _a;
672
- return ((_a = source.filterList) !== null && _a !== void 0 ? _a : []).flatMap(filter => { var _a; return (_a = filter.fieldUsage) !== null && _a !== void 0 ? _a : []; });
725
+ return ((_a = source.filterList) !== null && _a !== void 0 ? _a : []).flatMap(filter => (0, malloy_types_1.fieldUsageFrom)(filter.refSummary));
673
726
  }
674
727
  function resolveCompositeSources(source, segment) {
675
728
  var _a, _b;
676
729
  const fieldUsage = segmentFieldUsage(segment);
677
730
  const sourceExtensions = (0, malloy_types_1.isQuerySegment)(segment)
678
- ? (_a = segment.extendSource) !== null && _a !== void 0 ? _a : []
731
+ ? ((_a = segment.extendSource) !== null && _a !== void 0 ? _a : [])
679
732
  : [];
680
733
  const nestLevels = extractNestLevels(segment);
681
734
  const fields = mergeFields(source.fields, sourceExtensions);
@@ -758,6 +811,34 @@ function mergeFieldUsage(...usages) {
758
811
  return undefined;
759
812
  return usage;
760
813
  }
814
+ /**
815
+ * Concat-merge of `GivenUsage` arrays. Same shape as `mergeFieldUsage`:
816
+ * undefined inputs are skipped, all-empty result returns undefined,
817
+ * duplicates by id are kept (each carries its own `at` for diagnostics).
818
+ */
819
+ function mergeGivenUsage(...usages) {
820
+ const usage = [];
821
+ for (const oneUsage of usages) {
822
+ if (oneUsage === undefined)
823
+ continue;
824
+ usage.push(...oneUsage);
825
+ }
826
+ if (usage.length === 0)
827
+ return undefined;
828
+ return usage;
829
+ }
830
+ /**
831
+ * Combine N `RefSummary` values into one. Merges both `fieldUsage` and
832
+ * `givenUsage`; when more reference-tracking slots land on `RefSummary`,
833
+ * their merge logic lands here instead of being duplicated at every call
834
+ * site.
835
+ */
836
+ function mergeRefSummaries(...rss) {
837
+ return (0, malloy_types_1.mkRefSummary)({
838
+ fieldUsage: mergeFieldUsage(...rss.map(rs => (0, malloy_types_1.fieldUsageFrom)(rs))),
839
+ givenUsage: mergeGivenUsage(...rss.map(rs => (0, malloy_types_1.givenUsageFrom)(rs))),
840
+ });
841
+ }
761
842
  function fieldUsageDifference(a, b) {
762
843
  return a.filter(u1 => !b.some(u2 => pathEq(u1.path, u2.path)));
763
844
  }
@@ -839,7 +920,7 @@ function joinedUngroupings(joinPath, ungroupings) {
839
920
  }));
840
921
  }
841
922
  function extractNestLevels(segment) {
842
- var _a, _b, _c, _d;
923
+ var _a, _b, _c;
843
924
  const fieldsReferencedDirectly = [];
844
925
  const fieldsReferenced = [];
845
926
  const nested = [];
@@ -864,8 +945,9 @@ function extractNestLevels(segment) {
864
945
  // Check if the nested query has ANY unique key requirements
865
946
  let hasNestedUniqueKeyReqs = nestedLevels.fieldsReferenced.some(usage => usage.uniqueKeyRequirement);
866
947
  // Also check the head segment's fieldUsage directly
867
- if ((0, malloy_types_1.isQuerySegment)(head) && head.fieldUsage) {
868
- const hasDirectUniqueKeyReqs = head.fieldUsage.some(usage => usage.uniqueKeyRequirement);
948
+ if ((0, malloy_types_1.isQuerySegment)(head)) {
949
+ const headFieldUsages = (0, malloy_types_1.fieldUsageFrom)(head.refSummary);
950
+ const hasDirectUniqueKeyReqs = headFieldUsages.some(usage => usage.uniqueKeyRequirement);
869
951
  if (hasDirectUniqueKeyReqs) {
870
952
  hasNestedUniqueKeyReqs = true;
871
953
  }
@@ -888,13 +970,13 @@ function extractNestLevels(segment) {
888
970
  }, head.referencedAt));
889
971
  }
890
972
  else {
891
- const fieldUsage = (_a = field.fieldUsage) !== null && _a !== void 0 ? _a : [];
973
+ const fieldUsage = (0, malloy_types_1.fieldUsageFrom)(field.refSummary);
892
974
  fieldsReferenced.push(...fieldUsage);
893
- ungroupings.push(...((_b = field.ungroupings) !== null && _b !== void 0 ? _b : []));
894
- requiredGroupBys.push(...((_c = field.requiresGroupBy) !== null && _c !== void 0 ? _c : []));
975
+ ungroupings.push(...((_a = field.ungroupings) !== null && _a !== void 0 ? _a : []));
976
+ requiredGroupBys.push(...((_b = field.requiresGroupBy) !== null && _b !== void 0 ? _b : []));
895
977
  }
896
978
  }
897
- for (const filter of (_d = segment.filterList) !== null && _d !== void 0 ? _d : []) {
979
+ for (const filter of (_c = segment.filterList) !== null && _c !== void 0 ? _c : []) {
898
980
  if (!(0, malloy_types_1.expressionIsScalar)(filter.expressionType))
899
981
  continue;
900
982
  const fields = getSingleValueFilterFields(filter.e);
@@ -972,7 +1054,7 @@ function isSingleValueFilterNode(e) {
972
1054
  }
973
1055
  }
974
1056
  function expandRefs(nests, fields) {
975
- var _a, _b, _c, _d;
1057
+ var _a, _b, _c;
976
1058
  const newNests = [];
977
1059
  const requiredGroupBys = [...nests.requiredGroupBys];
978
1060
  const allUngroupings = [...nests.ungroupings];
@@ -1025,7 +1107,7 @@ function expandRefs(nests, fields) {
1025
1107
  if (def.ungroupings) {
1026
1108
  allUngroupings.push(...joinedUngroupings(joinPath, ungroupingsAt(def.ungroupings, field.at)));
1027
1109
  }
1028
- const fieldUsage = (_a = def.fieldUsage) !== null && _a !== void 0 ? _a : [];
1110
+ const fieldUsage = (0, malloy_types_1.fieldUsageFrom)(def.refSummary);
1029
1111
  const moreReferences = fieldUsageAt(joinedFieldUsage(joinPath, fieldUsage), field.at).filter(u1 => !references.some(u2 => pathEq(u1.path, u2.path)));
1030
1112
  references.push(...moreReferences);
1031
1113
  }
@@ -1045,10 +1127,10 @@ function expandRefs(nests, fields) {
1045
1127
  fieldsReferencedDirectly: [],
1046
1128
  ungroupings: [],
1047
1129
  nested: [],
1048
- requiredGroupBys: (_b = ungrouping.requiresGroupBy) !== null && _b !== void 0 ? _b : [],
1130
+ requiredGroupBys: (_a = ungrouping.requiresGroupBy) !== null && _a !== void 0 ? _a : [],
1049
1131
  singleValueFilters: [],
1050
1132
  }, fields);
1051
- missingFields.push(...((_c = expanded.missingFields) !== null && _c !== void 0 ? _c : []));
1133
+ missingFields.push(...((_b = expanded.missingFields) !== null && _b !== void 0 ? _b : []));
1052
1134
  for (const field of expanded.result.requiredGroupBys) {
1053
1135
  if (isUngroupedBy(ungrouping, field.path)) {
1054
1136
  unsatisfiableGroupBys.push(field);
@@ -1058,7 +1140,7 @@ function expandRefs(nests, fields) {
1058
1140
  const nested = [];
1059
1141
  for (const level of [...nests.nested, ...newNests]) {
1060
1142
  const expanded = expandRefs(level, fields);
1061
- missingFields.push(...((_d = expanded.missingFields) !== null && _d !== void 0 ? _d : []));
1143
+ missingFields.push(...((_c = expanded.missingFields) !== null && _c !== void 0 ? _c : []));
1062
1144
  nested.push(expanded.result);
1063
1145
  unsatisfiableGroupBys.push(...expanded.result.unsatisfiableGroupBys);
1064
1146
  allUngroupings.push(...expanded.result.ungroupings);
@@ -1076,13 +1158,101 @@ function expandRefs(nests, fields) {
1076
1158
  };
1077
1159
  }
1078
1160
  function getJoinFieldUsage(join, joinPath) {
1079
- var _a, _b;
1080
- return ((_b = mergeFieldUsage(
1161
+ var _a;
1162
+ return ((_a = mergeFieldUsage(
1081
1163
  // For `fieldUsage` from join `where`s, we need the path including the join name
1082
1164
  joinedFieldUsage(joinPath, (0, malloy_types_1.isSourceDef)(join) ? getFieldUsageFromFilterList(join) : []),
1083
1165
  // For `fieldUsage` from join `on`, we need the path excluding the join name, since it's
1084
1166
  // already rooted at the parent
1085
- joinedFieldUsage(joinPath.slice(0, -1), (_a = join.fieldUsage) !== null && _a !== void 0 ? _a : []))) !== null && _b !== void 0 ? _b : []);
1167
+ joinedFieldUsage(joinPath.slice(0, -1), (0, malloy_types_1.fieldUsageFrom)(join.refSummary)))) !== null && _a !== void 0 ? _a : []);
1168
+ }
1169
+ // Givens have no path — `joinedFieldUsage`'s path-rerooting has no analog here.
1170
+ // The `on:` clause lives on `join.refSummary` for every join shape; everything
1171
+ // else (filterList, plus any givens hiding inside the source's embedded Query)
1172
+ // is collected by `givenUsageOfSource` for source-typed joins.
1173
+ function getJoinGivenUsage(join) {
1174
+ const onClause = (0, malloy_types_1.givenUsageFrom)(join.refSummary);
1175
+ if ((0, malloy_types_1.isSourceDef)(join)) {
1176
+ return [...givenUsageOfSource(join), ...onClause];
1177
+ }
1178
+ return onClause;
1179
+ }
1180
+ // Dedup'd union of `expandedGivenUsage` across every segment in a Query's
1181
+ // finalized pipeline, recursing through nested turtle pipelines. This is the
1182
+ // per-Query summary that consumers (PreparedQuery.givens, satisfiability
1183
+ // check, `givenUsageOfSource` for QuerySourceDef) read instead of re-walking
1184
+ // segments. Called only from the query-arrow / query-refine construction
1185
+ // sites where every segment has been through getExpandedSegment.
1186
+ function computeQueryGivenUsage(pipeline) {
1187
+ const seen = new Set();
1188
+ const result = [];
1189
+ function add(items) {
1190
+ if (!items)
1191
+ return;
1192
+ for (const g of items) {
1193
+ if (seen.has(g.id))
1194
+ continue;
1195
+ seen.add(g.id);
1196
+ result.push(g);
1197
+ }
1198
+ }
1199
+ function visit(segment) {
1200
+ if ((0, malloy_types_1.isQuerySegment)(segment) || (0, malloy_types_1.isIndexSegment)(segment)) {
1201
+ add(segment.expandedGivenUsage);
1202
+ }
1203
+ if ((0, malloy_types_1.isQuerySegment)(segment)) {
1204
+ for (const field of segment.queryFields) {
1205
+ if ((0, malloy_types_1.isTurtle)(field)) {
1206
+ field.pipeline.forEach(visit);
1207
+ }
1208
+ }
1209
+ }
1210
+ }
1211
+ pipeline.forEach(visit);
1212
+ return result;
1213
+ }
1214
+ // Returns every given a SourceDef references — its own filterList plus any
1215
+ // givens hiding inside an embedded Query. The single point of knowledge for
1216
+ // "where can a Query embed in a SourceDef" — when a future SourceDef variant
1217
+ // grows an embedded Query, teach this helper.
1218
+ //
1219
+ // Composite sources: composite-source support across the codebase is
1220
+ // partial; the givens work isn't chasing it. The `composite` arm here is
1221
+ // defensive — for the top-level `run: composite ->` path it never fires
1222
+ // because composite resolution runs first and `expandRefUsage` is seeded
1223
+ // with the resolved branch. If an unresolved `CompositeSourceDef` ever
1224
+ // reaches here (e.g. composites in join positions if/when those work),
1225
+ // the conservative union over all branches is the safe fallback. See
1226
+ // ~/ctx/mp/implementation.md "Composite sources — partial coverage".
1227
+ function givenUsageOfSource(sd) {
1228
+ var _a, _b, _c;
1229
+ const fromFilters = ((_a = sd.filterList) !== null && _a !== void 0 ? _a : []).flatMap(f => (0, malloy_types_1.givenUsageFrom)(f.refSummary));
1230
+ switch (sd.type) {
1231
+ case 'query_source':
1232
+ return [...fromFilters, ...((_b = sd.query.givenUsage) !== null && _b !== void 0 ? _b : [])];
1233
+ case 'sql_select': {
1234
+ const fromSegments = ((_c = sd.selectSegments) !== null && _c !== void 0 ? _c : []).flatMap(seg => {
1235
+ var _a;
1236
+ if ((0, malloy_types_1.isSegmentSQL)(seg))
1237
+ return [];
1238
+ if ((0, malloy_types_1.isSegmentSource)(seg))
1239
+ return givenUsageOfSource(seg);
1240
+ // Remaining variant of SQLPhraseSegment is `Query`
1241
+ return (_a = seg.givenUsage) !== null && _a !== void 0 ? _a : [];
1242
+ });
1243
+ return [...fromFilters, ...fromSegments];
1244
+ }
1245
+ case 'composite':
1246
+ return [
1247
+ ...fromFilters,
1248
+ ...sd.sources.flatMap(s => givenUsageOfSource(s)),
1249
+ ];
1250
+ default:
1251
+ // table, query_result, finalize, nest_source, virtual — no embedded Query.
1252
+ // (query_result/finalize/nest_source are pipeline-internal and only seen
1253
+ // within a Query whose segments are walked anyway. virtual is opaque.)
1254
+ return fromFilters;
1255
+ }
1086
1256
  }
1087
1257
  function isUngroupedBy(ungrouping, groupedBy) {
1088
1258
  if (ungrouping.ungroupedFields === '*')
@@ -1098,7 +1268,7 @@ function checkRequiredGroupBys(compositeResolvedSourceDef, segment) {
1098
1268
  var _a;
1099
1269
  const nests = extractNestLevels(segment);
1100
1270
  const sourceExtensions = (0, malloy_types_1.isQuerySegment)(segment)
1101
- ? (_a = segment.extendSource) !== null && _a !== void 0 ? _a : []
1271
+ ? ((_a = segment.extendSource) !== null && _a !== void 0 ? _a : [])
1102
1272
  : [];
1103
1273
  const unsatisfied = _checkRequiredGroupBys(nests, mergeFields(compositeResolvedSourceDef.fields, sourceExtensions));
1104
1274
  return unsatisfied;