@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
@@ -17,9 +17,9 @@ export interface DialectFunctionOverloadDef {
17
17
  export declare function arg(name: string): Expr;
18
18
  export declare function spread(e: Expr, prefix?: string | undefined, suffix?: string | undefined): Expr;
19
19
  export declare function sql(strings: TemplateStringsArray, ...subExprs: SQLExprElement[]): Expr;
20
- export declare function constant(type: TypeDesc): TypeDesc;
21
- export declare function output(type: TypeDesc): TypeDesc;
22
- export declare function literal(type: TypeDesc): TypeDesc;
20
+ export declare function constant(type: FunctionParamTypeDesc): FunctionParamTypeDesc;
21
+ export declare function output(type: FunctionParamTypeDesc): FunctionParamTypeDesc;
22
+ export declare function literal(type: FunctionParamTypeDesc): FunctionParamTypeDesc;
23
23
  export declare function variadicParam(name: string, ...allowedTypes: FunctionParamTypeDesc[]): FunctionParameterDef;
24
24
  /**
25
25
  * Prefer `makeParam` for future function definitions
@@ -29,14 +29,14 @@ export declare function makeParam(name: string, ...allowedTypes: FunctionParamTy
29
29
  param: FunctionParameterDef;
30
30
  arg: Expr;
31
31
  };
32
- export declare function maxScalar(type: LeafExpressionType): TypeDesc;
33
- export declare function maxAggregate(type: LeafExpressionType): TypeDesc;
32
+ export declare function maxScalar(type: LeafExpressionType): FunctionParamTypeDesc;
33
+ export declare function maxAggregate(type: LeafExpressionType): FunctionParamTypeDesc;
34
34
  export declare function anyExprType(type: LeafExpressionType): FunctionParamTypeDesc;
35
35
  export declare function maxUngroupedAggregate(type: LeafExpressionType): FunctionParamTypeDesc;
36
36
  export declare function maxAnalytic(type: LeafExpressionType): FunctionParamTypeDesc;
37
- export declare function minScalar(type: LeafExpressionType): TypeDesc;
38
- export declare function minAggregate(type: LeafExpressionType): TypeDesc;
39
- export declare function minAnalytic(type: LeafExpressionType): TypeDesc;
37
+ export declare function minScalar(type: LeafExpressionType): FunctionParamTypeDesc;
38
+ export declare function minAggregate(type: LeafExpressionType): FunctionParamTypeDesc;
39
+ export declare function minAnalytic(type: LeafExpressionType): FunctionParamTypeDesc;
40
40
  export declare function overload(returnType: TypeDesc, params: FunctionParameterDef[], e: Expr, options?: {
41
41
  needsWindowOrderBy?: boolean;
42
42
  between?: {
@@ -23,6 +23,7 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.expandOverrideMapFromBase = exports.expandBlueprintMap = exports.overload = exports.minAnalytic = exports.minAggregate = exports.minScalar = exports.maxAnalytic = exports.maxUngroupedAggregate = exports.anyExprType = exports.maxAggregate = exports.maxScalar = exports.makeParam = exports.param = exports.variadicParam = exports.literal = exports.output = exports.constant = exports.sql = exports.spread = exports.arg = void 0;
26
+ const composite_source_utils_1 = require("../../model/composite_source_utils");
26
27
  function arg(name) {
27
28
  return { node: 'function_parameter', name };
28
29
  }
@@ -159,27 +160,34 @@ function removeGeneric(type, generic) {
159
160
  return generic.type;
160
161
  }
161
162
  function expandReturnTypeBlueprint(blueprint, generic) {
163
+ var _a;
164
+ let base;
162
165
  if (typeof blueprint === 'string') {
163
- return minScalar(blueprint);
166
+ base = minScalar(blueprint);
164
167
  }
165
168
  else if ('generic' in blueprint) {
166
- return minScalar(removeGeneric(blueprint, generic));
169
+ base = minScalar(removeGeneric(blueprint, generic));
167
170
  }
168
171
  else if ('literal' in blueprint) {
169
- return literal(minScalar(removeGeneric(blueprint.literal, generic)));
172
+ base = literal(minScalar(removeGeneric(blueprint.literal, generic)));
170
173
  }
171
174
  else if ('constant' in blueprint) {
172
- return constant(minScalar(removeGeneric(blueprint.constant, generic)));
175
+ base = constant(minScalar(removeGeneric(blueprint.constant, generic)));
173
176
  }
174
177
  else if ('dimension' in blueprint) {
175
- return minScalar(removeGeneric(blueprint.dimension, generic));
178
+ base = minScalar(removeGeneric(blueprint.dimension, generic));
176
179
  }
177
180
  else if ('measure' in blueprint) {
178
- return minAggregate(removeGeneric(blueprint.measure, generic));
181
+ base = minAggregate(removeGeneric(blueprint.measure, generic));
179
182
  }
180
183
  else {
181
- return minAnalytic(removeGeneric(blueprint.calculation, generic));
184
+ base = minAnalytic(removeGeneric(blueprint.calculation, generic));
182
185
  }
186
+ return {
187
+ ...base,
188
+ compositeFieldUsage: (0, composite_source_utils_1.emptyCompositeFieldUsage)(),
189
+ expressionType: (_a = base.expressionType) !== null && _a !== void 0 ? _a : 'scalar',
190
+ };
183
191
  }
184
192
  function isTypeDescBlueprint(blueprint) {
185
193
  return (typeof blueprint === 'string' ||
@@ -23,6 +23,7 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.errorFor = void 0;
26
+ const composite_source_utils_1 = require("../../model/composite_source_utils");
26
27
  /**
27
28
  * When a translation hits an error, log and return one of these as a value.
28
29
  * This will allow the rest of the translation walk to complete. The
@@ -37,6 +38,7 @@ function errorFor(reason) {
37
38
  expressionType: 'scalar',
38
39
  value: { node: 'error', message: reason },
39
40
  evalSpace: 'constant',
41
+ compositeFieldUsage: (0, composite_source_utils_1.emptyCompositeFieldUsage)(),
40
42
  };
41
43
  }
42
44
  exports.errorFor = errorFor;
@@ -92,6 +92,7 @@ class ExprAggregateFunction extends expression_def_1.ExpressionDef {
92
92
  ? { node: 'outputField', name: this.source.refString }
93
93
  : { node: 'field', path: this.source.path },
94
94
  evalSpace: footType.evalSpace,
95
+ compositeFieldUsage: footType.compositeFieldUsage,
95
96
  };
96
97
  structPath = this.source.path;
97
98
  // Here we handle a special case where you write `foo.agg()` and `foo` is a
@@ -46,8 +46,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
46
46
  };
47
47
  Object.defineProperty(exports, "__esModule", { value: true });
48
48
  exports.ExprLegacyIn = exports.ExprEquality = exports.ExprCompare = void 0;
49
- const model_1 = require("../../../model");
50
49
  const TDU = __importStar(require("../typedesc-utils"));
50
+ const expr_value_1 = require("../types/expr-value");
51
51
  const expression_def_1 = require("../types/expression-def");
52
52
  const binary_boolean_1 = require("./binary-boolean");
53
53
  const compareTypes = {
@@ -101,23 +101,16 @@ class ExprLegacyIn extends expression_def_1.ExpressionDef {
101
101
  }
102
102
  getExpression(fs) {
103
103
  const lookFor = this.expr.getExpression(fs);
104
- let { evalSpace, expressionType } = lookFor;
105
- const oneOf = this.choices.map(e => {
106
- const choice = e.getExpression(fs);
107
- expressionType = (0, model_1.maxExpressionType)(expressionType, choice.expressionType);
108
- evalSpace = (0, model_1.mergeEvalSpaces)(evalSpace, choice.evalSpace);
109
- return choice.value;
110
- });
111
- return {
112
- type: 'boolean',
113
- expressionType,
114
- evalSpace,
104
+ const oneOf = this.choices.map(e => e.getExpression(fs));
105
+ return (0, expr_value_1.computedExprValue)({
106
+ dataType: { type: 'boolean' },
115
107
  value: {
116
108
  node: 'in',
117
109
  not: this.notIn,
118
- kids: { e: lookFor.value, oneOf },
110
+ kids: { e: lookFor.value, oneOf: oneOf.map(v => v.value) },
119
111
  },
120
- };
112
+ from: [lookFor, ...oneOf],
113
+ });
121
114
  }
122
115
  }
123
116
  exports.ExprLegacyIn = ExprLegacyIn;
@@ -60,6 +60,7 @@ class ExprCountDistinct extends expr_aggregate_function_1.ExprAggregateFunction
60
60
  evalSpace: ev.evalSpace,
61
61
  expressionType: 'aggregate',
62
62
  value: ev.value,
63
+ compositeFieldUsage: ev.compositeFieldUsage,
63
64
  };
64
65
  }
65
66
  }
@@ -23,6 +23,7 @@
23
23
  */
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ExprCount = void 0;
26
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
26
27
  const expr_aggregate_function_1 = require("./expr-aggregate-function");
27
28
  class ExprCount extends expr_aggregate_function_1.ExprAggregateFunction {
28
29
  constructor(source) {
@@ -44,6 +45,7 @@ class ExprCount extends expr_aggregate_function_1.ExprAggregateFunction {
44
45
  evalSpace: ev.evalSpace,
45
46
  expressionType: 'aggregate',
46
47
  value: ev.value,
48
+ compositeFieldUsage: ev.compositeFieldUsage,
47
49
  };
48
50
  }
49
51
  getExpression(_fs) {
@@ -61,6 +63,7 @@ class ExprCount extends expr_aggregate_function_1.ExprAggregateFunction {
61
63
  expressionType: 'aggregate',
62
64
  value: ret,
63
65
  evalSpace: 'output',
66
+ compositeFieldUsage: (0, composite_source_utils_1.emptyCompositeFieldUsage)(),
64
67
  };
65
68
  }
66
69
  }
@@ -54,6 +54,7 @@ const expression_def_1 = require("../types/expression-def");
54
54
  const field_space_1 = require("../types/field-space");
55
55
  const utils_1 = require("../../../model/utils");
56
56
  const TDU = __importStar(require("../typedesc-utils"));
57
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
57
58
  class ExprFunc extends expression_def_1.ExpressionDef {
58
59
  constructor(name, args, isRaw, rawType, source) {
59
60
  super({ args: args });
@@ -141,6 +142,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
141
142
  expressionType: footType.expressionType,
142
143
  value: { node: 'field', path: this.source.path },
143
144
  evalSpace: footType.evalSpace,
145
+ compositeFieldUsage: footType.compositeFieldUsage,
144
146
  };
145
147
  structPath = this.source.path.slice(0, -1);
146
148
  }
@@ -335,6 +337,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
335
337
  expressionType,
336
338
  value: funcCall,
337
339
  evalSpace,
340
+ compositeFieldUsage: (0, composite_source_utils_1.mergeCompositeFieldUsage)(...argExprs.map(e => e.compositeFieldUsage)),
338
341
  };
