@malloydata/malloy 0.0.16-dev230104154319 → 0.0.16
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 -0
- package/dist/dialect/duckdb.d.ts +1 -0
- package/dist/dialect/duckdb.js +1 -0
- package/dist/dialect/postgres.d.ts +1 -0
- package/dist/dialect/postgres.js +1 -0
- package/dist/dialect/standardsql.d.ts +1 -0
- package/dist/dialect/standardsql.js +5 -1
- package/dist/lang/ast/apply-expr.js +6 -6
- package/dist/lang/ast/ast-expr.d.ts +7 -3
- package/dist/lang/ast/ast-expr.js +41 -38
- package/dist/lang/ast/ast-main.js +8 -9
- package/dist/lang/ast/ast-time-expr.js +15 -17
- package/dist/lang/ast/ast-types.d.ts +2 -2
- package/dist/lang/ast/ast-types.js +5 -5
- package/dist/lang/field-space.js +1 -1
- package/dist/lang/space-field.d.ts +1 -1
- package/dist/lang/space-field.js +4 -4
- package/dist/lang/test/parse.spec.js +2 -2
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/malloy.d.ts +1 -1
- package/dist/malloy.js +3 -2
- package/dist/model/malloy_query.d.ts +4 -3
- package/dist/model/malloy_query.js +91 -30
- package/dist/model/malloy_types.d.ts +21 -3
- package/dist/model/malloy_types.js +76 -1
- package/package.json +1 -1
|
@@ -28,6 +28,7 @@ export declare abstract class Dialect {
|
|
|
28
28
|
abstract supportUnnestArrayAgg: boolean;
|
|
29
29
|
abstract supportsCTEinCoorelatedSubQueries: boolean;
|
|
30
30
|
abstract dontUnionIndex: boolean;
|
|
31
|
+
abstract supportsQualify: boolean;
|
|
31
32
|
abstract quoteTablePath(tablePath: string): string;
|
|
32
33
|
abstract sqlGroupSetTable(groupSetCount: number): string;
|
|
33
34
|
abstract sqlAnyValue(groupSet: number, fieldName: string): string;
|
package/dist/dialect/duckdb.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export declare class DuckDBDialect extends Dialect {
|
|
|
14
14
|
supportUnnestArrayAgg: boolean;
|
|
15
15
|
supportsCTEinCoorelatedSubQueries: boolean;
|
|
16
16
|
dontUnionIndex: boolean;
|
|
17
|
+
supportsQualify: boolean;
|
|
17
18
|
functionInfo: Record<string, FunctionInfo>;
|
|
18
19
|
get udfPrefix(): string;
|
|
19
20
|
quoteTablePath(tableName: string): string;
|
package/dist/dialect/duckdb.js
CHANGED
|
@@ -124,6 +124,7 @@ class DuckDBDialect extends dialect_1.Dialect {
|
|
|
124
124
|
this.supportUnnestArrayAgg = true;
|
|
125
125
|
this.supportsCTEinCoorelatedSubQueries = true;
|
|
126
126
|
this.dontUnionIndex = false;
|
|
127
|
+
this.supportsQualify = true;
|
|
127
128
|
this.functionInfo = {
|
|
128
129
|
concat: { returnType: "string" },
|
|
129
130
|
};
|
|
@@ -15,6 +15,7 @@ export declare class PostgresDialect extends Dialect {
|
|
|
15
15
|
supportUnnestArrayAgg: boolean;
|
|
16
16
|
supportsCTEinCoorelatedSubQueries: boolean;
|
|
17
17
|
dontUnionIndex: boolean;
|
|
18
|
+
supportsQualify: boolean;
|
|
18
19
|
functionInfo: Record<string, FunctionInfo>;
|
|
19
20
|
quoteTablePath(tablePath: string): string;
|
|
20
21
|
sqlGroupSetTable(groupSetCount: number): string;
|
package/dist/dialect/postgres.js
CHANGED
|
@@ -53,6 +53,7 @@ class PostgresDialect extends dialect_1.Dialect {
|
|
|
53
53
|
this.supportUnnestArrayAgg = true;
|
|
54
54
|
this.supportsCTEinCoorelatedSubQueries = true;
|
|
55
55
|
this.dontUnionIndex = false;
|
|
56
|
+
this.supportsQualify = false;
|
|
56
57
|
this.functionInfo = {
|
|
57
58
|
concat: { returnType: "string" },
|
|
58
59
|
};
|
|
@@ -15,6 +15,7 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
15
15
|
supportUnnestArrayAgg: boolean;
|
|
16
16
|
supportsCTEinCoorelatedSubQueries: boolean;
|
|
17
17
|
dontUnionIndex: boolean;
|
|
18
|
+
supportsQualify: boolean;
|
|
18
19
|
functionInfo: Record<string, FunctionInfo>;
|
|
19
20
|
quoteTablePath(tablePath: string): string;
|
|
20
21
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
@@ -47,8 +47,12 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
47
47
|
this.supportUnnestArrayAgg = false;
|
|
48
48
|
this.supportsCTEinCoorelatedSubQueries = false;
|
|
49
49
|
this.dontUnionIndex = true; // bigquery can't use a sample table more than once in a query.
|
|
50
|
+
this.supportsQualify = true;
|
|
51
|
+
// I think we want an optional list of parameters types that we force a cast to.
|
|
50
52
|
this.functionInfo = {
|
|
51
|
-
timestamp_seconds: {
|
|
53
|
+
timestamp_seconds: {
|
|
54
|
+
returnType: "timestamp",
|
|
55
|
+
},
|
|
52
56
|
concat: { returnType: "string" },
|
|
53
57
|
};
|
|
54
58
|
this.keywords = `
|
|
@@ -57,7 +57,7 @@ function applyBinary(fs, left, op, right) {
|
|
|
57
57
|
};
|
|
58
58
|
return {
|
|
59
59
|
dataType: "number",
|
|
60
|
-
|
|
60
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(num.expressionType, denom.expressionType),
|
|
61
61
|
value: [div],
|
|
62
62
|
};
|
|
63
63
|
}
|
|
@@ -178,29 +178,29 @@ function equality(fs, left, op, right) {
|
|
|
178
178
|
}
|
|
179
179
|
return {
|
|
180
180
|
dataType: "boolean",
|
|
181
|
-
|
|
181
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType),
|
|
182
182
|
value,
|
|
183
183
|
};
|
|
184
184
|
}
|
|
185
185
|
function compare(fs, left, op, right) {
|
|
186
186
|
const lhs = left.getExpression(fs);
|
|
187
187
|
const rhs = right.getExpression(fs);
|
|
188
|
-
const
|
|
188
|
+
const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
|
|
189
189
|
const value = timeCompare(lhs, op, rhs) || (0, index_1.compose)(lhs.value, op, rhs.value);
|
|
190
190
|
return {
|
|
191
191
|
dataType: "boolean",
|
|
192
|
-
|
|
192
|
+
expressionType,
|
|
193
193
|
value: value,
|
|
194
194
|
};
|
|
195
195
|
}
|
|
196
196
|
function numeric(fs, left, op, right) {
|
|
197
197
|
const lhs = left.getExpression(fs);
|
|
198
198
|
const rhs = right.getExpression(fs);
|
|
199
|
-
const
|
|
199
|
+
const expressionType = (0, malloy_types_1.maxExpressionType)(lhs.expressionType, rhs.expressionType);
|
|
200
200
|
if (allAre("number", lhs, rhs)) {
|
|
201
201
|
return {
|
|
202
202
|
dataType: "number",
|
|
203
|
-
|
|
203
|
+
expressionType,
|
|
204
204
|
value: (0, index_1.compose)(lhs.value, op, rhs.value),
|
|
205
205
|
};
|
|
206
206
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { By, AtomicFieldType, FieldTypeDef, Fragment, TimeFieldType } from "../../model/malloy_types";
|
|
1
|
+
import { By, AtomicFieldType, FieldTypeDef, Fragment, TimeFieldType, ExpressionType } from "../../model/malloy_types";
|
|
2
2
|
import { FieldSpace } from "../field-space";
|
|
3
3
|
import { Filter, MalloyElement, ExprValue, FieldValueType, FragType } from "./index";
|
|
4
4
|
import { FieldName, FieldReference } from "./ast-main";
|
|
@@ -92,7 +92,7 @@ export declare class ExprRegEx extends ExpressionDef {
|
|
|
92
92
|
export declare class ExprTime extends ExpressionDef {
|
|
93
93
|
elementType: string;
|
|
94
94
|
readonly translationValue: ExprValue;
|
|
95
|
-
constructor(timeType: TimeFieldType, value: Fragment[] | string,
|
|
95
|
+
constructor(timeType: TimeFieldType, value: Fragment[] | string, expressionType?: ExpressionType);
|
|
96
96
|
getExpression(_fs: FieldSpace): ExprValue;
|
|
97
97
|
static fromValue(timeType: TimeFieldType, expr: ExprValue): ExprTime;
|
|
98
98
|
}
|
|
@@ -123,7 +123,11 @@ export declare abstract class BinaryBoolean<opType extends string> extends Expre
|
|
|
123
123
|
}
|
|
124
124
|
export declare class ExprLogicalOp extends BinaryBoolean<"and" | "or"> {
|
|
125
125
|
elementType: string;
|
|
126
|
-
legalChildTypes: FragType
|
|
126
|
+
legalChildTypes: (FragType | {
|
|
127
|
+
aggregate: boolean;
|
|
128
|
+
dataType: FieldValueType;
|
|
129
|
+
expressionType: ExpressionType;
|
|
130
|
+
})[];
|
|
127
131
|
}
|
|
128
132
|
export declare class ExprIdReference extends ExpressionDef {
|
|
129
133
|
readonly fieldReference: FieldReference;
|
|
@@ -87,7 +87,7 @@ class DollarReference extends ExpressionDef {
|
|
|
87
87
|
return {
|
|
88
88
|
dataType: this.refType,
|
|
89
89
|
value: [{ type: "applyVal" }],
|
|
90
|
-
|
|
90
|
+
expressionType: "scalar",
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -188,8 +188,8 @@ class FieldDeclaration extends index_1.MalloyElement {
|
|
|
188
188
|
if (compressValue.length > 0) {
|
|
189
189
|
template.e = compressValue;
|
|
190
190
|
}
|
|
191
|
-
if (exprValue.
|
|
192
|
-
template.
|
|
191
|
+
if (exprValue.expressionType) {
|
|
192
|
+
template.expressionType = exprValue.expressionType;
|
|
193
193
|
}
|
|
194
194
|
if (this.exprSrc) {
|
|
195
195
|
template.code = this.exprSrc;
|
|
@@ -265,20 +265,20 @@ class ExprRegEx extends ExpressionDef {
|
|
|
265
265
|
getExpression() {
|
|
266
266
|
return {
|
|
267
267
|
dataType: "regular expression",
|
|
268
|
-
|
|
268
|
+
expressionType: "scalar",
|
|
269
269
|
value: [`r'${this.regex}'`],
|
|
270
270
|
};
|
|
271
271
|
}
|
|
272
272
|
}
|
|
273
273
|
exports.ExprRegEx = ExprRegEx;
|
|
274
274
|
class ExprTime extends ExpressionDef {
|
|
275
|
-
constructor(timeType, value,
|
|
275
|
+
constructor(timeType, value, expressionType = "scalar") {
|
|
276
276
|
super();
|
|
277
277
|
this.elementType = "timestampOrDate";
|
|
278
278
|
this.elementType = timeType;
|
|
279
279
|
this.translationValue = {
|
|
280
280
|
dataType: timeType,
|
|
281
|
-
|
|
281
|
+
expressionType,
|
|
282
282
|
value: typeof value === "string" ? [value] : value,
|
|
283
283
|
};
|
|
284
284
|
}
|
|
@@ -300,7 +300,7 @@ class ExprTime extends ExpressionDef {
|
|
|
300
300
|
}
|
|
301
301
|
value = (0, index_1.compressExpr)([toTs]);
|
|
302
302
|
}
|
|
303
|
-
return new ExprTime(timeType, value, expr.
|
|
303
|
+
return new ExprTime(timeType, value, expr.expressionType);
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
exports.ExprTime = ExprTime;
|
|
@@ -355,7 +355,7 @@ class BinaryBoolean extends ExpressionDef {
|
|
|
355
355
|
if (this.typeCheck(this.left, left) && this.typeCheck(this.right, right)) {
|
|
356
356
|
return {
|
|
357
357
|
dataType: "boolean",
|
|
358
|
-
|
|
358
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(left.expressionType, right.expressionType),
|
|
359
359
|
value: (0, index_1.compose)(left.value, this.op, right.value),
|
|
360
360
|
};
|
|
361
361
|
}
|
|
@@ -387,9 +387,9 @@ class ExprIdReference extends ExpressionDef {
|
|
|
387
387
|
// TODO if type is a query or a struct this should fail nicely
|
|
388
388
|
const typeMixin = def.found.type();
|
|
389
389
|
const dataType = typeMixin.type;
|
|
390
|
-
const
|
|
390
|
+
const expressionType = typeMixin.expressionType || "scalar";
|
|
391
391
|
const value = [{ type: def.found.refType, path: this.refString }];
|
|
392
|
-
return { dataType,
|
|
392
|
+
return { dataType, expressionType, value };
|
|
393
393
|
}
|
|
394
394
|
this.log(def.error);
|
|
395
395
|
return (0, index_1.errorFor)(def.error);
|
|
@@ -402,7 +402,7 @@ class ExprIdReference extends ExpressionDef {
|
|
|
402
402
|
const lval = expr.getExpression(fs);
|
|
403
403
|
return {
|
|
404
404
|
dataType: "boolean",
|
|
405
|
-
|
|
405
|
+
expressionType: lval.expressionType,
|
|
406
406
|
value: [
|
|
407
407
|
{
|
|
408
408
|
type: "apply",
|
|
@@ -426,7 +426,7 @@ class ExprNULL extends ExpressionDef {
|
|
|
426
426
|
return {
|
|
427
427
|
dataType: "null",
|
|
428
428
|
value: ["NULL"],
|
|
429
|
-
|
|
429
|
+
expressionType: "scalar",
|
|
430
430
|
};
|
|
431
431
|
}
|
|
432
432
|
}
|
|
@@ -504,7 +504,7 @@ class ExprAlternationTree extends BinaryBoolean {
|
|
|
504
504
|
const choice2 = this.right.apply(fs, applyOp, expr);
|
|
505
505
|
return {
|
|
506
506
|
dataType: "boolean",
|
|
507
|
-
|
|
507
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(choice1.expressionType, choice2.expressionType),
|
|
508
508
|
value: (0, index_1.compose)(choice1.value, this.op === "&" ? "and" : "or", choice2.value),
|
|
509
509
|
};
|
|
510
510
|
}
|
|
@@ -542,7 +542,7 @@ class ExprAggregateFunction extends ExpressionDef {
|
|
|
542
542
|
if ((0, malloy_types_1.isAtomicFieldType)(footType.type)) {
|
|
543
543
|
exprVal = {
|
|
544
544
|
dataType: footType.type,
|
|
545
|
-
|
|
545
|
+
expressionType: footType.expressionType || "scalar",
|
|
546
546
|
value: [{ type: "field", path: this.source.refString }],
|
|
547
547
|
};
|
|
548
548
|
structPath = this.source.sourceString;
|
|
@@ -563,7 +563,10 @@ class ExprAggregateFunction extends ExpressionDef {
|
|
|
563
563
|
this.log("Missing expression for aggregate function");
|
|
564
564
|
return (0, index_1.errorFor)("agggregate without expression");
|
|
565
565
|
}
|
|
566
|
-
if (this.typeCheck(this.expr || this, {
|
|
566
|
+
if (this.typeCheck(this.expr || this, {
|
|
567
|
+
...exprVal,
|
|
568
|
+
expressionType: "scalar",
|
|
569
|
+
})) {
|
|
567
570
|
const f = {
|
|
568
571
|
type: "aggregate",
|
|
569
572
|
function: this.func,
|
|
@@ -574,7 +577,7 @@ class ExprAggregateFunction extends ExpressionDef {
|
|
|
574
577
|
}
|
|
575
578
|
return {
|
|
576
579
|
dataType: this.returns(exprVal),
|
|
577
|
-
|
|
580
|
+
expressionType: "aggregate",
|
|
578
581
|
value: [f],
|
|
579
582
|
};
|
|
580
583
|
}
|
|
@@ -653,7 +656,7 @@ class ExprCount extends ExprAggregateFunction {
|
|
|
653
656
|
}
|
|
654
657
|
return {
|
|
655
658
|
dataType: "number",
|
|
656
|
-
|
|
659
|
+
expressionType: "aggregate",
|
|
657
660
|
value: [ret],
|
|
658
661
|
};
|
|
659
662
|
}
|
|
@@ -687,12 +690,12 @@ class ExprUngroup extends ExpressionDef {
|
|
|
687
690
|
}
|
|
688
691
|
getExpression(fs) {
|
|
689
692
|
const exprVal = this.expr.getExpression(fs);
|
|
690
|
-
if (!exprVal.
|
|
693
|
+
if (!(0, malloy_types_1.expressionIsAggregate)(exprVal.expressionType)) {
|
|
691
694
|
this.expr.log(`${this.control}() expression must be an aggregate`);
|
|
692
695
|
return (0, index_1.errorFor)("ungrouped scalar");
|
|
693
696
|
}
|
|
694
697
|
const ungroup = { type: this.control, e: exprVal.value };
|
|
695
|
-
if (this.typeCheck(this.expr, { ...exprVal,
|
|
698
|
+
if (this.typeCheck(this.expr, { ...exprVal, expressionType: "scalar" })) {
|
|
696
699
|
if (this.fields.length > 0) {
|
|
697
700
|
let qs = fs;
|
|
698
701
|
if (fs instanceof field_space_1.DefSpace) {
|
|
@@ -715,7 +718,7 @@ class ExprUngroup extends ExpressionDef {
|
|
|
715
718
|
}
|
|
716
719
|
return {
|
|
717
720
|
dataType: this.returns(exprVal),
|
|
718
|
-
|
|
721
|
+
expressionType: "analytic",
|
|
719
722
|
value: [ungroup],
|
|
720
723
|
};
|
|
721
724
|
}
|
|
@@ -746,12 +749,12 @@ class ExprCase extends ExpressionDef {
|
|
|
746
749
|
}
|
|
747
750
|
getExpression(fs) {
|
|
748
751
|
let retType;
|
|
749
|
-
let
|
|
752
|
+
let expressionType = "scalar";
|
|
750
753
|
const caseExpr = ["CASE "];
|
|
751
754
|
for (const clause of this.when) {
|
|
752
755
|
const whenExpr = clause.whenThis.getExpression(fs);
|
|
753
756
|
const thenExpr = clause.thenThis.getExpression(fs);
|
|
754
|
-
|
|
757
|
+
expressionType = (0, malloy_types_1.maxExpressionType)(expressionType, (0, malloy_types_1.maxExpressionType)(whenExpr.expressionType, thenExpr.expressionType));
|
|
755
758
|
if (thenExpr.dataType !== "null") {
|
|
756
759
|
if (retType && !index_1.FT.typeEq(retType, thenExpr)) {
|
|
757
760
|
this.log(`Mismatched THEN clause types, ${index_1.FT.inspect(retType, thenExpr)}`);
|
|
@@ -765,7 +768,7 @@ class ExprCase extends ExpressionDef {
|
|
|
765
768
|
}
|
|
766
769
|
if (this.elseClause) {
|
|
767
770
|
const elseExpr = this.elseClause.getExpression(fs);
|
|
768
|
-
|
|
771
|
+
expressionType = (0, malloy_types_1.maxExpressionType)(expressionType, elseExpr.expressionType);
|
|
769
772
|
caseExpr.push(" ELSE ", ...elseExpr.value);
|
|
770
773
|
if (elseExpr.dataType !== "null") {
|
|
771
774
|
if (retType && !index_1.FT.typeEq(retType, elseExpr)) {
|
|
@@ -784,7 +787,7 @@ class ExprCase extends ExpressionDef {
|
|
|
784
787
|
caseExpr.push(" END");
|
|
785
788
|
return {
|
|
786
789
|
dataType: retType.dataType,
|
|
787
|
-
|
|
790
|
+
expressionType,
|
|
788
791
|
value: caseExpr,
|
|
789
792
|
};
|
|
790
793
|
}
|
|
@@ -802,17 +805,17 @@ class ExprFilter extends ExpressionDef {
|
|
|
802
805
|
const testList = this.filter.getFilterList(fs);
|
|
803
806
|
const resultExpr = this.expr.getExpression(fs);
|
|
804
807
|
for (const cond of testList) {
|
|
805
|
-
if (cond.
|
|
806
|
-
this.filter.log("Cannot filter a field with an aggregate computation");
|
|
808
|
+
if ((0, malloy_types_1.expressionIsCalculation)(cond.expressionType)) {
|
|
809
|
+
this.filter.log("Cannot filter a field with an aggregate or analytical computation");
|
|
807
810
|
return (0, index_1.errorFor)("no filter on aggregate");
|
|
808
811
|
}
|
|
809
812
|
}
|
|
810
|
-
if (
|
|
813
|
+
if (resultExpr.expressionType === "scalar") {
|
|
811
814
|
// TODO could log a warning, but I have a problem with the
|
|
812
815
|
// idea of warnings, so for now ...
|
|
813
816
|
return resultExpr;
|
|
814
817
|
}
|
|
815
|
-
if (this.typeCheck(this.expr, { ...resultExpr,
|
|
818
|
+
if (this.typeCheck(this.expr, { ...resultExpr, expressionType: "scalar" })) {
|
|
816
819
|
return {
|
|
817
820
|
...resultExpr,
|
|
818
821
|
value: [
|
|
@@ -845,7 +848,7 @@ class ExprCast extends ExpressionDef {
|
|
|
845
848
|
const expr = this.expr.getExpression(fs);
|
|
846
849
|
return {
|
|
847
850
|
dataType: this.castType,
|
|
848
|
-
|
|
851
|
+
expressionType: expr.expressionType,
|
|
849
852
|
value: (0, index_1.compressExpr)((0, time_utils_1.castTo)(this.castType, expr.value, this.safe)),
|
|
850
853
|
};
|
|
851
854
|
}
|
|
@@ -863,7 +866,7 @@ class TopBy extends index_1.MalloyElement {
|
|
|
863
866
|
getBy(fs) {
|
|
864
867
|
if (this.by instanceof ExpressionDef) {
|
|
865
868
|
const byExpr = this.by.getExpression(fs);
|
|
866
|
-
if (!byExpr.
|
|
869
|
+
if (!(0, malloy_types_1.expressionIsAggregate)(byExpr.expressionType)) {
|
|
867
870
|
this.log("top by expression must be an aggregate");
|
|
868
871
|
}
|
|
869
872
|
return { by: "expression", e: (0, index_1.compressExpr)(byExpr.value) };
|
|
@@ -890,7 +893,7 @@ class Range extends ExpressionDef {
|
|
|
890
893
|
const toValue = this.last.apply(fs, op3, expr);
|
|
891
894
|
return {
|
|
892
895
|
dataType: "boolean",
|
|
893
|
-
|
|
896
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(fromValue.expressionType, toValue.expressionType),
|
|
894
897
|
value: (0, index_1.compose)(fromValue.value, op2, toValue.value),
|
|
895
898
|
};
|
|
896
899
|
}
|
|
@@ -961,13 +964,13 @@ class Pick extends ExpressionDef {
|
|
|
961
964
|
apply(fs, op, expr) {
|
|
962
965
|
const caseValue = ["CASE"];
|
|
963
966
|
let returnType;
|
|
964
|
-
let
|
|
967
|
+
let anyExpressionType = "scalar";
|
|
965
968
|
for (const choice of this.choices) {
|
|
966
969
|
const whenExpr = choice.when.apply(fs, "=", expr);
|
|
967
970
|
const thenExpr = choice.pick
|
|
968
971
|
? choice.pick.getExpression(fs)
|
|
969
972
|
: expr.getExpression(fs);
|
|
970
|
-
|
|
973
|
+
anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, (0, malloy_types_1.maxExpressionType)(whenExpr.expressionType, thenExpr.expressionType));
|
|
971
974
|
if (returnType) {
|
|
972
975
|
if (!index_1.FT.typeEq(returnType, thenExpr, true)) {
|
|
973
976
|
const whenType = index_1.FT.inspect(thenExpr);
|
|
@@ -990,7 +993,7 @@ class Pick extends ExpressionDef {
|
|
|
990
993
|
}
|
|
991
994
|
return {
|
|
992
995
|
dataType: returnType.dataType,
|
|
993
|
-
|
|
996
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(anyExpressionType, elseVal.expressionType),
|
|
994
997
|
value: (0, index_1.compressExpr)([...caseValue, " ELSE ", ...elseVal.value, " END"]),
|
|
995
998
|
};
|
|
996
999
|
}
|
|
@@ -1017,7 +1020,7 @@ class Pick extends ExpressionDef {
|
|
|
1017
1020
|
}
|
|
1018
1021
|
const returnType = choiceValues[0].pick;
|
|
1019
1022
|
const caseValue = ["CASE"];
|
|
1020
|
-
let
|
|
1023
|
+
let anyExpressionType = returnType.expressionType;
|
|
1021
1024
|
for (const aChoice of choiceValues) {
|
|
1022
1025
|
if (!index_1.FT.typeEq(aChoice.when, index_1.FT.boolT)) {
|
|
1023
1026
|
this.log(`when expression must be boolean, not '${index_1.FT.inspect(aChoice.when)}`);
|
|
@@ -1028,11 +1031,11 @@ class Pick extends ExpressionDef {
|
|
|
1028
1031
|
this.log(`pick type '${whenType}', expected '${returnType.dataType}'`);
|
|
1029
1032
|
return (0, index_1.errorFor)("pick value type");
|
|
1030
1033
|
}
|
|
1031
|
-
|
|
1034
|
+
anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, (0, malloy_types_1.maxExpressionType)(aChoice.pick.expressionType, aChoice.when.expressionType));
|
|
1032
1035
|
caseValue.push(" WHEN ", ...aChoice.when.value, " THEN ", ...aChoice.pick.value);
|
|
1033
1036
|
}
|
|
1034
1037
|
const defVal = this.elsePick.getExpression(fs);
|
|
1035
|
-
|
|
1038
|
+
anyExpressionType = (0, malloy_types_1.maxExpressionType)(anyExpressionType, defVal.expressionType);
|
|
1036
1039
|
if (!index_1.FT.typeEq(returnType, defVal, true)) {
|
|
1037
1040
|
this.elsePick.log(`else type '${index_1.FT.inspect(defVal)}', expected '${returnType.dataType}'`);
|
|
1038
1041
|
return (0, index_1.errorFor)("pick value type mismatch");
|
|
@@ -1040,7 +1043,7 @@ class Pick extends ExpressionDef {
|
|
|
1040
1043
|
caseValue.push(" ELSE ", ...defVal.value, " END");
|
|
1041
1044
|
return {
|
|
1042
1045
|
dataType: returnType.dataType,
|
|
1043
|
-
|
|
1046
|
+
expressionType: anyExpressionType,
|
|
1044
1047
|
value: (0, index_1.compressExpr)(caseValue),
|
|
1045
1048
|
};
|
|
1046
1049
|
}
|
|
@@ -546,7 +546,7 @@ class RefinedExplore extends Mallobj {
|
|
|
546
546
|
for (const filter of filters) {
|
|
547
547
|
for (const el of filter.list) {
|
|
548
548
|
const fc = el.filterExpression(fs);
|
|
549
|
-
if (fc.
|
|
549
|
+
if (model.expressionIsCalculation(fc.expressionType)) {
|
|
550
550
|
el.log("Can't use aggregate computations in top level filters");
|
|
551
551
|
}
|
|
552
552
|
else {
|
|
@@ -925,15 +925,14 @@ class FilterElement extends MalloyElement {
|
|
|
925
925
|
return {
|
|
926
926
|
code: this.exprSrc,
|
|
927
927
|
expression: ["_FILTER_MUST_RETURN_BOOLEAN_"],
|
|
928
|
+
expressionType: "scalar",
|
|
928
929
|
};
|
|
929
930
|
}
|
|
930
931
|
const exprCond = {
|
|
931
932
|
code: this.exprSrc,
|
|
932
933
|
expression: (0, index_1.compressExpr)(exprVal.value),
|
|
934
|
+
expressionType: exprVal.expressionType,
|
|
933
935
|
};
|
|
934
|
-
if (exprVal.aggregate) {
|
|
935
|
-
exprCond.aggregate = true;
|
|
936
|
-
}
|
|
937
936
|
return exprCond;
|
|
938
937
|
}
|
|
939
938
|
}
|
|
@@ -954,14 +953,14 @@ class Filter extends ListOf {
|
|
|
954
953
|
// here allows better reflection of errors back to user.
|
|
955
954
|
if (this.havingClause !== undefined) {
|
|
956
955
|
if (this.havingClause) {
|
|
957
|
-
if (
|
|
958
|
-
oneElement.log("Aggregate expression expected in HAVING filter");
|
|
956
|
+
if (model.expressionIsCalculation(fExpr.expressionType)) {
|
|
957
|
+
oneElement.log("Aggregate or Analytical expression expected in HAVING filter");
|
|
959
958
|
continue;
|
|
960
959
|
}
|
|
961
960
|
}
|
|
962
961
|
else {
|
|
963
|
-
if (fExpr.
|
|
964
|
-
oneElement.log("Aggregate
|
|
962
|
+
if (fExpr.expressionType !== "scalar") {
|
|
963
|
+
oneElement.log("Aggregate or Analytical expressions not allowed in WHERE");
|
|
965
964
|
continue;
|
|
966
965
|
}
|
|
967
966
|
}
|
|
@@ -1921,7 +1920,7 @@ class Top extends MalloyElement {
|
|
|
1921
1920
|
}
|
|
1922
1921
|
else {
|
|
1923
1922
|
const byExpr = this.by.getExpression(fs);
|
|
1924
|
-
if (
|
|
1923
|
+
if (model.expressionIsAggregate(byExpr.expressionType)) {
|
|
1925
1924
|
this.log("top by expression must be an aggregate");
|
|
1926
1925
|
}
|
|
1927
1926
|
return { by: "expression", e: (0, index_1.compressExpr)(byExpr.value) };
|
|
@@ -98,15 +98,15 @@ class ExprGranularTime extends index_1.ExpressionDef {
|
|
|
98
98
|
timestampRange(fs, op, expr) {
|
|
99
99
|
const begin = this.getExpression(fs);
|
|
100
100
|
const beginTime = index_1.ExprTime.fromValue("timestamp", begin);
|
|
101
|
-
const endTime = new index_1.ExprTime("timestamp", (0, index_1.timeOffset)("timestamp", begin.value, "+", (0, malloy_types_1.mkExpr) `1`, this.units), begin.
|
|
101
|
+
const endTime = new index_1.ExprTime("timestamp", (0, index_1.timeOffset)("timestamp", begin.value, "+", (0, malloy_types_1.mkExpr) `1`, this.units), begin.expressionType);
|
|
102
102
|
const range = new index_1.Range(beginTime, endTime);
|
|
103
103
|
return range.apply(fs, op, expr);
|
|
104
104
|
}
|
|
105
105
|
dateRange(fs, op, expr) {
|
|
106
106
|
const begin = this.getExpression(fs);
|
|
107
|
-
const beginTime = new index_1.ExprTime("date", begin.value, begin.
|
|
107
|
+
const beginTime = new index_1.ExprTime("date", begin.value, begin.expressionType);
|
|
108
108
|
const endAt = (0, index_1.timeOffset)("date", begin.value, "+", ["1"], this.units);
|
|
109
|
-
const end = new index_1.ExprTime("date", endAt, begin.
|
|
109
|
+
const end = new index_1.ExprTime("date", endAt, begin.expressionType);
|
|
110
110
|
const range = new index_1.Range(beginTime, end);
|
|
111
111
|
return range.apply(fs, op, expr);
|
|
112
112
|
}
|
|
@@ -206,7 +206,7 @@ class GranularLiteral extends index_1.ExpressionDef {
|
|
|
206
206
|
const dataType = this.timeType || "date";
|
|
207
207
|
const value = {
|
|
208
208
|
dataType,
|
|
209
|
-
|
|
209
|
+
expressionType: "scalar",
|
|
210
210
|
value: (0, index_1.timeLiteral)(this.moment, dataType, "UTC"),
|
|
211
211
|
};
|
|
212
212
|
// Literals with date resolution can be used as timestamps or dates,
|
|
@@ -236,7 +236,7 @@ class ExprNow extends index_1.ExpressionDef {
|
|
|
236
236
|
getExpression(_fs) {
|
|
237
237
|
return {
|
|
238
238
|
dataType: "timestamp",
|
|
239
|
-
|
|
239
|
+
expressionType: "scalar",
|
|
240
240
|
value: [
|
|
241
241
|
{
|
|
242
242
|
type: "dialect",
|
|
@@ -275,13 +275,13 @@ class ExprDuration extends index_1.ExpressionDef {
|
|
|
275
275
|
const result = (0, index_1.timeOffset)("timestamp", lhs.value, op, num.value, this.timeframe);
|
|
276
276
|
return (0, index_1.timeResult)({
|
|
277
277
|
dataType: "timestamp",
|
|
278
|
-
|
|
278
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, num.expressionType),
|
|
279
279
|
value: result,
|
|
280
280
|
}, resultGranularity);
|
|
281
281
|
}
|
|
282
282
|
return (0, index_1.timeResult)({
|
|
283
283
|
dataType: "date",
|
|
284
|
-
|
|
284
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(lhs.expressionType, num.expressionType),
|
|
285
285
|
value: (0, index_1.timeOffset)("date", lhs.value, op, num.value, this.timeframe),
|
|
286
286
|
}, resultGranularity);
|
|
287
287
|
}
|
|
@@ -290,7 +290,7 @@ class ExprDuration extends index_1.ExpressionDef {
|
|
|
290
290
|
getExpression(_fs) {
|
|
291
291
|
return {
|
|
292
292
|
dataType: "duration",
|
|
293
|
-
|
|
293
|
+
expressionType: "scalar",
|
|
294
294
|
value: ["__ERROR_DURATION_IS_NOT_A_VALUE__"],
|
|
295
295
|
};
|
|
296
296
|
}
|
|
@@ -408,10 +408,10 @@ class ForRange extends index_1.ExpressionDef {
|
|
|
408
408
|
// ... not a literal, need a cast
|
|
409
409
|
from = (0, index_1.castDateToTimestamp)(from);
|
|
410
410
|
}
|
|
411
|
-
rangeStart = new index_1.ExprTime("timestamp", from, startV.
|
|
411
|
+
rangeStart = new index_1.ExprTime("timestamp", from, startV.expressionType);
|
|
412
412
|
}
|
|
413
413
|
const to = (0, index_1.timeOffset)("timestamp", from, "+", nV.value, units);
|
|
414
|
-
const rangeEnd = new index_1.ExprTime("timestamp", to, startV.
|
|
414
|
+
const rangeEnd = new index_1.ExprTime("timestamp", to, startV.expressionType);
|
|
415
415
|
return new index_1.Range(rangeStart, rangeEnd).apply(fs, op, applyTo);
|
|
416
416
|
}
|
|
417
417
|
requestExpression(_fs) {
|
|
@@ -464,7 +464,7 @@ class ExprTimeExtract extends index_1.ExpressionDef {
|
|
|
464
464
|
}
|
|
465
465
|
return {
|
|
466
466
|
dataType: "number",
|
|
467
|
-
|
|
467
|
+
expressionType: (0, malloy_types_1.maxExpressionType)(first.expressionType, last.expressionType),
|
|
468
468
|
value: [
|
|
469
469
|
{
|
|
470
470
|
type: "dialect",
|
|
@@ -481,7 +481,7 @@ class ExprTimeExtract extends index_1.ExpressionDef {
|
|
|
481
481
|
if ((0, malloy_types_1.isTimeFieldType)(argV.dataType)) {
|
|
482
482
|
return {
|
|
483
483
|
dataType: "number",
|
|
484
|
-
|
|
484
|
+
expressionType: argV.expressionType,
|
|
485
485
|
value: [
|
|
486
486
|
{
|
|
487
487
|
type: "dialect",
|
|
@@ -519,14 +519,12 @@ class ExprFunc extends index_1.ExpressionDef {
|
|
|
519
519
|
}
|
|
520
520
|
getExpression(fs) {
|
|
521
521
|
var _a, _b, _c;
|
|
522
|
-
let
|
|
522
|
+
let expressionType = "scalar";
|
|
523
523
|
let collectType;
|
|
524
524
|
const funcCall = [`${this.name}(`];
|
|
525
525
|
for (const fexpr of this.args) {
|
|
526
526
|
const expr = fexpr.getExpression(fs);
|
|
527
|
-
|
|
528
|
-
anyAggregate = true;
|
|
529
|
-
}
|
|
527
|
+
expressionType = (0, malloy_types_1.maxExpressionType)(expressionType, expr.expressionType);
|
|
530
528
|
if (collectType) {
|
|
531
529
|
funcCall.push(",");
|
|
532
530
|
}
|
|
@@ -540,7 +538,7 @@ class ExprFunc extends index_1.ExpressionDef {
|
|
|
540
538
|
const dataType = (_c = (_b = (_a = dialect === null || dialect === void 0 ? void 0 : dialect.getFunctionInfo(this.name)) === null || _a === void 0 ? void 0 : _a.returnType) !== null && _b !== void 0 ? _b : collectType) !== null && _c !== void 0 ? _c : "number";
|
|
541
539
|
return {
|
|
542
540
|
dataType: dataType,
|
|
543
|
-
|
|
541
|
+
expressionType,
|
|
544
542
|
value: (0, index_1.compressExpr)(funcCall),
|
|
545
543
|
};
|
|
546
544
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { AtomicFieldType, Fragment, TimestampUnit, Expr, TimeFieldType } from "../../model/malloy_types";
|
|
1
|
+
import { AtomicFieldType, Fragment, TimestampUnit, Expr, TimeFieldType, ExpressionType } from "../../model/malloy_types";
|
|
2
2
|
export declare type ExpressionValueType = AtomicFieldType | "null" | "unknown" | "duration" | "regular expression";
|
|
3
3
|
export declare function isExpressionValueType(fv: FieldValueType): fv is ExpressionValueType;
|
|
4
4
|
export declare type StageFieldType = "turtle";
|
|
5
5
|
export declare type FieldValueType = ExpressionValueType | StageFieldType | "struct";
|
|
6
6
|
export interface FragType {
|
|
7
7
|
dataType: FieldValueType;
|
|
8
|
-
|
|
8
|
+
expressionType: ExpressionType;
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
11
|
* Collects functions which operate on fragtype compatible objects
|
|
@@ -43,7 +43,7 @@ class FT {
|
|
|
43
43
|
static eq(good, checkThis) {
|
|
44
44
|
return (checkThis !== undefined &&
|
|
45
45
|
good.dataType === checkThis.dataType &&
|
|
46
|
-
good.
|
|
46
|
+
good.expressionType === checkThis.expressionType);
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
49
|
* Checks if the base types, ignoring aggregate, are equal
|
|
@@ -66,8 +66,8 @@ class FT {
|
|
|
66
66
|
const strings = types.map((type) => {
|
|
67
67
|
if (type) {
|
|
68
68
|
let inspected = type.dataType;
|
|
69
|
-
if (type.
|
|
70
|
-
inspected = `
|
|
69
|
+
if (type.expressionType != "scalar") {
|
|
70
|
+
inspected = `expressionType ${type.expressionType}`;
|
|
71
71
|
}
|
|
72
72
|
return inspected;
|
|
73
73
|
}
|
|
@@ -91,7 +91,7 @@ FT.anyAtomicT = [
|
|
|
91
91
|
FT.boolT,
|
|
92
92
|
];
|
|
93
93
|
function mkFragType(dType) {
|
|
94
|
-
return { dataType: dType,
|
|
94
|
+
return { dataType: dType, expressionType: "scalar" };
|
|
95
95
|
}
|
|
96
96
|
function isGranularResult(v) {
|
|
97
97
|
if (v.dataType !== "date" && v.dataType !== "timestamp") {
|
|
@@ -111,7 +111,7 @@ exports.isGranularResult = isGranularResult;
|
|
|
111
111
|
function errorFor(reason) {
|
|
112
112
|
return {
|
|
113
113
|
dataType: "unknown",
|
|
114
|
-
|
|
114
|
+
expressionType: "scalar",
|
|
115
115
|
value: [`_ERROR_${reason.replace(/ /g, "_")}`],
|
|
116
116
|
};
|
|
117
117
|
}
|
package/dist/lang/field-space.js
CHANGED
|
@@ -555,7 +555,7 @@ class ProjectFieldSpace extends ResultSpace {
|
|
|
555
555
|
this.log("Cannot nest queries in project");
|
|
556
556
|
return false;
|
|
557
557
|
}
|
|
558
|
-
if (qd.
|
|
558
|
+
if (model.expressionIsAggregate(qd.expressionType)) {
|
|
559
559
|
this.log("Cannot add aggregate measures to project");
|
|
560
560
|
return false;
|
|
561
561
|
}
|
|
@@ -3,7 +3,7 @@ import { FieldSpace, DynamicSpace, QuerySpace } from "./field-space";
|
|
|
3
3
|
import { FieldValueType, FieldDeclaration, TurtleDecl, HasParameter, Join, FieldReference } from "./ast";
|
|
4
4
|
interface FieldType {
|
|
5
5
|
type: FieldValueType;
|
|
6
|
-
|
|
6
|
+
expressionType?: model.ExpressionType;
|
|
7
7
|
}
|
|
8
8
|
export declare abstract class SpaceEntry {
|
|
9
9
|
abstract type(): FieldType;
|
package/dist/lang/space-field.js
CHANGED
|
@@ -82,8 +82,8 @@ class SpaceField extends SpaceEntry {
|
|
|
82
82
|
}
|
|
83
83
|
fieldTypeFromFieldDef(def) {
|
|
84
84
|
const ref = { type: def.type };
|
|
85
|
-
if (model.isFieldTypeDef(def) && def.
|
|
86
|
-
ref.
|
|
85
|
+
if (model.isFieldTypeDef(def) && def.expressionType) {
|
|
86
|
+
ref.expressionType = def.expressionType;
|
|
87
87
|
}
|
|
88
88
|
return ref;
|
|
89
89
|
}
|
|
@@ -256,7 +256,7 @@ class FANSPaceField extends SpaceField {
|
|
|
256
256
|
type: fieldType,
|
|
257
257
|
name: this.as,
|
|
258
258
|
e: [{ type: "parameter", path: this.ref.refString }],
|
|
259
|
-
|
|
259
|
+
expressionType: "scalar",
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
262
|
let fieldExpr = [{ type: "field", path: this.ref.refString }];
|
|
@@ -274,7 +274,7 @@ class FANSPaceField extends SpaceField {
|
|
|
274
274
|
type: fieldType,
|
|
275
275
|
name: this.as,
|
|
276
276
|
e: fieldExpr,
|
|
277
|
-
|
|
277
|
+
expressionType: fieldTypeInfo.expressionType,
|
|
278
278
|
};
|
|
279
279
|
}
|
|
280
280
|
return undefined;
|
|
@@ -832,7 +832,7 @@ describe("qops", () => {
|
|
|
832
832
|
const f = q.extendSource[0];
|
|
833
833
|
expect(f.type).toBe("number");
|
|
834
834
|
if (f.type == "number") {
|
|
835
|
-
expect(f.
|
|
835
|
+
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
836
836
|
}
|
|
837
837
|
}
|
|
838
838
|
else {
|
|
@@ -856,7 +856,7 @@ describe("qops", () => {
|
|
|
856
856
|
const f = q.extendSource[0];
|
|
857
857
|
expect(f.type).toBe("number");
|
|
858
858
|
if (f.type == "number") {
|
|
859
|
-
expect(f.
|
|
859
|
+
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
860
860
|
}
|
|
861
861
|
}
|
|
862
862
|
else {
|
package/dist/malloy.d.ts
CHANGED
|
@@ -507,7 +507,7 @@ export declare class AtomicField extends Entity {
|
|
|
507
507
|
isQueryField(): this is QueryField;
|
|
508
508
|
isExploreField(): this is ExploreField;
|
|
509
509
|
isAtomicField(): this is AtomicField;
|
|
510
|
-
|
|
510
|
+
isCalculation(): boolean;
|
|
511
511
|
get sourceField(): Field;
|
|
512
512
|
get sourceClasses(): string[];
|
|
513
513
|
sourceWasMeasure(): boolean;
|
package/dist/malloy.js
CHANGED
|
@@ -1005,8 +1005,9 @@ class AtomicField extends Entity {
|
|
|
1005
1005
|
isAtomicField() {
|
|
1006
1006
|
return true;
|
|
1007
1007
|
}
|
|
1008
|
-
|
|
1009
|
-
return !!this.fieldTypeDef.aggregate;
|
|
1008
|
+
isCalculation() {
|
|
1009
|
+
//return !!this.fieldTypeDef.aggregate;
|
|
1010
|
+
return (0, model_1.expressionIsCalculation)(this.fieldTypeDef.expressionType);
|
|
1010
1011
|
}
|
|
1011
1012
|
get sourceField() {
|
|
1012
1013
|
// TODO
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Dialect, DialectFieldList } from "../dialect";
|
|
2
|
-
import { FieldDef, FieldRef, FilterExpression, ModelDef, Query, QueryFieldDef, StructDef, StructRef, OrderBy, ResultMetadataDef, Expr, FieldFragment, AggregateFragment, CompiledQuery, FilterFragment, PipeSegment, TurtleDef, QuerySegment, Filtered, ParameterFragment, Parameter, JoinRelationship, DialectFragment, UngroupFragment, NamedQuery } from "./malloy_types";
|
|
2
|
+
import { FieldDef, FieldRef, FilterExpression, ModelDef, Query, QueryFieldDef, StructDef, StructRef, OrderBy, ResultMetadataDef, Expr, FieldFragment, AggregateFragment, CompiledQuery, FilterFragment, PipeSegment, TurtleDef, QuerySegment, Filtered, ParameterFragment, Parameter, JoinRelationship, DialectFragment, UngroupFragment, NamedQuery, AnalyticFragment } from "./malloy_types";
|
|
3
3
|
import { AndChain } from "./utils";
|
|
4
4
|
import { ResultStructMetadataDef, SearchIndexResult } from ".";
|
|
5
5
|
import { Connection } from "..";
|
|
@@ -64,13 +64,14 @@ declare class QueryField extends QueryNode {
|
|
|
64
64
|
generateAvgFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: AggregateFragment, state: GenerateState): string;
|
|
65
65
|
generateCountFragment(resultSet: FieldInstanceResult, context: QueryStruct, expr: AggregateFragment, state: GenerateState): string;
|
|
66
66
|
generateDialect(resultSet: FieldInstanceResult, context: QueryStruct, expr: DialectFragment, state: GenerateState): string;
|
|
67
|
+
generateAnalyticFragment(resultStruct: FieldInstanceResult, context: QueryStruct, expr: AnalyticFragment, state: GenerateState): string;
|
|
67
68
|
generateExpressionFromExpr(resultSet: FieldInstanceResult, context: QueryStruct, e: Expr, state?: GenerateState): string;
|
|
68
69
|
getExpr(): Expr;
|
|
69
70
|
generateExpression(resultSet: FieldInstanceResult): string;
|
|
70
71
|
}
|
|
71
72
|
declare class QueryAtomicField extends QueryField {
|
|
72
73
|
includeInWildcard(): boolean;
|
|
73
|
-
|
|
74
|
+
isCalculated(): boolean;
|
|
74
75
|
getFilterList(): FilterExpression[];
|
|
75
76
|
hasExpression(): boolean;
|
|
76
77
|
}
|
|
@@ -157,7 +158,7 @@ declare class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
157
158
|
joins: Map<string, JoinInstance>;
|
|
158
159
|
havings: AndChain;
|
|
159
160
|
isComplexQuery: boolean;
|
|
160
|
-
|
|
161
|
+
queryUsesPartitioning: boolean;
|
|
161
162
|
computeOnlyGroups: number[];
|
|
162
163
|
elimatedComputeGroups: boolean;
|
|
163
164
|
constructor(turtleDef: TurtleDef);
|
|
@@ -340,6 +340,62 @@ class QueryField extends QueryNode {
|
|
|
340
340
|
generateDialect(resultSet, context, expr, state) {
|
|
341
341
|
return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(expr), state);
|
|
342
342
|
}
|
|
343
|
+
generateAnalyticFragment(resultStruct, context, expr, state) {
|
|
344
|
+
const fields = resultStruct.getUngroupPartitions(undefined);
|
|
345
|
+
let partitionBy = "";
|
|
346
|
+
const fieldsString = fields.map((f) => f.getPartitionSQL()).join(", ");
|
|
347
|
+
if (fieldsString.length > 0) {
|
|
348
|
+
partitionBy = `PARTITION BY ${fieldsString}`;
|
|
349
|
+
}
|
|
350
|
+
let orderBy = "";
|
|
351
|
+
// calculate the ordering.
|
|
352
|
+
const obSQL = [];
|
|
353
|
+
let orderingField;
|
|
354
|
+
const orderByDef = resultStruct.firstSegment.orderBy ||
|
|
355
|
+
resultStruct.calculateDefaultOrderBy();
|
|
356
|
+
for (const ordering of orderByDef) {
|
|
357
|
+
if (typeof ordering.field === "string") {
|
|
358
|
+
orderingField = {
|
|
359
|
+
name: ordering.field,
|
|
360
|
+
fif: resultStruct.getField(ordering.field),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
orderingField = resultStruct.getFieldByNumber(ordering.field);
|
|
365
|
+
}
|
|
366
|
+
if (resultStruct.firstSegment.type === "reduce") {
|
|
367
|
+
obSQL.push(" " +
|
|
368
|
+
orderingField.fif.getSQL() +
|
|
369
|
+
// this.parent.dialect.sqlMaybeQuoteIdentifier(
|
|
370
|
+
// `${orderingField.name}__${resultStruct.groupSet}`
|
|
371
|
+
// ) +
|
|
372
|
+
` ${ordering.dir || "ASC"}`);
|
|
373
|
+
}
|
|
374
|
+
else if (resultStruct.firstSegment.type === "project") {
|
|
375
|
+
obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir || "ASC"}`);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
const func = malloy_types_1.malloyFunctions[expr.function];
|
|
379
|
+
const paramSQL = [];
|
|
380
|
+
if (expr.parameters !== undefined) {
|
|
381
|
+
for (const e of expr.parameters) {
|
|
382
|
+
if (typeof e === "string") {
|
|
383
|
+
paramSQL.push(e); // need to map to dimensional expression.
|
|
384
|
+
}
|
|
385
|
+
else if (typeof e === "number") {
|
|
386
|
+
paramSQL.push(e.toString());
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
paramSQL.push(this.generateExpressionFromExpr(resultStruct, context, e, state));
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
if (obSQL.length > 0) {
|
|
394
|
+
orderBy = " " + this.parent.dialect.sqlOrderBy(obSQL);
|
|
395
|
+
}
|
|
396
|
+
const sqlName = func.sqlName || expr.function;
|
|
397
|
+
return `${sqlName}(${paramSQL.join(", ")}) OVER(${partitionBy} ${orderBy} )`;
|
|
398
|
+
}
|
|
343
399
|
generateExpressionFromExpr(resultSet, context, e, state = new GenerateState()) {
|
|
344
400
|
let s = "";
|
|
345
401
|
for (const expr of e) {
|
|
@@ -358,6 +414,9 @@ class QueryField extends QueryNode {
|
|
|
358
414
|
else if ((0, malloy_types_1.isUngroupFragment)(expr)) {
|
|
359
415
|
s += this.generateUngroupedFragment(resultSet, context, expr, state);
|
|
360
416
|
}
|
|
417
|
+
else if ((0, malloy_types_1.isAnalyticFragment)(expr)) {
|
|
418
|
+
s += this.generateAnalyticFragment(resultSet, context, expr, state);
|
|
419
|
+
}
|
|
361
420
|
else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
|
|
362
421
|
let agg;
|
|
363
422
|
if (expr.function === "sum") {
|
|
@@ -423,19 +482,18 @@ class QueryField extends QueryNode {
|
|
|
423
482
|
return this.generateExpressionFromExpr(resultSet, this.parent, this.getExpr());
|
|
424
483
|
}
|
|
425
484
|
}
|
|
426
|
-
function
|
|
427
|
-
return f instanceof QueryAtomicField && f.
|
|
485
|
+
function isCalculatedField(f) {
|
|
486
|
+
return f instanceof QueryAtomicField && f.isCalculated();
|
|
428
487
|
}
|
|
429
488
|
function isScalarField(f) {
|
|
430
|
-
return f instanceof QueryAtomicField && !f.
|
|
489
|
+
return f instanceof QueryAtomicField && !f.isCalculated();
|
|
431
490
|
}
|
|
432
491
|
class QueryAtomicField extends QueryField {
|
|
433
492
|
includeInWildcard() {
|
|
434
493
|
return true;
|
|
435
494
|
}
|
|
436
|
-
|
|
437
|
-
return (this.fieldDef.
|
|
438
|
-
this.fieldDef.aggregate === true);
|
|
495
|
+
isCalculated() {
|
|
496
|
+
return (0, malloy_types_1.expressionIsCalculation)(this.fieldDef.expressionType);
|
|
439
497
|
}
|
|
440
498
|
getFilterList() {
|
|
441
499
|
return [];
|
|
@@ -754,7 +812,7 @@ class FieldInstanceResult {
|
|
|
754
812
|
if (["date", "timestamp"].indexOf(fi.f.fieldDef.type) > -1) {
|
|
755
813
|
return [{ dir: "desc", field: fi.fieldUsage.resultIndex }];
|
|
756
814
|
}
|
|
757
|
-
else if (
|
|
815
|
+
else if (isCalculatedField(fi.f)) {
|
|
758
816
|
return [{ dir: "desc", field: fi.fieldUsage.resultIndex }];
|
|
759
817
|
}
|
|
760
818
|
}
|
|
@@ -873,7 +931,7 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
873
931
|
this.joins = new Map();
|
|
874
932
|
this.havings = new utils_1.AndChain();
|
|
875
933
|
this.isComplexQuery = false;
|
|
876
|
-
this.
|
|
934
|
+
this.queryUsesPartitioning = false;
|
|
877
935
|
this.computeOnlyGroups = [];
|
|
878
936
|
this.elimatedComputeGroups = false;
|
|
879
937
|
}
|
|
@@ -1114,7 +1172,7 @@ class QueryQuery extends QueryField {
|
|
|
1114
1172
|
// turtles
|
|
1115
1173
|
// Timestamps and Dates (are just fine to leave as is).
|
|
1116
1174
|
// measures
|
|
1117
|
-
let e;
|
|
1175
|
+
// let e: Expr;
|
|
1118
1176
|
if (field instanceof QueryQuery) {
|
|
1119
1177
|
const newFieldDef = (0, lodash_1.cloneDeep)(field.fieldDef);
|
|
1120
1178
|
newFieldDef.as = f.name;
|
|
@@ -1123,18 +1181,18 @@ class QueryQuery extends QueryField {
|
|
|
1123
1181
|
}
|
|
1124
1182
|
else if (!(field instanceof QueryFieldTimestamp ||
|
|
1125
1183
|
field instanceof QueryFieldDate)) {
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
};
|
|
1137
|
-
field = this.parent.makeQueryField(newFieldDef);
|
|
1184
|
+
throw new Error(`No longer generate code this way. \n ${JSON.stringify(f, undefined, 2)}`);
|
|
1185
|
+
// // its a measure
|
|
1186
|
+
// e = [{ type: "field", path: field.getFullOutputName() }];
|
|
1187
|
+
// if ("filterList" in f && f.filterList) {
|
|
1188
|
+
// e = [{ type: "filterExpression", filterList: f.filterList, e: e }];
|
|
1189
|
+
// }
|
|
1190
|
+
// const newFieldDef = {
|
|
1191
|
+
// type: field.fieldDef.type,
|
|
1192
|
+
// name: f.as,
|
|
1193
|
+
// e,
|
|
1194
|
+
// };
|
|
1195
|
+
// field = this.parent.makeQueryField(newFieldDef as FieldDef);
|
|
1138
1196
|
}
|
|
1139
1197
|
// or inline field FieldTypeDef
|
|
1140
1198
|
}
|
|
@@ -1228,7 +1286,7 @@ class QueryQuery extends QueryField {
|
|
|
1228
1286
|
if ((0, malloy_types_1.isUngroupFragment)(expr)) {
|
|
1229
1287
|
resultStruct.resultUsesUngrouped = true;
|
|
1230
1288
|
resultStruct.root().isComplexQuery = true;
|
|
1231
|
-
resultStruct.root().
|
|
1289
|
+
resultStruct.root().queryUsesPartitioning = true;
|
|
1232
1290
|
if (expr.fields && expr.fields.length > 0) {
|
|
1233
1291
|
const key = expr.fields.sort().join("|") + expr.type;
|
|
1234
1292
|
if (resultStruct.ungroupedSets.get(key) === undefined) {
|
|
@@ -1304,6 +1362,9 @@ class QueryQuery extends QueryField {
|
|
|
1304
1362
|
}
|
|
1305
1363
|
this.addDependantExpr(resultStruct, context, expr.e, joinStack);
|
|
1306
1364
|
}
|
|
1365
|
+
else if ((0, malloy_types_1.isAnalyticFragment)(expr)) {
|
|
1366
|
+
resultStruct.root().queryUsesPartitioning = true;
|
|
1367
|
+
}
|
|
1307
1368
|
}
|
|
1308
1369
|
}
|
|
1309
1370
|
addDependancies(resultStruct, field) {
|
|
@@ -1329,7 +1390,7 @@ class QueryQuery extends QueryField {
|
|
|
1329
1390
|
type: "result",
|
|
1330
1391
|
});
|
|
1331
1392
|
this.addDependancies(resultStruct, field);
|
|
1332
|
-
if (
|
|
1393
|
+
if (isCalculatedField(field)) {
|
|
1333
1394
|
if (this.firstSegment.type === "project") {
|
|
1334
1395
|
throw new Error(`Aggregate Fields cannot be used in PROJECT - '${field.fieldDef.name}'`);
|
|
1335
1396
|
}
|
|
@@ -1387,8 +1448,8 @@ class QueryQuery extends QueryField {
|
|
|
1387
1448
|
// in the correct catgory.
|
|
1388
1449
|
for (const cond of list || []) {
|
|
1389
1450
|
const context = this.parent;
|
|
1390
|
-
if ((which === "having" && cond.
|
|
1391
|
-
(which === "where" &&
|
|
1451
|
+
if ((which === "having" && (0, malloy_types_1.expressionIsCalculation)(cond.expressionType)) ||
|
|
1452
|
+
(which === "where" && cond.expressionType === "scalar")) {
|
|
1392
1453
|
const sqlClause = this.generateExpressionFromExpr(resultStruct, context, cond.expression, undefined);
|
|
1393
1454
|
resultFilters.add(sqlClause);
|
|
1394
1455
|
}
|
|
@@ -1412,7 +1473,7 @@ class QueryQuery extends QueryField {
|
|
|
1412
1473
|
const sourceField = fi.f.parent.getFullOutputName() + (fieldDef.name || fieldDef.as);
|
|
1413
1474
|
const sourceExpression = fieldDef.code;
|
|
1414
1475
|
const sourceClasses = [sourceField];
|
|
1415
|
-
if (
|
|
1476
|
+
if (isCalculatedField(fi.f)) {
|
|
1416
1477
|
filterList = fi.f.getFilterList();
|
|
1417
1478
|
return {
|
|
1418
1479
|
sourceField,
|
|
@@ -1762,7 +1823,7 @@ class QueryQuery extends QueryField {
|
|
|
1762
1823
|
const exp = fi.getSQL();
|
|
1763
1824
|
if (isScalarField(fi.f)) {
|
|
1764
1825
|
if (this.parent.dialect.name === "standardsql" &&
|
|
1765
|
-
this.rootResult.
|
|
1826
|
+
this.rootResult.queryUsesPartitioning) {
|
|
1766
1827
|
// BigQuery can't partition aggregate function except when the field has no
|
|
1767
1828
|
// expression. Additionally it can't partition by floats. We stuff expressions
|
|
1768
1829
|
// and numbers as strings into a lateral join when the query has ungrouped expressions
|
|
@@ -1788,7 +1849,7 @@ class QueryQuery extends QueryField {
|
|
|
1788
1849
|
}
|
|
1789
1850
|
output.dimensionIndexes.push(output.fieldIndex++);
|
|
1790
1851
|
}
|
|
1791
|
-
else if (
|
|
1852
|
+
else if (isCalculatedField(fi.f)) {
|
|
1792
1853
|
output.sql.push(`${exp} as ${outputName}`);
|
|
1793
1854
|
output.fieldIndex++;
|
|
1794
1855
|
}
|
|
@@ -1919,7 +1980,7 @@ class QueryQuery extends QueryField {
|
|
|
1919
1980
|
output.sql.push(`${exp} as ${sqlFieldName}`);
|
|
1920
1981
|
output.dimensionIndexes.push(output.fieldIndex++);
|
|
1921
1982
|
}
|
|
1922
|
-
else if (
|
|
1983
|
+
else if (isCalculatedField(fi.f)) {
|
|
1923
1984
|
const exp = this.parent.dialect.sqlAnyValue(resultSet.groupSet, sqlFieldName);
|
|
1924
1985
|
output.sql.push(`${exp} as ${sqlFieldName}`);
|
|
1925
1986
|
output.fieldIndex++;
|
|
@@ -1991,7 +2052,7 @@ class QueryQuery extends QueryField {
|
|
|
1991
2052
|
fieldsSQL.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
|
|
1992
2053
|
dimensionIndexes.push(fieldIndex++);
|
|
1993
2054
|
}
|
|
1994
|
-
else if (
|
|
2055
|
+
else if (isCalculatedField(fi.f)) {
|
|
1995
2056
|
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(name, this.rootResult.groupSet, sqlName));
|
|
1996
2057
|
fieldIndex++;
|
|
1997
2058
|
}
|
|
@@ -111,6 +111,20 @@ export interface UngroupFragment {
|
|
|
111
111
|
fields?: string[];
|
|
112
112
|
}
|
|
113
113
|
export declare function isUngroupFragment(f: Fragment): f is UngroupFragment;
|
|
114
|
+
export declare type MalloyFunctionParam = AtomicFieldType | "any" | "nconst" | "regexp";
|
|
115
|
+
export interface MalloyFunctionInfo {
|
|
116
|
+
returnType: AtomicFieldType;
|
|
117
|
+
parameters: "any" | "none" | MalloyFunctionParam[];
|
|
118
|
+
expressionType?: "aggregate" | "analytic";
|
|
119
|
+
sqlName?: string;
|
|
120
|
+
}
|
|
121
|
+
export declare const malloyFunctions: Record<string, MalloyFunctionInfo>;
|
|
122
|
+
export interface AnalyticFragment {
|
|
123
|
+
type: "analytic";
|
|
124
|
+
function: string;
|
|
125
|
+
parameters?: (string | number | Expr)[];
|
|
126
|
+
}
|
|
127
|
+
export declare function isAnalyticFragment(f: Fragment): f is AnalyticFragment;
|
|
114
128
|
export interface FieldFragment {
|
|
115
129
|
type: "field";
|
|
116
130
|
path: string;
|
|
@@ -189,7 +203,7 @@ export interface StringLiteralFragment extends DialectFragmentBase {
|
|
|
189
203
|
literal: string;
|
|
190
204
|
}
|
|
191
205
|
export declare type DialectFragment = DivFragment | TimeLiteralFragment | NowFragment | TimeDeltaFragment | TimeDiffFragment | TimeTruncFragment | TypecastFragment | TimeExtractFragment | StringLiteralFragment | RegexpMatchFragment;
|
|
192
|
-
export declare type Fragment = string | ApplyFragment | ApplyValueFragment | FieldFragment | ParameterFragment | FilterFragment | AggregateFragment | UngroupFragment | DialectFragment;
|
|
206
|
+
export declare type Fragment = string | ApplyFragment | ApplyValueFragment | FieldFragment | ParameterFragment | FilterFragment | AggregateFragment | UngroupFragment | DialectFragment | AnalyticFragment;
|
|
193
207
|
export declare type Expr = Fragment[];
|
|
194
208
|
export interface TypedValue {
|
|
195
209
|
value: Expr;
|
|
@@ -210,11 +224,15 @@ declare type TagElement = Expr | string;
|
|
|
210
224
|
* ```
|
|
211
225
|
*/
|
|
212
226
|
export declare function mkExpr(src: TemplateStringsArray, ...exprs: TagElement[]): Expr;
|
|
227
|
+
export declare type ExpressionType = "scalar" | "aggregate" | "analytic";
|
|
213
228
|
export interface Expression {
|
|
214
229
|
e?: Expr;
|
|
215
|
-
|
|
230
|
+
expressionType?: ExpressionType;
|
|
216
231
|
code?: string;
|
|
217
232
|
}
|
|
233
|
+
export declare function expressionIsAggregate(e: ExpressionType | undefined): boolean;
|
|
234
|
+
export declare function expressionIsCalculation(e: ExpressionType | undefined): boolean;
|
|
235
|
+
export declare function maxExpressionType(e1: ExpressionType, e2: ExpressionType): ExpressionType;
|
|
218
236
|
interface JustExpression {
|
|
219
237
|
e: Expr;
|
|
220
238
|
}
|
|
@@ -446,7 +464,7 @@ export declare type PrimaryKeyRef = string;
|
|
|
446
464
|
export interface FilterExpression {
|
|
447
465
|
expression: Expr;
|
|
448
466
|
code: string;
|
|
449
|
-
|
|
467
|
+
expressionType: ExpressionType;
|
|
450
468
|
}
|
|
451
469
|
/** Get the output name for a NamedObject */
|
|
452
470
|
export declare function getIdentifier(n: AliasedName): string;
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* GNU General Public License for more details.
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.
|
|
15
|
+
exports.isMeasureLike = exports.getPhysicalFields = exports.getDimensions = exports.isPhysical = exports.isDimensional = exports.isTurtleDef = exports.getIdentifier = exports.isFieldStructDef = exports.isFieldTimeBased = exports.isFieldTypeDef = exports.isSQLBlock = exports.isSQLFragment = exports.isJoinOn = exports.isIndexSegment = exports.isSamplingEnable = exports.isSamplingPercent = exports.isSamplingRows = exports.isQuerySegment = exports.isProjectSegment = exports.isReduceSegment = exports.refIsStructDef = exports.isByExpression = exports.isByName = exports.ValueType = exports.isExtractUnit = exports.isTimestampUnit = exports.isDateUnit = exports.FieldIsIntrinsic = exports.isAtomicFieldType = exports.isTimeFieldType = exports.hasExpression = exports.maxExpressionType = exports.expressionIsCalculation = exports.expressionIsAggregate = exports.mkExpr = exports.isApplyFragment = exports.isApplyValue = exports.isParameterFragment = exports.isFieldFragment = exports.isAnalyticFragment = exports.malloyFunctions = exports.isUngroupFragment = exports.isAsymmetricFragment = exports.isAggregateFragment = exports.isDialectFragment = exports.isFilterFragment = exports.isFilteredAliasedName = exports.paramHasValue = exports.isConditionParameter = exports.isValueParameter = void 0;
|
|
16
|
+
exports.isValueDate = exports.isValueTimestamp = exports.isValueBoolean = exports.isValueNumber = exports.isValueString = void 0;
|
|
16
17
|
function isValueParameter(p) {
|
|
17
18
|
return p.value !== undefined;
|
|
18
19
|
}
|
|
@@ -55,6 +56,61 @@ function isUngroupFragment(f) {
|
|
|
55
56
|
return ftype == "all" || ftype == "exclude";
|
|
56
57
|
}
|
|
57
58
|
exports.isUngroupFragment = isUngroupFragment;
|
|
59
|
+
exports.malloyFunctions = {
|
|
60
|
+
row_number: {
|
|
61
|
+
returnType: "number",
|
|
62
|
+
parameters: "none",
|
|
63
|
+
expressionType: "analytic",
|
|
64
|
+
},
|
|
65
|
+
rank: {
|
|
66
|
+
returnType: "number",
|
|
67
|
+
parameters: "none",
|
|
68
|
+
expressionType: "analytic",
|
|
69
|
+
},
|
|
70
|
+
dense_rank: {
|
|
71
|
+
returnType: "number",
|
|
72
|
+
parameters: "none",
|
|
73
|
+
expressionType: "analytic",
|
|
74
|
+
},
|
|
75
|
+
first_value_in_column: {
|
|
76
|
+
returnType: "number",
|
|
77
|
+
parameters: "any",
|
|
78
|
+
expressionType: "analytic",
|
|
79
|
+
sqlName: "first_value",
|
|
80
|
+
},
|
|
81
|
+
last_value_in_column: {
|
|
82
|
+
returnType: "number",
|
|
83
|
+
parameters: "any",
|
|
84
|
+
expressionType: "analytic",
|
|
85
|
+
sqlName: "last_value",
|
|
86
|
+
},
|
|
87
|
+
min_in_column: {
|
|
88
|
+
returnType: "number",
|
|
89
|
+
parameters: "any",
|
|
90
|
+
expressionType: "analytic",
|
|
91
|
+
sqlName: "min",
|
|
92
|
+
},
|
|
93
|
+
max_in_column: {
|
|
94
|
+
returnType: "number",
|
|
95
|
+
parameters: "any",
|
|
96
|
+
expressionType: "analytic",
|
|
97
|
+
sqlName: "max",
|
|
98
|
+
},
|
|
99
|
+
ntile: {
|
|
100
|
+
returnType: "number",
|
|
101
|
+
parameters: ["nconst"],
|
|
102
|
+
expressionType: "analytic",
|
|
103
|
+
},
|
|
104
|
+
lag: {
|
|
105
|
+
returnType: "number",
|
|
106
|
+
parameters: ["number", "nconst"],
|
|
107
|
+
expressionType: "analytic",
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
function isAnalyticFragment(f) {
|
|
111
|
+
return (f === null || f === void 0 ? void 0 : f.type) === "analytic";
|
|
112
|
+
}
|
|
113
|
+
exports.isAnalyticFragment = isAnalyticFragment;
|
|
58
114
|
function isFieldFragment(f) {
|
|
59
115
|
return (f === null || f === void 0 ? void 0 : f.type) === "field";
|
|
60
116
|
}
|
|
@@ -102,6 +158,25 @@ function mkExpr(src, ...exprs) {
|
|
|
102
158
|
return ret;
|
|
103
159
|
}
|
|
104
160
|
exports.mkExpr = mkExpr;
|
|
161
|
+
function expressionIsAggregate(e) {
|
|
162
|
+
return e === "aggregate";
|
|
163
|
+
}
|
|
164
|
+
exports.expressionIsAggregate = expressionIsAggregate;
|
|
165
|
+
function expressionIsCalculation(e) {
|
|
166
|
+
return e === "aggregate" || e === "analytic";
|
|
167
|
+
}
|
|
168
|
+
exports.expressionIsCalculation = expressionIsCalculation;
|
|
169
|
+
function maxExpressionType(e1, e2) {
|
|
170
|
+
let ret = "scalar";
|
|
171
|
+
if (e1 === "aggregate" || e2 === "aggregate") {
|
|
172
|
+
ret = "aggregate";
|
|
173
|
+
}
|
|
174
|
+
if (e1 === "analytic" || e2 === "analytic") {
|
|
175
|
+
ret = "analytic";
|
|
176
|
+
}
|
|
177
|
+
return ret;
|
|
178
|
+
}
|
|
179
|
+
exports.maxExpressionType = maxExpressionType;
|
|
105
180
|
/** Grants access to the expression property of a FielfDef */
|
|
106
181
|
function hasExpression(f) {
|
|
107
182
|
return f.e !== undefined;
|