@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.
- package/dist/dialect/dialect.d.ts +1 -1
- package/dist/dialect/duckdb/duckdb.d.ts +1 -1
- package/dist/dialect/duckdb/duckdb.js +2 -6
- package/dist/dialect/mysql/mysql.d.ts +1 -1
- package/dist/dialect/mysql/mysql.js +2 -6
- package/dist/dialect/postgres/postgres.d.ts +1 -1
- package/dist/dialect/postgres/postgres.js +2 -6
- package/dist/dialect/snowflake/snowflake.d.ts +1 -1
- package/dist/dialect/snowflake/snowflake.js +2 -5
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +2 -6
- package/dist/dialect/trino/trino.d.ts +1 -1
- package/dist/dialect/trino/trino.js +2 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -3
- package/dist/lang/ast/expressions/expr-aggregate-function.js +12 -2
- package/dist/lang/ast/expressions/expr-count.js +3 -1
- package/dist/lang/ast/expressions/expr-func.js +34 -10
- package/dist/lang/ast/expressions/expr-props.js +1 -1
- package/dist/lang/ast/expressions/expr-ungroup.js +7 -3
- package/dist/lang/ast/expressions/function-ordering.d.ts +19 -5
- package/dist/lang/ast/expressions/function-ordering.js +61 -9
- package/dist/lang/ast/field-space/include-utils.js +1 -1
- package/dist/lang/ast/field-space/index-field-space.js +3 -1
- package/dist/lang/ast/field-space/query-spaces.js +20 -11
- package/dist/lang/ast/query-builders/index-builder.js +1 -1
- package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
- package/dist/lang/ast/query-elements/query-arrow.js +14 -4
- package/dist/lang/ast/query-elements/query-base.d.ts +1 -0
- package/dist/lang/ast/query-elements/query-base.js +14 -4
- package/dist/lang/ast/query-elements/query-refine.js +2 -0
- package/dist/lang/ast/query-properties/drill.js +1 -1
- package/dist/lang/ast/source-properties/join.js +6 -2
- package/dist/lang/ast/statements/define-source.js +1 -1
- package/dist/lang/ast/types/expr-value.js +1 -1
- package/dist/lang/ast/view-elements/reference-view.js +4 -1
- package/dist/lang/ast/view-elements/refine-utils.js +1 -1
- package/dist/{model/composite_source_utils.d.ts → lang/composite-source-utils.d.ts} +4 -17
- package/dist/{model/composite_source_utils.js → lang/composite-source-utils.js} +274 -44
- package/dist/lang/test/parse-expects.d.ts +1 -1
- package/dist/lang/test/parse-expects.js +6 -2
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/malloy.js +1 -1
- package/dist/model/expression_compiler.d.ts +27 -0
- package/dist/model/expression_compiler.js +780 -0
- package/dist/model/field_instance.d.ts +108 -0
- package/dist/model/field_instance.js +520 -0
- package/dist/model/index.d.ts +5 -1
- package/dist/model/index.js +25 -4
- package/dist/model/join_instance.d.ts +18 -0
- package/dist/model/join_instance.js +71 -0
- package/dist/model/malloy_types.d.ts +48 -2
- package/dist/model/malloy_types.js +39 -1
- package/dist/model/query_model.d.ts +2 -0
- package/dist/model/query_model.js +7 -0
- package/dist/model/query_model_contract.d.ts +32 -0
- package/dist/model/query_model_contract.js +7 -0
- package/dist/model/query_model_impl.d.ts +30 -0
- package/dist/model/query_model_impl.js +266 -0
- package/dist/model/query_node.d.ts +132 -0
- package/dist/model/query_node.js +638 -0
- package/dist/model/query_query.d.ts +86 -0
- package/dist/model/query_query.js +1724 -0
- package/dist/model/sql_block.js +2 -2
- package/dist/model/stage_writer.d.ts +25 -0
- package/dist/model/stage_writer.js +120 -0
- package/dist/model/utils.d.ts +18 -1
- package/dist/model/utils.js +66 -1
- package/dist/to_stable.js +3 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/model/malloy_query.d.ts +0 -391
- 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
|
package/dist/model/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './malloy_types';
|
|
2
|
-
|
|
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';
|
package/dist/model/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
42
|
-
Object.defineProperty(exports, "
|
|
43
|
-
Object.defineProperty(exports, "
|
|
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; } });
|