@malloydata/malloy 0.0.303 → 0.0.305

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 (74) hide show
  1. package/dist/dialect/dialect.d.ts +1 -1
  2. package/dist/dialect/duckdb/duckdb.d.ts +1 -1
  3. package/dist/dialect/duckdb/duckdb.js +2 -6
  4. package/dist/dialect/mysql/mysql.d.ts +1 -1
  5. package/dist/dialect/mysql/mysql.js +2 -6
  6. package/dist/dialect/postgres/postgres.d.ts +1 -1
  7. package/dist/dialect/postgres/postgres.js +2 -6
  8. package/dist/dialect/snowflake/snowflake.d.ts +1 -1
  9. package/dist/dialect/snowflake/snowflake.js +2 -5
  10. package/dist/dialect/standardsql/standardsql.d.ts +1 -1
  11. package/dist/dialect/standardsql/standardsql.js +2 -6
  12. package/dist/dialect/trino/trino.d.ts +1 -1
  13. package/dist/dialect/trino/trino.js +2 -6
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +2 -3
  16. package/dist/lang/ast/expressions/expr-aggregate-function.js +12 -2
  17. package/dist/lang/ast/expressions/expr-count.js +3 -1
  18. package/dist/lang/ast/expressions/expr-func.js +34 -10
  19. package/dist/lang/ast/expressions/expr-props.js +1 -1
  20. package/dist/lang/ast/expressions/expr-ungroup.js +7 -3
  21. package/dist/lang/ast/expressions/function-ordering.d.ts +19 -5
  22. package/dist/lang/ast/expressions/function-ordering.js +61 -9
  23. package/dist/lang/ast/field-space/include-utils.js +1 -1
  24. package/dist/lang/ast/field-space/index-field-space.js +3 -1
  25. package/dist/lang/ast/field-space/query-spaces.js +20 -11
  26. package/dist/lang/ast/query-builders/index-builder.js +1 -1
  27. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  28. package/dist/lang/ast/query-elements/query-arrow.js +14 -4
  29. package/dist/lang/ast/query-elements/query-base.d.ts +1 -0
  30. package/dist/lang/ast/query-elements/query-base.js +14 -4
  31. package/dist/lang/ast/query-elements/query-refine.js +2 -0
  32. package/dist/lang/ast/query-properties/drill.js +1 -1
  33. package/dist/lang/ast/source-properties/join.js +6 -2
  34. package/dist/lang/ast/statements/define-source.js +1 -1
  35. package/dist/lang/ast/types/expr-value.js +1 -1
  36. package/dist/lang/ast/view-elements/reference-view.js +4 -1
  37. package/dist/lang/ast/view-elements/refine-utils.js +1 -1
  38. package/dist/{model/composite_source_utils.d.ts → lang/composite-source-utils.d.ts} +4 -17
  39. package/dist/{model/composite_source_utils.js → lang/composite-source-utils.js} +274 -44
  40. package/dist/lang/test/parse-expects.d.ts +1 -1
  41. package/dist/lang/test/parse-expects.js +6 -2
  42. package/dist/lang/test/test-translator.js +1 -1
  43. package/dist/malloy.js +1 -1
  44. package/dist/model/expression_compiler.d.ts +27 -0
  45. package/dist/model/expression_compiler.js +780 -0
  46. package/dist/model/field_instance.d.ts +108 -0
  47. package/dist/model/field_instance.js +520 -0
  48. package/dist/model/index.d.ts +5 -1
  49. package/dist/model/index.js +25 -4
  50. package/dist/model/join_instance.d.ts +18 -0
  51. package/dist/model/join_instance.js +71 -0
  52. package/dist/model/malloy_types.d.ts +48 -2
  53. package/dist/model/malloy_types.js +39 -1
  54. package/dist/model/query_model.d.ts +2 -0
  55. package/dist/model/query_model.js +7 -0
  56. package/dist/model/query_model_contract.d.ts +32 -0
  57. package/dist/model/query_model_contract.js +7 -0
  58. package/dist/model/query_model_impl.d.ts +30 -0
  59. package/dist/model/query_model_impl.js +266 -0
  60. package/dist/model/query_node.d.ts +132 -0
  61. package/dist/model/query_node.js +638 -0
  62. package/dist/model/query_query.d.ts +86 -0
  63. package/dist/model/query_query.js +1724 -0
  64. package/dist/model/sql_block.js +2 -2
  65. package/dist/model/stage_writer.d.ts +25 -0
  66. package/dist/model/stage_writer.js +120 -0
  67. package/dist/model/utils.d.ts +18 -1
  68. package/dist/model/utils.js +66 -1
  69. package/dist/to_stable.js +3 -4
  70. package/dist/version.d.ts +1 -1
  71. package/dist/version.js +1 -1
  72. package/package.json +4 -4
  73. package/dist/model/malloy_query.d.ts +0 -391
  74. package/dist/model/malloy_query.js +0 -3926