339
342
  }
340
343
  }
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ExprIdReference = void 0;
26
26
  const malloy_types_1 = require("../../../model/malloy_types");
27
27
  const expression_def_1 = require("../types/expression-def");
28
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
28
29
  class ExprIdReference extends expression_def_1.ExpressionDef {
29
30
  constructor(fieldReference) {
30
31
  super();
@@ -37,14 +38,22 @@ class ExprIdReference extends expression_def_1.ExpressionDef {
37
38
  }
38
39
  getExpression(fs) {
39
40
  const def = this.fieldReference.getField(fs);
41
+ // TODO Currently the join usage is always equivalent to the reference path here;
42
+ // if/when we add namespaces, this will not be the case, and we will need to get the
43
+ // join path from `getField` / `lookup`
44
+ const compositeJoinUsage = this.fieldReference.list
45
+ .map(n => n.name)
46
+ .slice(0, -1);
40
47
  if (def.found) {
41
48
  const td = def.found.typeDesc();
49
+ const compositeFieldUsage = (0, composite_source_utils_1.joinedCompositeFieldUsage)(compositeJoinUsage, td.compositeFieldUsage);
42
50
  if (def.isOutputField) {
43
51
  return {
44
52
  ...td,
45
53
  // TODO what about literal??
46
54
  evalSpace: td.evalSpace === 'constant' ? 'constant' : 'output',
47
55
  value: { node: 'outputField', name: this.refString },
56
+ compositeFieldUsage,
48
57
  };
49
58
  }
50
59
  const value = { node: def.found.refType, path: this.fieldReference.path };
@@ -52,7 +61,7 @@ class ExprIdReference extends expression_def_1.ExpressionDef {
52
61
  const evalSpace = (0, malloy_types_1.expressionIsAggregate)(td.expressionType)
53
62
  ? 'output'
54
63
  : td.evalSpace;
55
- return { ...td, value, evalSpace };
64
+ return { ...td, value, evalSpace, compositeFieldUsage };
56
65
  }
57
66
  return this.loggedErrorExpr(def.error.code, def.error.message);
58
67
  }
@@ -24,6 +24,7 @@
24
24
  Object.defineProperty(exports, "__esModule", { value: true });
25
25
  exports.ExprNow = void 0;
26
26
  const expression_def_1 = require("../types/expression-def");
27
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
27
28
  class ExprNow extends expression_def_1.ExpressionDef {
28
29
  constructor() {
29
30
  super(...arguments);
@@ -36,6 +37,7 @@ class ExprNow extends expression_def_1.ExpressionDef {
36
37
  // `now` is considered to be a constant, at least in the dialects we support today
37
38
  evalSpace: 'constant',
38
39
  value: { node: 'now' },
40
+ compositeFieldUsage: (0, composite_source_utils_1.emptyCompositeFieldUsage)(),
39
41
  };
40
42
  }
41
43
  }
@@ -106,6 +106,7 @@ class ExprUngroup extends expression_def_1.ExpressionDef {
106
106
  expressionType: 'ungrouped_aggregate',
107
107
  value: ungroup,
108
108
  evalSpace: 'output',
109
+ compositeFieldUsage: exprVal.compositeFieldUsage,
109
110
  };
110
111
  }
111
112
  return this.loggedErrorExpr('ungroup-with-non-scalar', `${this.control}() incompatible type`);
@@ -7,14 +7,12 @@ import { ParameterSpace } from './parameter-space';
7
7
  import { SourceDef } from '../../../model/malloy_types';
8
8
  import { SourceFieldSpace } from '../types/field-space';
9
9
  export declare abstract class DynamicSpace extends StaticSpace implements SourceFieldSpace {
10
- protected final: model.SourceDef | undefined;
10
+ protected sourceDef: model.SourceDef | undefined;
11
11
  protected fromSource: model.SourceDef;
12
- completions: (() => void)[];
13
12
  private complete;
14
13
  private parameters;
15
14
  protected newTimezone?: string;
16
15
  constructor(extending: SourceDef);
17
- isComplete(): void;
18
16
  protected setEntry(name: string, value: SpaceEntry): void;
19
17
  addParameters(parameters: HasParameter[]): DynamicSpace;
20
18
  parameterSpace(): ParameterSpace;
@@ -59,17 +59,13 @@ const parameter_space_1 = require("./parameter-space");
59
59
  class DynamicSpace extends static_space_1.StaticSpace {
60
60
  constructor(extending) {
61
61
  super(structuredClone(extending));
62
- this.completions = [];
63
62
  this.complete = false;
64
63
  this.parameters = [];
65
64
  this.fromSource = extending;
66
- this.final = undefined;
67
- }
68
- isComplete() {
69
- this.complete = true;
65
+ this.sourceDef = undefined;
70
66
  }
71
67
  setEntry(name, value) {
72
- if (this.final) {
68
+ if (this.complete) {
73
69
  throw new Error('Space already final');
74
70
  }
75
71
  super.setEntry(name, value);
@@ -104,7 +100,8 @@ class DynamicSpace extends static_space_1.StaticSpace {
104
100
  this.newTimezone = tz;
105
101
  }
106
102
  structDef() {
107
- if (this.final === undefined) {
103
+ this.complete = true;
104
+ if (this.sourceDef === undefined) {
108
105
  // Grab all the parameters so that we can populate the "final" structDef
109
106
  // with parameters immediately so that views can see them when they are translating
110
107
  const parameters = {};
@@ -113,8 +110,8 @@ class DynamicSpace extends static_space_1.StaticSpace {
113
110
  parameters[name] = entry.parameter();
114
111
  }
115
112
  }
116
- this.final = { ...this.fromSource, fields: [] };
117
- this.final.parameters = parameters;
113
+ this.sourceDef = { ...this.fromSource, fields: [] };
114
+ this.sourceDef.parameters = parameters;
118
115
  // Need to process the entities in specific order
119
116
  const fields = [];
120
117
  const joins = [];
@@ -137,14 +134,14 @@ class DynamicSpace extends static_space_1.StaticSpace {
137
134
  if (field instanceof join_space_field_1.JoinSpaceField) {
138
135
  const joinStruct = field.join.structDef(parameterSpace);
139
136
  if (!error_factory_1.ErrorFactory.didCreate(joinStruct)) {
140
- this.final.fields.push(joinStruct);
137
+ this.sourceDef.fields.push(joinStruct);
141
138
  fixupJoins.push([field.join, joinStruct]);
142
139
  }
143
140
  }
144
141
  else {
145
142
  const fieldDef = field.fieldDef();
146
143
  if (fieldDef) {
147
- this.final.fields.push(fieldDef);
144
+ this.sourceDef.fields.push(fieldDef);
148
145
  }
149
146
  // TODO I'm just removing this, but perhaps instead I should just filter
150
147
  // out ReferenceFields and still make this check.
@@ -158,11 +155,10 @@ class DynamicSpace extends static_space_1.StaticSpace {
158
155
  join.fixupJoinOn(this, missingOn);
159
156
  }
160
157
  }
161
- if (this.newTimezone && model.isSourceDef(this.final)) {
162
- this.final.queryTimezone = this.newTimezone;
158
+ if (this.newTimezone && model.isSourceDef(this.sourceDef)) {
159
+ this.sourceDef.queryTimezone = this.newTimezone;
163
160
  }
164
- this.isComplete();
165
- return this.final;
161
+ return this.sourceDef;
166
162
  }
167
163
  emptyStructDef() {
168
164
  const ret = { ...this.fromSource };
@@ -1,10 +1,12 @@
1
1
  import * as model from '../../../model/malloy_types';
2
2
  import { FieldName, QueryFieldSpace, SourceFieldSpace } from '../types/field-space';
3
3
  import { MalloyElement } from '../types/malloy-element';
4
+ import { SpaceField } from '../types/space-field';
4
5
  import { WildcardFieldReference } from '../query-items/field-references';
5
6
  import { RefinedSpace } from './refined-space';
6
7
  import { LookupResult } from '../types/lookup-result';
7
8
  import { QueryInputSpace } from './query-input-space';
9
+ import { SpaceEntry } from '../types/space-entry';
8
10
  import { LogMessageOptions, MessageCode, MessageParameterType } from '../../parse-log';
9
11
  /**
10
12
  * The output space of a query operation. It is not named "QueryOutputSpace"
@@ -19,12 +21,29 @@ export declare abstract class QueryOperationSpace extends RefinedSpace implement
19
21
  protected exprSpace: QueryInputSpace;
20
22
  abstract readonly segmentType: 'reduce' | 'project' | 'index';
21
23
  expandedWild: Record<string, string[]>;
24
+ compositeFieldUsers: ({
25
+ type: 'filter';
26
+ filter: model.FilterCondition;
27
+ logTo: MalloyElement;
28
+ } | {
29
+ type: 'field';
30
+ name: string;
31
+ field: SpaceField;
32
+ logTo: MalloyElement | undefined;
33
+ })[];
34
+ _compositeFieldUsage: model.CompositeFieldUsage | undefined;
35
+ get compositeFieldUsage(): model.CompositeFieldUsage;
22
36
  constructor(queryInputSpace: SourceFieldSpace, refineThis: model.PipeSegment | undefined, nestParent: QueryOperationSpace | undefined, astEl: MalloyElement);
23
37
  abstract addRefineFromFields(refineThis: model.PipeSegment): void;
24
38
  logError<T extends MessageCode>(code: T, parameters: MessageParameterType<T>, options?: Omit<LogMessageOptions, 'severity'>): T;
25
39
  inputSpace(): QueryInputSpace;
26
40
  outputSpace(): QueryOperationSpace;
27
41
  protected addWild(wild: WildcardFieldReference): void;
42
+ protected addValidatedCompositeFieldUserFromEntry(name: string, entry: SpaceEntry): void;
43
+ private getJoinOnCompositeFieldUsage;
44
+ protected getCompositeFieldUsageIncludingJoinOns(compositeFieldUsage: model.CompositeFieldUsage): model.CompositeFieldUsage;
45
+ addCompositeFieldUserFromFilter(filter: model.FilterCondition, logTo: MalloyElement): void;
46
+ newEntry(name: string, logTo: MalloyElement, entry: SpaceEntry): void;
28
47
  }
29
48
  export declare abstract class QuerySpace extends QueryOperationSpace {
30
49
  addRefineFromFields(refineThis: model.PipeSegment): void;
@@ -55,6 +55,8 @@ const refined_space_1 = require("./refined-space");
55
55
  const column_space_field_1 = require("./column-space-field");
56
56
  const static_space_1 = require("./static-space");
57
57
  const query_input_space_1 = require("./query-input-space");
58
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
59
+ const struct_space_field_base_1 = require("./struct-space-field-base");
58
60
  /**
59
61
  * The output space of a query operation. It is not named "QueryOutputSpace"
60
62
  * because this is the namespace of the Query which is a layer of an output and
@@ -62,12 +64,22 @@ const query_input_space_1 = require("./query-input-space");
62
64
  * created and paired when a QueryOperationSpace is created.
63
65
  */
64
66
  class QueryOperationSpace extends refined_space_1.RefinedSpace {
67
+ get compositeFieldUsage() {
68
+ if (this._compositeFieldUsage === undefined) {
69
+ throw new Error('Composite field usage accessed before computed');
70
+ }
71
+ return this._compositeFieldUsage;
72
+ }
65
73
  constructor(queryInputSpace, refineThis, nestParent, astEl) {
66
74
  super(queryInputSpace.emptyStructDef());
67
75
  this.queryInputSpace = queryInputSpace;
68
76
  this.nestParent = nestParent;
69
77
  this.astEl = astEl;
70
78
  this.expandedWild = {};
79
+ this.compositeFieldUsers = [];
80
+ // Composite field usage is not computed until `queryFieldDefs` is called; if anyone
81
+ // tries to access it before that, they'll get an error
82
+ this._compositeFieldUsage = undefined;
71
83
  this.exprSpace = new query_input_space_1.QueryInputSpace(queryInputSpace.structDef(), this);
72
84
  if (refineThis)
73
85
  this.addRefineFromFields(refineThis);
@@ -134,8 +146,48 @@ class QueryOperationSpace extends refined_space_1.RefinedSpace {
134
146
  }
135
147
  // There were tests which expected these to be sorted, and that seems reasonable
136
148
  for (const x of expandEntries.sort((a, b) => a.name.localeCompare(b.name))) {
137
- this.setEntry(x.name, x.entry);
149
+ this.newEntry(x.name, wild, x.entry);
150
+ }
151
+ }
152
+ addValidatedCompositeFieldUserFromEntry(name, entry) {
153
+ if (entry instanceof space_field_1.SpaceField) {
154
+ this.compositeFieldUsers.push({
155
+ type: 'field',
156
+ name,
157
+ field: entry,
158
+ logTo: undefined,
159
+ });
160
+ }
161
+ }
162
+ getJoinOnCompositeFieldUsage(joinPath) {
163
+ var _a;
164
+ const reference = joinPath.map(n => new field_space_1.FieldName(n));
165
+ this.astEl.has({ reference });
166
+ const lookup = this.queryInputSpace.lookup(reference);
167
+ // Should always be found...
168
+ if (lookup.found && lookup.found instanceof struct_space_field_base_1.StructSpaceFieldBase) {
169
+ return ((_a = lookup.found.fieldDef().onCompositeFieldUsage) !== null && _a !== void 0 ? _a : (0, composite_source_utils_1.emptyCompositeFieldUsage)());
138
170
  }
171
+ throw new Error('Unexpected join lookup was not found or not a struct');
172
+ }
173
+ getCompositeFieldUsageIncludingJoinOns(compositeFieldUsage) {
174
+ let compositeFieldUsageIncludingJoinOns = compositeFieldUsage;
175
+ const joinPaths = (0, composite_source_utils_1.compositeFieldUsageJoinPaths)(compositeFieldUsage);
176
+ for (const joinPath of joinPaths) {
177
+ compositeFieldUsageIncludingJoinOns = (0, composite_source_utils_1.mergeCompositeFieldUsage)(this.getJoinOnCompositeFieldUsage(joinPath), compositeFieldUsageIncludingJoinOns);
178
+ }
179
+ return compositeFieldUsageIncludingJoinOns;
180
+ }
181
+ addCompositeFieldUserFromFilter(filter, logTo) {
182
+ if (filter.compositeFieldUsage !== undefined) {
183
+ this.compositeFieldUsers.push({ type: 'filter', filter, logTo });
184
+ }
185
+ }
186
+ newEntry(name, logTo, entry) {
187
+ if (entry instanceof space_field_1.SpaceField) {
188
+ this.compositeFieldUsers.push({ type: 'field', name, field: entry, logTo });
189
+ }
190
+ super.newEntry(name, logTo, entry);
139
191
  }
140
192
  }
141
193
  exports.QueryOperationSpace = QueryOperationSpace;
@@ -147,17 +199,27 @@ class QuerySpace extends QueryOperationSpace {
147
199
  // TODO mtoy raw,partial,index
148
200
  return;
149
201
  }
202
+ if (refineThis === null || refineThis === void 0 ? void 0 : refineThis.extendSource) {
203
+ for (const xField of refineThis.extendSource) {
204
+ this.exprSpace.addFieldDef(xField);
205
+ }
206
+ }
150
207
  for (const field of refineThis.queryFields) {
151
208
  if (field.type === 'fieldref') {
152
209
  const refTo = this.exprSpace.lookup(field.path.map(f => new field_space_1.FieldName(f)));
153
210
  if (refTo.found) {
154
- this.setEntry(field.path[field.path.length - 1], refTo.found);
211
+ const name = field.path[field.path.length - 1];
212
+ this.setEntry(name, refTo.found);
213
+ this.addValidatedCompositeFieldUserFromEntry(name, refTo.found);
155
214
  }
156
215
  }
157
216
  else if (field.type !== 'turtle') {
158
217
  // TODO can you reference fields in a turtle as fields in the output space,
159
218
  // e.g. order_by: my_turtle.foo, or lag(my_turtle.foo)
160
- this.setEntry((_a = field.as) !== null && _a !== void 0 ? _a : field.name, new column_space_field_1.ColumnSpaceField(field));
219
+ const entry = new column_space_field_1.ColumnSpaceField(field);
220
+ const name = (_a = field.as) !== null && _a !== void 0 ? _a : field.name;
221
+ this.setEntry(name, entry);
222
+ this.addValidatedCompositeFieldUserFromEntry(name, entry);
161
223
  }
162
224
  }
163
225
  }
@@ -176,16 +238,28 @@ class QuerySpace extends QueryOperationSpace {
176
238
  }
177
239
  queryFieldDefs() {
178
240
  const fields = [];
179
- for (const [name, field] of this.entries()) {
180
- if (field instanceof space_field_1.SpaceField) {
241
+ let compositeFieldUsage = (0, composite_source_utils_1.emptyCompositeFieldUsage)();
242
+ let narrowedCompositeFieldResolution = (0, composite_source_utils_1.emptyNarrowedCompositeFieldResolution)();
243
+ const source = this.inputSpace().structDef();
244
+ for (const user of this.compositeFieldUsers) {
245
+ let nextCompositeFieldUsage = undefined;
246
+ if (user.type === 'filter') {
247
+ if (user.filter.compositeFieldUsage) {
248
+ nextCompositeFieldUsage = user.filter.compositeFieldUsage;
249
+ }
250
+ }
251
+ else {
252
+ const { name, field } = user;
181
253
  const wildPath = this.expandedWild[name];
182
254
  if (wildPath) {
183
255
  fields.push({ type: 'fieldref', path: wildPath });
184
256
  continue;
185
257
  }
258
+ // TODO handle wildcards for composite sources
186
259
  const fieldQueryDef = field.getQueryFieldDef(this.exprSpace);
187
260
  if (fieldQueryDef) {
188
261
  const typeDesc = field.typeDesc();
262
+ nextCompositeFieldUsage = typeDesc.compositeFieldUsage;
189
263
  // Filter out fields whose type is 'error', which means that a totally bad field
190
264
  // isn't sent to the compiler, where it will wig out.
191
265
  // TODO Figure out how to make errors generated by `canContain` go in the right place,
@@ -205,8 +279,32 @@ class QuerySpace extends QueryOperationSpace {
205
279
  // project statements, where we added "*" as a field and also all the individual
206
280
  // fields, but the individual fields didn't have field defs.
207
281
  }
282
+ if (nextCompositeFieldUsage) {
283
+ const newCompositeFieldUsage = this.getCompositeFieldUsageIncludingJoinOns((0, composite_source_utils_1.compositeFieldUsageDifference)(nextCompositeFieldUsage, compositeFieldUsage));
284
+ compositeFieldUsage = (0, composite_source_utils_1.mergeCompositeFieldUsage)(compositeFieldUsage, newCompositeFieldUsage);
285
+ if (!(0, composite_source_utils_1.isEmptyCompositeFieldUsage)(newCompositeFieldUsage)) {
286
+ const result = (0, composite_source_utils_1.narrowCompositeFieldResolution)(source, compositeFieldUsage, narrowedCompositeFieldResolution);
287
+ if (result.error) {
288
+ if (user.logTo) {
289
+ user.logTo.logError('invalid-composite-field-usage', {
290
+ newUsage: newCompositeFieldUsage,
291
+ allUsage: compositeFieldUsage,
292
+ });
293
+ }
294
+ else {
295
+ // This should not happen; logTo should only be not set for a field from a refinement,
296
+ // which should never fail the composite resolution
297
+ throw new Error('Unexpected invalid composite field resolution');
298
+ }
299
+ }
300
+ else {
301
+ narrowedCompositeFieldResolution =
302
+ result.narrowedCompositeFieldResolution;
303
+ }
304
+ }
305
+ }
208
306
  }
209
- this.isComplete();
307
+ this._compositeFieldUsage = compositeFieldUsage;
210
308
  return fields;
211
309
  }
212
310
  getQuerySegment(rf) {
@@ -222,11 +320,6 @@ class QuerySpace extends QueryOperationSpace {
222
320
  this.logError('unexpected-index-segment', 'internal error generating index segment from non index query');
223
321
  return { type: 'reduce', queryFields: [] };
224
322
  }
225
- if (refineFrom === null || refineFrom === void 0 ? void 0 : refineFrom.extendSource) {
226
- for (const xField of refineFrom.extendSource) {
227
- this.exprSpace.addFieldDef(xField);
228
- }
229
- }
230
323
  const segment = {
231
324
  type: this.segmentType,
232
325
  queryFields: this.queryFieldDefs(),
@@ -249,7 +342,6 @@ class QuerySpace extends QueryOperationSpace {
249
342
  if (this.newTimezone) {
250
343
  segment.queryTimezone = this.newTimezone;
251
344
  }
252
- this.isComplete();
253
345
  return segment;
254
346
  }
255
347
  lookup(path) {
@@ -49,6 +49,7 @@ exports.ReferenceField = void 0;
49
49
  const malloy_types_1 = require("../../../model/malloy_types");
50
50
  const TDU = __importStar(require("../typedesc-utils"));
51
51
  const space_field_1 = require("../types/space-field");
52
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
52
53
  class ReferenceField extends space_field_1.SpaceField {
53
54
  constructor(fieldRef, inFS) {
54
55
  super();
@@ -112,7 +113,12 @@ class ReferenceField extends space_field_1.SpaceField {
112
113
  return this.memoTypeDesc;
113
114
  const refTo = this.referenceTo;
114
115
  if (refTo) {
115
- this.memoTypeDesc = refTo.typeDesc();
116
+ const joinPath = this.fieldRef.list.slice(0, -1).map(x => x.refString);
117
+ const typeDesc = refTo.typeDesc();
118
+ this.memoTypeDesc = {
119
+ ...typeDesc,
120
+ compositeFieldUsage: (0, composite_source_utils_1.joinedCompositeFieldUsage)(joinPath, typeDesc.compositeFieldUsage),
121
+ };
116
122
  return this.memoTypeDesc;
117
123
  }
118
124
  return TDU.errorT;
@@ -11,4 +11,5 @@ export declare class RefinedSpace extends DynamicSpace {
11
11
  */
12
12
  static filteredFrom(from: SourceDef, choose: FieldListEdit | undefined, parameters: ParameterSpace | undefined): RefinedSpace;
13
13
  pushFields(...defs: MalloyElement[]): void;
14
+ addField(def: MalloyElement): void;
14
15
  }
@@ -60,12 +60,15 @@ class RefinedSpace extends dynamic_space_1.DynamicSpace {
60
60
  }
61
61
  pushFields(...defs) {
62
62
  for (const me of defs) {
63
- if ((0, space_entry_1.canMakeEntry)(me)) {
64
- me.makeEntry(this);
65
- }
66
- else {
67
- me.logError('unexpected-element-type', `Internal error, ${me.elementType} not expected in this context`);
68
- }
63
+ this.addField(me);
64
+ }
65
+ }
66
+ addField(def) {
67
+ if ((0, space_entry_1.canMakeEntry)(def)) {
68
+ def.makeEntry(this);
69
+ }
70
+ else {
71
+ def.logError('unexpected-element-type', `Internal error, ${def.elementType} not expected in this context`);
69
72
  }
70
73
  }
71
74
  }