@malloydata/malloy 0.0.300 → 0.0.301

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.
@@ -263,8 +263,13 @@ class FieldDefinitionValue extends space_field_1.SpaceField {
263
263
  return this.fieldName;
264
264
  }
265
265
  fieldDef() {
266
- var _a;
267
- const def = (_a = this.defInSource) !== null && _a !== void 0 ? _a : this.exprDef.fieldDef(this.space, this.name);
266
+ var _a, _b;
267
+ // Checking `defInQuery` is necessary to support a case where a field needs
268
+ // to be looked up from the output space (ex: in `order_by: a`), but where
269
+ // the field is defined in a group_by expression (ex: `group_by: a.day`).
270
+ // In this case, this.exprDef.fieldDef() only ever returns an
271
+ // AtomicFieldDef anyways, so it is safe in this particular implementation.
272
+ const def = (_b = (_a = this.defInSource) !== null && _a !== void 0 ? _a : this.defInQuery) !== null && _b !== void 0 ? _b : this.exprDef.fieldDef(this.space, this.name);
268
273
  this.defInSource = def;
269
274
  return def;
270
275
  }
@@ -26,6 +26,7 @@ exports.DefineSourceList = exports.DefineSource = void 0;
26
26
  const error_factory_1 = require("../error-factory");
27
27
  const malloy_element_1 = require("../types/malloy-element");
28
28
  const noteable_1 = require("../types/noteable");