@@ -0,0 +1,108 @@
1
+ import type { QueryInfo } from '../dialect';
2
+ import type { QueryStruct } from './query_node';
3
+ import type { Expr, OrderBy, PipeSegment, TurtleDef, UniqueKeyRequirement } from './malloy_types';
4
+ import { AndChain, type GenerateState } from './utils';
5
+ import { JoinInstance } from './join_instance';
6
+ import { type QueryField } from './query_node';
7
+ import type * as Malloy from '@malloydata/malloy-interfaces';
8
+ type InstanceFieldUsage = {
9
+ type: 'result';
10
+ resultIndex: number;
11
+ } | {
12
+ type: 'where';
13
+ } | {
14
+ type: 'dependant';
15
+ };
16
+ export declare class FieldInstanceField implements FieldInstance {
17
+ f: QueryField;
18
+ fieldUsage: InstanceFieldUsage;
19
+ parent: FieldInstanceResult;
20
+ readonly drillExpression: Malloy.Expression | undefined;
21
+ type: FieldInstanceType;
22
+ additionalGroupSets: number[];
23
+ analyticalSQL: string | undefined;
24
+ partitionSQL: string | undefined;
25
+ constructor(f: QueryField, fieldUsage: InstanceFieldUsage, parent: FieldInstanceResult, drillExpression: Malloy.Expression | undefined);
26
+ root(): FieldInstanceResultRoot;
27
+ static exprCompiler?: (field: QueryField, resultSet: FieldInstanceResult, context: QueryStruct, expr: Expr, state?: GenerateState) => string;
28
+ static registerExpressionCompiler(compiler: (field: QueryField, resultSet: FieldInstanceResult, context: QueryStruct, expr: Expr, state?: GenerateState) => string): void;
29
+ getSQL(): string;
30
+ generateExpression(): string;
31
+ private generateDistinctKeyExpression;
32
+ getAnalyticalSQL(forPartition: boolean): string;
33
+ }
34
+ type RepeatedResultType = 'nested' | 'inline_all_numbers' | 'inline';
35
+ export type UngroupSet = {
36
+ type: 'all' | 'exclude';
37
+ fields: string[];
38
+ groupSet: number;
39
+ };
40
+ export declare class FieldInstanceResult implements FieldInstance {
41
+ turtleDef: TurtleDef;
42
+ parent: FieldInstanceResult | undefined;
43
+ type: FieldInstanceType;
44
+ allFields: Map<string, FieldInstance>;
45
+ groupSet: number;
46
+ depth: number;
47
+ childGroups: number[];
48
+ firstSegment: PipeSegment;
49
+ hasHaving: boolean;
50
+ ungroupedSets: Map<string, UngroupSet>;
51
+ resultUsesUngrouped: boolean;
52
+ constructor(turtleDef: TurtleDef, parent: FieldInstanceResult | undefined);
53
+ getLimit(): number | undefined;
54
+ /**
55
+ * Information about the query containing this result set. Invented
56
+ * to pass on timezone information, but maybe more things will
57
+ * eventually go in here.
58
+ * @returns QueryInfo
59
+ */
60
+ getQueryInfo(): QueryInfo;
61
+ addField(as: string, field: QueryField, usage: InstanceFieldUsage, drillExpression: Malloy.Expression | undefined): void;
62
+ parentGroupSet(): number;
63
+ add(name: string, f: FieldInstance): void;
64
+ hasField(name: string): boolean;
65
+ getField(name: string): FieldInstanceField;
66
+ getFieldByNumber(index: number): {
67
+ name: string;
68
+ fif: FieldInstanceField;
69
+ };
70
+ computeGroups(nextGroupSetNumber: number, depth: number): {
71
+ nextGroupSetNumber: number;
72
+ maxDepth: number;
73
+ children: number[];
74
+ isComplex: boolean;
75
+ };
76
+ fields(fn?: undefined | ((field: FieldInstanceField) => boolean)): FieldInstanceField[];
77
+ fieldNames(fn: undefined | ((field: FieldInstanceField) => boolean)): string[];
78
+ getRepeatedResultType(): RepeatedResultType;
79
+ structs(): FieldInstanceResult[];
80
+ selectStructs(result: FieldInstanceResult[], fn: (result: FieldInstanceResult) => boolean): FieldInstanceResult[];
81
+ calculateDefaultOrderBy(): OrderBy[];
82
+ addStructToJoin(qs: QueryStruct, uniqueKeyRequirement: UniqueKeyRequirement): void;
83
+ root(): FieldInstanceResultRoot;
84
+ getUngroupPartitions(ungroupSet: UngroupSet | undefined): FieldInstanceField[];
85
+ assignFieldsToGroups(): void;
86
+ }
87
+ type FieldInstanceType = 'field' | 'query';
88
+ export interface FieldInstance {
89
+ type: FieldInstanceType;
90
+ root(): FieldInstanceResultRoot;
91
+ }
92
+ export declare class FieldInstanceResultRoot extends FieldInstanceResult {
93
+ joins: Map<string, JoinInstance>;
94
+ havings: AndChain;
95
+ isComplexQuery: boolean;
96
+ queryUsesPartitioning: boolean;
97
+ computeOnlyGroups: number[];
98
+ elimatedComputeGroups: boolean;
99
+ constructor(turtleDef: TurtleDef);
100
+ root(): FieldInstanceResultRoot;
101
+ eliminateComputeGroupsSQL(): string;
102
+ calculateSymmetricAggregates(): void;
103
+ }
104
+ export declare function sqlFullChildReference(struct: QueryStruct, name: string, expand: {
105
+ result: FieldInstanceResult;
106
+ field: QueryField;
107
+ } | undefined): string;
108
+ export {};
@@ -0,0 +1,520 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.FieldInstanceResultRoot = exports.FieldInstanceResult = exports.FieldInstanceField = void 0;
8
+ exports.sqlFullChildReference = sqlFullChildReference;
9
+ const malloy_types_1 = require("./malloy_types");
10
+ const utils_1 = require("./utils");
11
+ const join_instance_1 = require("./join_instance");
12
+ const query_node_1 = require("./query_node");
13
+ class FieldInstanceField {
14
+ constructor(f, fieldUsage, parent, drillExpression) {
15
+ this.f = f;
16
+ this.fieldUsage = fieldUsage;
17
+ this.parent = parent;
18
+ this.drillExpression = drillExpression;
19
+ this.type = 'field';
20
+ this.additionalGroupSets = [];
21
+ }
22
+ root() {
23
+ return this.parent.root();
24
+ }
25
+ static registerExpressionCompiler(compiler) {
26
+ FieldInstanceField.exprCompiler = compiler;
27
+ }
28
+ getSQL() {
29
+ let exp = this.generateExpression(); // Changed from this.f.generateExpression(this.parent)
30
+ if ((0, query_node_1.isScalarField)(this.f)) {
31
+ exp = (0, utils_1.caseGroup)(this.parent.groupSet > 0
32
+ ? this.parent.childGroups.concat(this.additionalGroupSets)
33
+ : [], exp);
34
+ }
35
+ return exp;
36
+ }
37
+ generateExpression() {
38
+ if (!FieldInstanceField.exprCompiler) {
39
+ throw new Error('Expression compiler not registered with FieldInstanceField');
40
+ }
41
+ // Check for distinct key by its characteristic properties
42
+ if (this.f.fieldDef.type === 'string' &&
43
+ this.f.fieldDef.name === '__distinct_key') {
44
+ return this.generateDistinctKeyExpression();
45
+ }
46
+ // Normal field expression generation
47
+ if ((0, malloy_types_1.hasExpression)(this.f.fieldDef)) {
48
+ return FieldInstanceField.exprCompiler(this.f, this.parent, this.f.parent, this.f.fieldDef.e);
49
+ }
50
+ // Walk the tree and compute record aliases if needed
51
+ for (let ancestor = this.f.parent; ancestor !== undefined; ancestor = ancestor.parent) {
52
+ if (ancestor.structDef.type === 'record' &&
53
+ (0, malloy_types_1.hasExpression)(ancestor.structDef) &&
54
+ ancestor.recordAlias === undefined) {
55
+ if (!ancestor.parent) {
56
+ throw new Error('Inconceivable record ancestor with expression but no parent');
57
+ }
58
+ const aliasValue = FieldInstanceField.exprCompiler(this.f, this.parent, ancestor.parent, ancestor.structDef.e);
59
+ ancestor.informOfAliasValue(aliasValue);
60
+ }
61
+ }
62
+ return sqlFullChildReference(this.f.parent, this.f.fieldDef.name, this.f.parent.structDef.type === 'record'
63
+ ? {
64
+ result: this.parent,
65
+ field: this.f,
66
+ }
67
+ : undefined);
68
+ }
69
+ generateDistinctKeyExpression() {
70
+ var _a;
71
+ if (this.f.parent.primaryKey()) {
72
+ const pk = this.f.parent.getPrimaryKeyField(this.f.fieldDef);
73
+ const pkName = pk.fieldDef.as || pk.fieldDef.name;
74
+ const pkField = this.parent.getField(pkName);
75
+ return pkField.generateExpression();
76
+ }
77
+ else if (this.f.parent.structDef.type === 'array') {
78
+ const parentDistinctKey = (_a = this.f.parent.parent) === null || _a === void 0 ? void 0 : _a.getDistinctKey();
79
+ if (parentDistinctKey && this.parent.parent) {
80
+ const keyField = this.parent.parent.getField('__distinct_key');
81
+ const parentKeySQL = keyField.generateExpression();
82
+ return this.f.parent.dialect.sqlMakeUnnestKey(parentKeySQL, this.f.parent.dialect.sqlFieldReference(this.f.parent.getIdentifier(), 'table', '__row_id', 'string'));
83
+ }
84
+ return this.f.parent.dialect.sqlMakeUnnestKey('', this.f.parent.dialect.sqlFieldReference(this.f.parent.getIdentifier(), 'table', '__row_id', 'string'));
85
+ }
86
+ else {
87
+ return this.f.parent.dialect.sqlFieldReference(this.f.parent.getIdentifier(), 'table', '__distinct_key', 'string');
88
+ }
89
+ }
90
+ getAnalyticalSQL(forPartition) {
91
+ if (this.analyticalSQL === undefined) {
92
+ return this.getSQL();
93
+ }
94
+ else if (forPartition && this.partitionSQL) {
95
+ return this.partitionSQL;
96
+ }
97
+ else {
98
+ return this.analyticalSQL;
99
+ }
100
+ }
101
+ }
102
+ exports.FieldInstanceField = FieldInstanceField;
103
+ class FieldInstanceResult {
104
+ constructor(turtleDef, parent) {
105
+ this.turtleDef = turtleDef;
106
+ this.parent = parent;
107
+ this.type = 'query';
108
+ this.allFields = new Map();
109
+ this.groupSet = 0;
110
+ this.depth = 0;
111
+ this.childGroups = [];
112
+ this.hasHaving = false;
113
+ this.ungroupedSets = new Map();
114
+ // query: QueryQuery;
115
+ this.resultUsesUngrouped = false;
116
+ this.firstSegment = turtleDef.pipeline[0];
117
+ }
118
+ // Gets a limit if it has one.
119
+ getLimit() {
120
+ if (this.firstSegment.type === 'reduce' ||
121
+ this.firstSegment.type === 'project') {
122
+ return this.firstSegment.limit;
123
+ }
124
+ else {
125
+ return undefined;
126
+ }
127
+ }
128
+ /**
129
+ * Information about the query containing this result set. Invented
130
+ * to pass on timezone information, but maybe more things will
131
+ * eventually go in here.
132
+ * @returns QueryInfo
133
+ */
134
+ getQueryInfo() {
135
+ if (!(0, malloy_types_1.isIndexSegment)(this.firstSegment) &&
136
+ !(0, malloy_types_1.isRawSegment)(this.firstSegment)) {
137
+ const { queryTimezone } = this.firstSegment;
138
+ if (queryTimezone) {
139
+ return { queryTimezone };
140
+ }
141
+ }
142
+ return {};
143
+ }
144
+ addField(as, field, usage, drillExpression) {
145
+ const fi = this.allFields.get(as);
146
+ if (fi) {
147
+ if (fi.type === 'query') {
148
+ throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
149
+ }
150
+ const fif = fi;
151
+ if (fif.fieldUsage.type === 'result') {
152
+ if (usage.type !== 'result') {
153
+ // its already in the result, we can just ignore it.
154
+ return;
155
+ }
156
+ else {
157
+ throw new Error(`Ambiguous output field name '${field.fieldDef.name}'.`);
158
+ }
159
+ }
160
+ }
161
+ this.add(as, new FieldInstanceField(field, usage, this, drillExpression));
162
+ }
163
+ parentGroupSet() {
164
+ if (this.parent) {
165
+ return this.parent.groupSet;
166
+ }
167
+ else {
168
+ return 0;
169
+ }
170
+ }
171
+ add(name, f) {
172
+ this.allFields.set(name, f);
173
+ }
174
+ hasField(name) {
175
+ const fi = this.allFields.get(name);
176
+ return fi !== undefined && fi instanceof FieldInstanceField;
177
+ }
178
+ getField(name) {
179
+ const fi = this.allFields.get(name);
180
+ if (fi === undefined) {
181
+ throw new Error(`Internal Error, field Not defined ${name}`);
182
+ }
183
+ else if (fi instanceof FieldInstanceField) {
184
+ return fi;
185
+ }
186
+ throw new Error(`can't use a query here ${name}`);
187
+ }
188
+ getFieldByNumber(index) {
189
+ for (const [name, fi] of this.allFields) {
190
+ if (fi instanceof FieldInstanceField) {
191
+ if (fi.fieldUsage.type === 'result' &&
192
+ fi.fieldUsage.resultIndex === index) {
193
+ return { name, fif: fi };
194
+ }
195
+ }
196
+ }
197
+ throw new Error(`Invalid Order By index '${index}`);
198
+ }
199
+ // loops through all the turtled queries and computes recomputes the group numbers
200
+ computeGroups(nextGroupSetNumber, depth) {
201
+ // if the root node uses a total, start at 1.
202
+ if (nextGroupSetNumber === 0 && this.resultUsesUngrouped) {
203
+ this.root().computeOnlyGroups.push(nextGroupSetNumber++);
204
+ }
205
+ // make a groupset for each unique ungrouping expression
206
+ for (const [_key, grouping] of this.ungroupedSets) {
207
+ const groupSet = nextGroupSetNumber++;
208
+ grouping.groupSet = groupSet;
209
+ this.root().computeOnlyGroups.push(groupSet);
210
+ }
211
+ this.groupSet = nextGroupSetNumber++;
212
+ this.depth = depth;
213
+ let maxDepth = depth;
214
+ let isComplex = false;
215
+ let children = [this.groupSet];
216
+ for (const [_name, fi] of this.allFields) {
217
+ if (fi.type === 'query') {
218
+ const fir = fi;
219
+ isComplex = true;
220
+ if (fir.firstSegment.type === 'reduce') {
221
+ const r = fir.computeGroups(nextGroupSetNumber, depth + 1);
222
+ children = children.concat(r.children);
223
+ nextGroupSetNumber = r.nextGroupSetNumber;
224
+ if (r.maxDepth > maxDepth) {
225
+ maxDepth = r.maxDepth;
226
+ }
227
+ }
228
+ }
229
+ }
230
+ this.childGroups = children;
231
+ return { nextGroupSetNumber, maxDepth, children, isComplex };
232
+ }
233
+ fields(fn = undefined) {
234
+ const ret = [];
235
+ for (const e of this.allFields.values()) {
236
+ if (e instanceof FieldInstanceField) {
237
+ if (fn === undefined || fn(e)) {
238
+ ret.push(e);
239
+ }
240
+ }
241
+ }
242
+ return ret;
243
+ }
244
+ fieldNames(fn) {
245
+ const ret = [];
246
+ for (const [name, fi] of this.allFields) {
247
+ if (fi instanceof FieldInstanceField) {
248
+ if (fn === undefined || fn(fi)) {
249
+ ret.push(name);
250
+ }
251
+ }
252
+ }
253
+ return ret;
254
+ }
255
+ // if a turtled result is all measures, we emit use ANY_VALUE for the aggregation
256
+ // and emit the resulting structure as a RECORD instead of REPEATED
257
+ // if we have all numbers, we need to know because we'll have to conjur a record.
258
+ getRepeatedResultType() {
259
+ let ret = 'inline_all_numbers';
260
+ for (const f of this.fields()) {
261
+ if (f.fieldUsage.type === 'result') {
262
+ if ((0, query_node_1.isBasicScalar)(f.f)) {
263
+ return 'nested';
264
+ }
265
+ if (f.f instanceof query_node_1.QueryFieldStruct) {
266
+ ret = 'inline';
267
+ }
268
+ }
269
+ }
270
+ return ret;
271
+ }
272
+ structs() {
273
+ const ret = [];
274
+ for (const e of this.allFields.values()) {
275
+ if (e instanceof FieldInstanceResult) {
276
+ ret.push(e);
277
+ }
278
+ }
279
+ return ret;
280
+ }
281
+ // return a list of structs that match the criteria
282
+ // specified in the function.
283
+ selectStructs(result, fn) {
284
+ if (fn(this)) {
285
+ result.push(this);
286
+ }
287
+ for (const e of this.structs()) {
288
+ e.selectStructs(result, fn);
289
+ }
290
+ return result;
291
+ }
292
+ calculateDefaultOrderBy() {
293
+ // LookML rules for default ordering.
294
+ // Date or time or ordnal based fields, that field ascending
295
+ // First Measure Descending.
296
+ let firstField;
297
+ for (const [_name, fi] of this.allFields) {
298
+ if (fi instanceof FieldInstanceField) {
299
+ if (fi.fieldUsage.type === 'result') {
300
+ if (fi.f.fieldDef.type === 'turtle' ||
301
+ (0, malloy_types_1.isJoined)(fi.f.fieldDef) ||
302
+ (0, malloy_types_1.expressionIsAnalytic)(fi.f.fieldDef.expressionType)) {
303
+ continue;
304
+ }
305
+ firstField || (firstField = fi.fieldUsage.resultIndex);
306
+ if (['date', 'timestamp'].indexOf(fi.f.fieldDef.type) > -1) {
307
+ return [{ dir: 'desc', field: fi.fieldUsage.resultIndex }];
308
+ }
309
+ else if ((0, query_node_1.isBasicAggregate)(fi.f)) {
310
+ return [{ dir: 'desc', field: fi.fieldUsage.resultIndex }];
311
+ }
312
+ }
313
+ }
314
+ }
315
+ if (firstField) {
316
+ return [{ dir: 'asc', field: firstField }];
317
+ }
318
+ return [];
319
+ }
320
+ addStructToJoin(qs, uniqueKeyRequirement) {
321
+ var _a;
322
+ const name = qs.getIdentifier();
323
+ let join = this.root().joins.get(name);
324
+ if (join) {
325
+ join.uniqueKeyRequirement = (0, malloy_types_1.mergeUniqueKeyRequirement)(join.uniqueKeyRequirement, uniqueKeyRequirement);
326
+ return;
327
+ }
328
+ // if we have a parent, join it first.
329
+ let parent;
330
+ const parentStruct = (_a = qs.parent) === null || _a === void 0 ? void 0 : _a.getJoinableParent();
331
+ if (parentStruct) {
332
+ // add dependant expressions first...
333
+ this.addStructToJoin(parentStruct, undefined);
334
+ parent = this.root().joins.get(parentStruct.getIdentifier());
335
+ }
336
+ if (!(join = this.root().joins.get(name))) {
337
+ join = new join_instance_1.JoinInstance(qs, name, parent);
338
+ this.root().joins.set(name, join);
339
+ }
340
+ join.uniqueKeyRequirement = (0, malloy_types_1.mergeUniqueKeyRequirement)(join.uniqueKeyRequirement, uniqueKeyRequirement);
341
+ }
342
+ root() {
343
+ if (this.parent) {
344
+ return this.parent.root();
345
+ }
346
+ throw new Error('Internal Error, Null parent FieldInstanceResult');
347
+ }
348
+ getUngroupPartitions(ungroupSet) {
349
+ let ret = [];
350
+ let p = this;
351
+ let excludeFields = [];
352
+ let inScopeFieldNames = [];
353
+ // all defaults to all fields at the current level.
354
+ if (ungroupSet === undefined || ungroupSet.type === 'all') {
355
+ // fields specified an an all, convert it to an exclude set.
356
+ const allFields = (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || [];
357
+ // convert an All into the equivalent exclude
358
+ excludeFields = this.fields(fi => (0, query_node_1.isBasicScalar)(fi.f) &&
359
+ fi.fieldUsage.type === 'result' &&
360
+ allFields.indexOf(fi.f.getIdentifier()) === -1).map(fi => fi.f.getIdentifier());
361
+ }
362
+ else {
363
+ excludeFields = ungroupSet.fields;
364
+ }
365
+ let firstScope = true;
366
+ while (p !== undefined) {
367
+ // get a list of valid fieldnames for the current scope.
368
+ if (firstScope || (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type) === 'exclude') {
369
+ inScopeFieldNames = inScopeFieldNames.concat(p
370
+ .fields(fi => (0, query_node_1.isScalarField)(fi.f) && fi.fieldUsage.type === 'result')
371
+ .map(fi => fi.f.getIdentifier()));
372
+ }
373
+ ret = ret.concat(p.fields(fi => (0, query_node_1.isScalarField)(fi.f) &&
374
+ fi.fieldUsage.type === 'result' &&
375
+ excludeFields.indexOf(fi.f.getIdentifier()) === -1));
376
+ p = p.parent;
377
+ firstScope = false;
378
+ }
379
+ // verify that all names specified are available in the current scope.
380
+ for (const fieldName of (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || []) {
381
+ if (inScopeFieldNames.indexOf(fieldName) === -1) {
382
+ throw new Error(`${ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type}(): unknown field name "${fieldName}" or name not in scope.`);
383
+ }
384
+ }
385
+ return ret;
386
+ }
387
+ assignFieldsToGroups() {
388
+ for (const [_key, grouping] of this.ungroupedSets) {
389
+ for (const fieldInstance of this.getUngroupPartitions(grouping)) {
390
+ fieldInstance.additionalGroupSets.push(grouping.groupSet);
391
+ }
392
+ }
393
+ for (const child of this.structs()) {
394
+ child.assignFieldsToGroups();
395
+ }
396
+ }
397
+ }
398
+ exports.FieldInstanceResult = FieldInstanceResult;
399
+ /* Root Result as opposed to a turtled result */
400
+ class FieldInstanceResultRoot extends FieldInstanceResult {
401
+ constructor(turtleDef) {
402
+ super(turtleDef, undefined);
403
+ this.joins = new Map();
404
+ this.havings = new utils_1.AndChain();
405
+ this.isComplexQuery = false;
406
+ this.queryUsesPartitioning = false;
407
+ this.computeOnlyGroups = [];
408
+ this.elimatedComputeGroups = false;
409
+ }
410
+ root() {
411
+ return this;
412
+ }
413
+ // in the stage immediately following stage0 we need to elimiate any of the
414
+ // groups that were used in ungroup calculations. We need to do this only
415
+ // once and in the very next stage.
416
+ eliminateComputeGroupsSQL() {
417
+ if (this.elimatedComputeGroups || this.computeOnlyGroups.length === 0) {
418
+ return '';
419
+ }
420
+ else {
421
+ this.elimatedComputeGroups = true;
422
+ return `group_set NOT IN (${this.computeOnlyGroups.join(',')})`;
423
+ }
424
+ }
425
+ // look at all the fields again in the structs in the query
426
+ calculateSymmetricAggregates() {
427
+ var _a;
428
+ let leafiest;
429
+ for (const [name, join] of this.joins) {
430
+ // first join is by default the
431
+ const relationship = join.parentRelationship();
432
+ if (relationship === 'many_to_many' ||
433
+ join.forceAllSymmetricCalculations()) {
434
+ // everything must be calculated with symmetric aggregates
435
+ leafiest = '0never';
436
+ }
437
+ else if (leafiest === undefined) {
438
+ leafiest = name;
439
+ }
440
+ else if (join.parentRelationship() === 'one_to_many') {
441
+ // check up the parent relationship until you find
442
+ // the current leafiest node. If it isn't in the direct path
443
+ // we need symmetric aggregate for everything.
444
+ // if it is in the path, than this one becomes leafiest
445
+ const s = join.queryStruct;
446
+ if (s.parent && s.parent.getIdentifier() === leafiest) {
447
+ leafiest = name;
448
+ }
449
+ else {
450
+ // we have more than on one_to_many join chain, all bets are off.
451
+ leafiest = '0never';
452
+ }
453
+ }
454
+ }
455
+ // console.log(`LEAFIEST: ${leafiest}`);
456
+ for (const [name, join] of this.joins) {
457
+ join.leafiest = name === leafiest;
458
+ }
459
+ // figure out which joins we need to manufacture distinct keys for.
460
+ // Nested Unique keys are dependant on the primary key of the parent
461
+ // and the table.
462
+ for (const [_name, join] of this.joins) {
463
+ // in a one_to_many join we need a key to count there may be a failed
464
+ // match in a left join.
465
+ // users -> {
466
+ // group_by: user_id
467
+ // aggregate: order_count is orders.count()
468
+ if (
469
+ // we have a leafiest count() joined subtree
470
+ (join.leafiest &&
471
+ join.parent !== undefined &&
472
+ ((_a = join.uniqueKeyRequirement) === null || _a === void 0 ? void 0 : _a.isCount)) ||
473
+ // or not leafiest and we use an asymetric function
474
+ (!join.leafiest && join.uniqueKeyRequirement)) {
475
+ let j = join;
476
+ while (j) {
477
+ if (!j.queryStruct.primaryKey()) {
478
+ j.makeUniqueKey = true;
479
+ }
480
+ if (j.queryStruct.structDef.type === 'array') {
481
+ j = j.parent;
482
+ }
483
+ else {
484
+ j = undefined;
485
+ }
486
+ }
487
+ }
488
+ }
489
+ }
490
+ }
491
+ exports.FieldInstanceResultRoot = FieldInstanceResultRoot;
492
+ function sqlFullChildReference(struct, name, expand) {
493
+ let parentRef = struct.getSQLIdentifier();
494
+ if (expand && (0, malloy_types_1.isAtomic)(struct.structDef) && (0, malloy_types_1.hasExpression)(struct.structDef)) {
495
+ if (!struct.parent) {
496
+ throw new Error(`Cannot expand reference to ${name} without parent`);
497
+ }
498
+ if (!FieldInstanceField.exprCompiler) {
499
+ throw new Error('Expression compiler not registered with FieldInstanceField');
500
+ }
501
+ parentRef = FieldInstanceField.exprCompiler(expand.field, expand.result, struct.parent, struct.structDef.e);
502
+ }
503
+ let refType = 'table';
504
+ if (struct.structDef.type === 'record') {
505
+ refType = 'record';
506
+ }
507
+ else if (struct.structDef.type === 'array') {
508
+ refType =
509
+ struct.structDef.elementTypeDef.type === 'record_element'
510
+ ? 'array[record]'
511
+ : 'array[scalar]';
512
+ }
513
+ else if (struct.structDef.type === 'nest_source') {
514
+ refType = 'nest source';
515
+ }
516
+ const child = struct.getChildByName(name);
517
+ const childType = (child === null || child === void 0 ? void 0 : child.fieldDef.type) || 'unknown';
518
+ return struct.dialect.sqlFieldReference(parentRef, refType, name, childType);
519
+ }
520
+ //# sourceMappingURL=field_instance.js.map
@@ -1,3 +1,7 @@
1
1
  export * from './malloy_types';
2
- export { Segment, QueryModel } from './malloy_query';
2
+ import { QueryField, QueryStruct } from './query_node';
3
+ import { QueryQuery } from './query_query';
4
+ import { QueryModelImpl } from './query_model_impl';
5
+ export { QueryField, QueryStruct, QueryQuery, QueryModelImpl as QueryModel };
6
+ export { getResultStructDefForQuery, getResultStructDefForView, } from './query_model_impl';
3
7
  export { indent, composeSQLExpr } from './utils';
@@ -36,11 +36,32 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
36
36
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.composeSQLExpr = exports.indent = exports.QueryModel = exports.Segment = void 0;
39
+ exports.composeSQLExpr = exports.indent = exports.getResultStructDefForView = exports.getResultStructDefForQuery = exports.QueryModel = exports.QueryQuery = exports.QueryStruct = exports.QueryField = void 0;
40
40
  __exportStar(require("./malloy_types"), exports);
41
- var malloy_query_1 = require("./malloy_query");
42
- Object.defineProperty(exports, "Segment", { enumerable: true, get: function () { return malloy_query_1.Segment; } });
43
- Object.defineProperty(exports, "QueryModel", { enumerable: true, get: function () { return malloy_query_1.QueryModel; } });
41
+ const query_node_1 = require("./query_node");
42
+ Object.defineProperty(exports, "QueryField", { enumerable: true, get: function () { return query_node_1.QueryField; } });
43
+ Object.defineProperty(exports, "QueryStruct", { enumerable: true, get: function () { return query_node_1.QueryStruct; } });
44
+ const expression_compiler_1 = require("./expression_compiler");
45
+ const query_query_1 = require("./query_query");
46
+ Object.defineProperty(exports, "QueryQuery", { enumerable: true, get: function () { return query_query_1.QueryQuery; } });
47
+ const field_instance_1 = require("./field_instance");
48
+ const query_model_impl_1 = require("./query_model_impl");
49
+ Object.defineProperty(exports, "QueryModel", { enumerable: true, get: function () { return query_model_impl_1.QueryModelImpl; } });
50
+ // We have a circular dependency issue, and this is the minimal
51
+ // dependency injection needed to get around it. Ideally, we would
52
+ // like to avoid this, and I think a thoughtful pass through
53
+ // FieldInstance and QueryField might eliminate the need for this.
54
+ function getLookupFun(mri) {
55
+ if (mri instanceof query_model_impl_1.QueryModelImpl) {
56
+ return (name) => mri.structs.get(name);
57
+ }
58
+ return () => undefined;
59
+ }
60
+ field_instance_1.FieldInstanceField.registerExpressionCompiler(expression_compiler_1.exprToSQL);
61
+ query_node_1.QueryStruct.registerTurtleFieldMaker((field, parent) => query_query_1.QueryQuery.makeQuery(field, parent, undefined, false, getLookupFun(parent.getModel())));
62
+ var query_model_impl_2 = require("./query_model_impl");
63
+ Object.defineProperty(exports, "getResultStructDefForQuery", { enumerable: true, get: function () { return query_model_impl_2.getResultStructDefForQuery; } });
64
+ Object.defineProperty(exports, "getResultStructDefForView", { enumerable: true, get: function () { return query_model_impl_2.getResultStructDefForView; } });
44
65
  var utils_1 = require("./utils");
45
66
  Object.defineProperty(exports, "indent", { enumerable: true, get: function () { return utils_1.indent; } });
46
67
  Object.defineProperty(exports, "composeSQLExpr", { enumerable: true, get: function () { return utils_1.composeSQLExpr; } });