29
+ const composite_source_utils_1 = require("../../../model/composite_source_utils");
29
30
  class DefineSource extends malloy_element_1.MalloyElement {
30
31
  constructor(name, sourceExpr, exported, parameters) {
31
32
  super();
@@ -44,33 +45,34 @@ class DefineSource extends malloy_element_1.MalloyElement {
44
45
  }
45
46
  }
46
47
  execute(doc) {
47
- var _a;
48
+ var _a, _b, _c;
48
49
  if (doc.modelEntry(this.name)) {
49
50
  this.logError('source-definition-name-conflict', `Cannot redefine '${this.name}'`);
51
+ return;
50
52
  }
51
- else {
52
- const theSource = (_a = this.sourceExpr) === null || _a === void 0 ? void 0 : _a.getSource();
53
- if (theSource === undefined) {
54
- return;
55
- }
56
- const parameters = this.deduplicatedParameters();
57
- const structDef = theSource.withParameters(undefined, this.parameters);
58
- this.validateParameterShadowing(parameters, structDef);
59
- if (error_factory_1.ErrorFactory.didCreate(structDef)) {
60
- return;
61
- }
62
- const entry = {
63
- ...structDef,
64
- as: this.name,
65
- location: this.location,
66
- };
67
- if (this.note) {
68
- entry.annotation = structDef.annotation
69
- ? { ...this.note, inherits: structDef.annotation }
70
- : this.note;
71
- }
72
- doc.setEntry(this.name, { entry, exported: this.exported });
53
+ const theSource = (_a = this.sourceExpr) === null || _a === void 0 ? void 0 : _a.getSource();
54
+ if (theSource === undefined) {
55
+ return;
56
+ }
57
+ const parameters = this.deduplicatedParameters();
58
+ const structDef = theSource.withParameters(undefined, this.parameters);
59
+ this.validateParameterShadowing(parameters, structDef);
60
+ if (error_factory_1.ErrorFactory.didCreate(structDef)) {
61
+ return;
62
+ }
63
+ const entry = {
64
+ ...structDef,
65
+ as: this.name,
66
+ location: this.location,
67
+ };
68
+ if (this.note) {
69
+ entry.annotation = structDef.annotation
70
+ ? { ...this.note, inherits: structDef.annotation }
71
+ : this.note;
73
72
  }
73
+ entry.partitionComposite =
74
+ (_c = (0, composite_source_utils_1.getPartitionCompositeDesc)(this.note, structDef, (_b = this.sourceExpr) !== null && _b !== void 0 ? _b : this)) !== null && _c !== void 0 ? _c : structDef.partitionComposite;
75
+ doc.setEntry(this.name, { entry, exported: this.exported });
74
76
  }
75
77
  deduplicatedParameters() {
76
78
  if (this.parameters === undefined)
@@ -384,6 +384,7 @@ type MessageParameterTypes = {
384
384
  'grouped-by-not-found': string;
385
385
  'non-scalar-grouped-by': string;
386
386
  'missing-required-group-by': string;
387
+ 'invalid-partition-composite': string;
387
388
  };
388
389
  export declare const MESSAGE_FORMATTERS: PartialErrorCodeMessageMap;
389
390
  export type MessageCode = keyof MessageParameterTypes;
@@ -1,6 +1,6 @@
1
1
  import type { BooleanFilter, NumberFilter, StringFilter, TemporalFilter } from '@malloydata/malloy-filter';
2
2
  import type { MalloyElement } from '../lang/ast';
3
- import type { FieldUsage, PipeSegment, SourceDef, Expr, StructDef, RequiredGroupBy } from './malloy_types';
3
+ import type { FieldUsage, PipeSegment, SourceDef, Expr, RequiredGroupBy, Annotation, PartitionCompositeDesc, StructDef } from './malloy_types';
4
4
  type CompositeCouldNotFindFieldError = {
5
5
  code: 'could_not_find_field';
6
6
  data: {
@@ -51,6 +51,7 @@ interface CompositeFailure {
51
51
  source: SourceDef;
52
52
  issues: CompositeIssue[];
53
53
  }
54
+ export declare function getPartitionCompositeDesc(annotation: Annotation | undefined, structDef: StructDef, logTo: MalloyElement): PartitionCompositeDesc | undefined;
54
55
  type SingleNarrowedCompositeFieldResolution = {
55
56
  source: SourceDef;
56
57
  nested?: SingleNarrowedCompositeFieldResolution | undefined;
@@ -84,7 +85,7 @@ export declare function fieldUsageJoinPaths(fieldUsage: FieldUsage[]): string[][
84
85
  export declare function checkRequiredGroupBys(compositeResolvedSourceDef: SourceDef, segment: PipeSegment): RequiredGroupBy[];
85
86
  export declare function pathEq(a: string[], b: string[]): boolean;
86
87
  export declare function pathBegins(path: string[], prefix: string[]): boolean;
87
- export declare function hasCompositesAnywhere(source: StructDef): boolean;
88
+ export declare function hasCompositesAnywhere(source: SourceDef): boolean;
88
89
  export declare function logCompositeError(error: CompositeError, logTo: MalloyElement): void;
89
90
  export declare function compileFilterExpression(ft: string, fexpr: Expr): {
90
91
  kind: 'date' | 'timestamp';
@@ -6,6 +6,7 @@
6
6
  * LICENSE file in the root directory of this source tree.
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.getPartitionCompositeDesc = getPartitionCompositeDesc;
9
10
  exports.resolveCompositeSources = resolveCompositeSources;
10
11
  exports.fieldUsagePaths = fieldUsagePaths;
11
12
  exports.formatFieldUsages = formatFieldUsages;
@@ -27,10 +28,11 @@ exports.compileFilterExpression = compileFilterExpression;
27
28
  const malloy_filter_1 = require("@malloydata/malloy-filter");
28
29
  const malloy_types_1 = require("./malloy_types");
29
30
  const utils_1 = require("../lang/utils");
31
+ const annotation_1 = require("../annotation");
30
32
  function _resolveCompositeSources(path, source, rootFields, nests, fieldUsage,
31
33
  // for resolving nested composites; the list of sources to try
32
34
  sources) {
33
- var _a, _b, _c, _d;
35
+ var _a, _b, _c, _d, _e;
34
36
  // TODO skip all this if the tree doesn't have any composite sources
35
37
  let base = { ...source };
36
38
  let anyComposites = false;
@@ -93,8 +95,9 @@ sources) {
93
95
  abort();
94
96
  continue overSources;
95
97
  }
96
- if (inputSource.type === 'composite') {
97
- const resolveInner = _resolveCompositeSources(path, inputSource, genRootFields(rootFields, path, fieldsForLookup, false), nests, compositeUsageInThisSource, inputSource.sources);
98
+ if (inputSource.type === 'composite' ||
99
+ inputSource.partitionComposite !== undefined) {
100
+ const resolveInner = _resolveCompositeSources(path, inputSource, genRootFields(rootFields, path, fieldsForLookup, false), nests, compositeUsageInThisSource, inputSource.type === 'composite' ? inputSource.sources : undefined);
98
101
  if ('error' in resolveInner) {
99
102
  // Third point where we abort; if a nested composite failed; we don't call abort() because we want to unnest the failures from
100
103
  if (resolveInner.error.code === 'no_suitable_composite_source_input') {
@@ -171,6 +174,29 @@ sources) {
171
174
  };
172
175
  }
173
176
  }
177
+ else if (source.partitionComposite !== undefined) {
178
+ anyComposites = true;
179
+ const expanded = expandFieldUsage(fieldUsage, rootFields).result;
180
+ // TODO possibly abort if expanded has missing fields...
181
+ const expandedCategorized = categorizeFieldUsage(expanded);
182
+ const { partitionFilter, issues } = getPartitionCompositeFilter(source.partitionComposite, expandedCategorized.sourceUsage);
183
+ if (issues !== undefined) {
184
+ return {
185
+ error: {
186
+ code: 'no_suitable_composite_source_input',
187
+ data: {
188
+ failures: issues.map(iss => ({ source, issues: iss })),
189
+ usage: expanded,
190
+ path,
191
+ },
192
+ },
193
+ };
194
+ }
195
+ base = {
196
+ ...source,
197
+ filterList: [...((_e = source.filterList) !== null && _e !== void 0 ? _e : []), partitionFilter],
198
+ };
199
+ }
174
200
  if (!joinsProcessed) {
175
201
  const expanded = expandFieldUsage(fieldUsage, getJoinFields(rootFields, path));
176
202
  if (expanded.missingFields.length > 0) {
@@ -260,6 +286,89 @@ function categorizeFieldUsage(fieldUsage) {
260
286
  }
261
287
  return categorized;
262
288
  }
289
+ function getPartitionCompositePartition(partitionComposite, fieldUsage) {
290
+ const issues = [];
291
+ const compositeFieldsUsed = fieldUsage.filter(u => partitionComposite.compositeFields.some(f => u.path.length === 1 && u.path[0] === f));
292
+ for (const partition of partitionComposite.partitions) {
293
+ const missingFields = compositeFieldsUsed.filter(u => u.path.length !== 1 || !partition.fields.includes(u.path[0]));
294
+ if (missingFields.length === 0) {
295
+ return { partitionId: partition.id, issues: undefined };
296
+ }
297
+ issues.push(missingFields.map(f => ({ type: 'missing-field', field: f })));
298
+ }
299
+ return {
300
+ partitionId: undefined,
301
+ issues,
302
+ };
303
+ }
304
+ function getPartitionCompositeFilter(partitionComposite, fieldUsage) {
305
+ const { partitionId, issues } = getPartitionCompositePartition(partitionComposite, fieldUsage);
306
+ if (issues !== undefined)
307
+ return { issues, partitionFilter: undefined };
308
+ const partitionFilter = {
309
+ node: 'filterCondition',
310
+ code: '',
311
+ expressionType: 'scalar',
312
+ e: {
313
+ node: '=',
314
+ kids: {
315
+ left: {
316
+ node: 'field',
317
+ // TODO validate field exists
318
+ path: [partitionComposite.partitionField],
319
+ },
320
+ right: {
321
+ node: 'stringLiteral',
322
+ literal: partitionId,
323
+ },
324
+ },
325
+ },
326
+ };
327
+ return { partitionFilter, issues: undefined };
328
+ }
329
+ function getPartitionCompositeDesc(annotation, structDef, logTo) {
330
+ if (annotation === undefined)
331
+ return undefined;
332
+ const compilerFlags = (0, annotation_1.annotationToTag)(annotation, { prefix: /^#!\s*/ }).tag;
333
+ const partitionCompositeTag = compilerFlags.tag('experimental', 'partition_composite');
334
+ if (partitionCompositeTag === undefined)
335
+ return undefined;
336
+ if (structDef.type === 'composite') {
337
+ logTo.logError('invalid-partition-composite', 'Source is already composite; cannot apply partition composite');
338
+ return undefined;
339
+ }
340
+ const partitionField = partitionCompositeTag.text('partition_field');
341
+ const partitionsTag = partitionCompositeTag.tag('partitions');
342
+ if (partitionField === undefined) {
343
+ logTo.logError('invalid-partition-composite', 'Partition composite must specify `partition_field`');
344
+ return undefined;
345
+ }
346
+ if (partitionsTag === undefined) {
347
+ logTo.logError('invalid-partition-composite', 'Partition composite must specify `partitions`');
348
+ return undefined;
349
+ }
350
+ const partitions = [];
351
+ const allFields = new Set();
352
+ const ids = Object.keys(partitionsTag.getProperties());
353
+ for (const id of ids) {
354
+ const partitionTag = partitionsTag.tag(id);
355
+ if (partitionTag === undefined) {
356
+ logTo.logError('invalid-partition-composite', `Invalid partition specification for \`${id}\`; must be a tag with property \\fields\``);
357
+ return undefined;
358
+ }
359
+ const fields = Object.keys(partitionsTag.getProperties());
360
+ allFields.forEach(f => allFields.add(f));
361
+ partitions.push({ id, fields });
362
+ }
363
+ for (const field of [partitionField, ...allFields]) {
364
+ const def = structDef.fields.find(f => { var _a; return ((_a = f.as) !== null && _a !== void 0 ? _a : f.name) === field; });
365
+ if (def === undefined) {
366
+ logTo.logError('invalid-partition-composite', `Composite partition field \`${field}\` not present in source`);
367
+ }
368
+ }
369
+ const compositeFields = structDef.fields.map(f => { var _a; return (_a = f.as) !== null && _a !== void 0 ? _a : f.name; });
370
+ return { partitionField, partitions, compositeFields };
371
+ }
263
372
  function composeAnnotations(base, slice) {
264
373
  var _a, _b, _c, _d;
265
374
  if (base === undefined)
@@ -854,10 +963,11 @@ function sortIssuesByReferenceLocation(issues) {
854
963
  });
855
964
  }
856
965
  function hasCompositesAnywhere(source) {
857
- if (source.type === 'composite')
966
+ if (source.type === 'composite' || source.partitionComposite !== undefined) {
858
967
  return true;
968
+ }
859
969
  for (const field of source.fields) {
860
- if ((0, malloy_types_1.isJoined)(field) && hasCompositesAnywhere(field)) {
970
+ if ((0, malloy_types_1.isJoined)(field) && (0, malloy_types_1.isSourceDef)(field) && hasCompositesAnywhere(field)) {
861
971
  return true;
862
972
  }
863
973
  }
@@ -623,6 +623,14 @@ interface StructDefBase extends HasLocation, NamedObject {
623
623
  modelAnnotation?: ModelAnnotation;
624
624
  fields: FieldDef[];
625
625
  }
626
+ export interface PartitionCompositeDesc {
627
+ partitionField: string;
628
+ partitions: {
629
+ id: string;
630
+ fields: string[];
631
+ }[];
632
+ compositeFields: string[];
633
+ }
626
634
  interface SourceDefBase extends StructDefBase, Filtered, ResultStructMetadata {
627
635
  arguments?: Record<string, Argument>;
628
636
  parameters?: Record<string, Parameter>;
@@ -630,6 +638,7 @@ interface SourceDefBase extends StructDefBase, Filtered, ResultStructMetadata {
630
638
  connection: string;
631
639
  primaryKey?: PrimaryKeyRef;
632
640
  dialect: string;
641
+ partitionComposite?: PartitionCompositeDesc;
633
642
  }
634
643
  /** which field is the primary key in this struct */
635
644
  export type PrimaryKeyRef = string;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const MALLOY_VERSION = "0.0.300";
1
+ export declare const MALLOY_VERSION = "0.0.301";
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.300';
5
+ exports.MALLOY_VERSION = '0.0.301';
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.300",
3
+ "version": "0.0.301",
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.300",
45
- "@malloydata/malloy-interfaces": "0.0.300",
46
- "@malloydata/malloy-tag": "0.0.300",
44
+ "@malloydata/malloy-filter": "0.0.301",
45
+ "@malloydata/malloy-interfaces": "0.0.301",
46
+ "@malloydata/malloy-tag": "0.0.301",
47
47
  "antlr4ts": "^0.5.0-alpha.4",
48
48
  "assert": "^2.0.0",
49
49
  "jaro-winkler": "^0.2.8",