@malloydata/malloy 0.0.123-dev240205203120 → 0.0.123-dev240207164302
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/duckdb/duckdb.js +1 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.js +11 -2
- package/dist/lang/ast/expressions/expr-minus.js +1 -1
- package/dist/lang/ast/fragtype-utils.d.ts +6 -0
- package/dist/lang/ast/fragtype-utils.js +12 -0
- package/dist/lang/test/expressions.spec.js +17 -0
- package/dist/lang/test/parse.spec.js +14 -0
- package/dist/model/utils.js +7 -1
- package/package.json +1 -1
|
@@ -76,7 +76,7 @@ class DuckDBDialect extends dialect_1.Dialect {
|
|
|
76
76
|
return `__udf${Math.floor(Math.random() * 100000)}`;
|
|
77
77
|
}
|
|
78
78
|
quoteTablePath(tableName) {
|
|
79
|
-
return tableName.match(
|
|
79
|
+
return tableName.match(/[/*:]/) ? `'${tableName}'` : tableName;
|
|
80
80
|
}
|
|
81
81
|
sqlGroupSetTable(groupSetCount) {
|
|
82
82
|
return `CROSS JOIN (SELECT UNNEST(GENERATE_SERIES(0,${groupSetCount},1)) as group_set ) as group_set`;
|
|
@@ -87,7 +87,8 @@ class ExprAggregateFunction extends expression_def_1.ExpressionDef {
|
|
|
87
87
|
// locality to be that join path
|
|
88
88
|
const joinUsage = this.getJoinUsage(inputFS);
|
|
89
89
|
const allUsagesSame = joinUsage.length === 1 ||
|
|
90
|
-
joinUsage.
|
|
90
|
+
(joinUsage.length > 1 &&
|
|
91
|
+
joinUsage.slice(1).every(p => joinPathEq(p, joinUsage[0])));
|
|
91
92
|
if (allUsagesSame) {
|
|
92
93
|
structPath = joinUsage[0].map(p => p.name);
|
|
93
94
|
sourceRelationship = joinUsage[0];
|
|
@@ -227,7 +228,6 @@ function getJoinUsage(fs, expr) {
|
|
|
227
228
|
};
|
|
228
229
|
(0, utils_1.exprWalk)(expr, frag => {
|
|
229
230
|
if (typeof frag !== 'string') {
|
|
230
|
-
// TODO make this work for field references inside sql_* functions
|
|
231
231
|
if (frag.type === 'field') {
|
|
232
232
|
const def = lookup(fs, frag.path);
|
|
233
233
|
if (def.def.type !== 'struct' && def.def.type !== 'turtle') {
|
|
@@ -240,6 +240,15 @@ function getJoinUsage(fs, expr) {
|
|
|
240
240
|
}
|
|
241
241
|
}
|
|
242
242
|
}
|
|
243
|
+
else if (frag.type === 'source-reference') {
|
|
244
|
+
if (frag.path) {
|
|
245
|
+
const def = lookup(fs, frag.path);
|
|
246
|
+
result.push(def.relationship);
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
result.push([]);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
243
252
|
}
|
|
244
253
|
});
|
|
245
254
|
return result;
|
|
@@ -35,7 +35,7 @@ class ExprMinus extends expression_def_1.ExpressionDef {
|
|
|
35
35
|
}
|
|
36
36
|
getExpression(fs) {
|
|
37
37
|
const expr = this.expr.getExpression(fs);
|
|
38
|
-
if (
|
|
38
|
+
if (fragtype_utils_1.FT.typeIn(expr, this.legalChildTypes)) {
|
|
39
39
|
if (expr.value.length > 1) {
|
|
40
40
|
return { ...expr, dataType: 'number', value: ['-(', ...expr.value, ')'] };
|
|
41
41
|
}
|
|
@@ -15,6 +15,12 @@ export declare class FT {
|
|
|
15
15
|
* @param checkThis The possibly undefined candidate
|
|
16
16
|
*/
|
|
17
17
|
static eq(good: TypeDesc, checkThis: TypeDesc | undefined): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Checks if a given type is in a list, ignoring aggregate
|
|
20
|
+
* @param check The type to check (can be undefined)
|
|
21
|
+
* @param from List of types which are OK
|
|
22
|
+
*/
|
|
23
|
+
static typeIn(check: TypeDesc | undefined, from: TypeDesc[]): boolean;
|
|
18
24
|
/**
|
|
19
25
|
* Checks if the base types, ignoring aggregate, are equal
|
|
20
26
|
* @param left Left type
|
|
@@ -53,6 +53,18 @@ class FT {
|
|
|
53
53
|
good.dataType === checkThis.dataType &&
|
|
54
54
|
good.expressionType === checkThis.expressionType);
|
|
55
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Checks if a given type is in a list, ignoring aggregate
|
|
58
|
+
* @param check The type to check (can be undefined)
|
|
59
|
+
* @param from List of types which are OK
|
|
60
|
+
*/
|
|
61
|
+
static typeIn(check, from) {
|
|
62
|
+
if (check) {
|
|
63
|
+
const found = from.find(okType => FT.typeEq(okType, check));
|
|
64
|
+
return found !== undefined;
|
|
65
|
+
}
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
56
68
|
/**
|
|
57
69
|
* Checks if the base types, ignoring aggregate, are equal
|
|
58
70
|
* @param left Left type
|
|
@@ -475,6 +475,18 @@ describe('expressions', () => {
|
|
|
475
475
|
test('source.sum(nested.column)', () => {
|
|
476
476
|
expect(modelX `source.sum(nested.column)`).translationToFailWith('Cannot compute `sum` across repeated relationship `nested`; use `nested.column.sum()`');
|
|
477
477
|
});
|
|
478
|
+
test('can aggregate field defined with no join usage', () => {
|
|
479
|
+
expect((0, test_translator_1.markSource) `
|
|
480
|
+
##! experimental { sql_functions }
|
|
481
|
+
source: s is a extend {
|
|
482
|
+
measure: c is count()
|
|
483
|
+
dimension: f is 1
|
|
484
|
+
}
|
|
485
|
+
run: s -> {
|
|
486
|
+
aggregate: v is f.sum()
|
|
487
|
+
}
|
|
488
|
+
`).toTranslate();
|
|
489
|
+
});
|
|
478
490
|
test('sum(inline.column)', () => {
|
|
479
491
|
expect(modelX `sum(inline.column)`).toTranslateWithWarnings('Join path is required for this calculation; use `inline.column.sum()` or `source.sum(inline.column)` to get a result weighted with respect to `source`');
|
|
480
492
|
});
|
|
@@ -799,6 +811,11 @@ describe('unspported fields in schema', () => {
|
|
|
799
811
|
test('negative numbers are not tokens', () => {
|
|
800
812
|
expect((0, test_translator_1.expr) `ai-1`).toTranslate();
|
|
801
813
|
});
|
|
814
|
+
describe('sql functions', () => {
|
|
815
|
+
test('can aggregate a sql_ function', () => {
|
|
816
|
+
expect((0, test_translator_1.expr) `sum(sql_number("\${a} * 2"))`).toTranslate();
|
|
817
|
+
});
|
|
818
|
+
});
|
|
802
819
|
describe('cast', () => {
|
|
803
820
|
// The "+ 1"s are there to make sure the result is of type 'number'
|
|
804
821
|
test('sql cast', () => {
|
|
@@ -875,5 +875,19 @@ describe('m3/m4 source query sentences', () => {
|
|
|
875
875
|
`).toTranslate();
|
|
876
876
|
});
|
|
877
877
|
});
|
|
878
|
+
describe('sql_functions', () => {
|
|
879
|
+
test('can aggregate sql function', () => {
|
|
880
|
+
expect((0, test_translator_1.markSource) `
|
|
881
|
+
##! experimental { sql_functions }
|
|
882
|
+
source: s is a extend {
|
|
883
|
+
measure: c is count()
|
|
884
|
+
dimension: sql_expr is sql_number("LENGTH(\${TABLE}.astr)")
|
|
885
|
+
}
|
|
886
|
+
run: s -> {
|
|
887
|
+
aggregate: v is sql_expr.sum()
|
|
888
|
+
}
|
|
889
|
+
`).toTranslate();
|
|
890
|
+
});
|
|
891
|
+
});
|
|
878
892
|
test('non breaking space in source', () => expect('source:\u00a0z\u00a0is\u00a0a').toParse());
|
|
879
893
|
//# sourceMappingURL=parse.spec.js.map
|
package/dist/model/utils.js
CHANGED
|
@@ -125,6 +125,9 @@ function exprMap(expr, func) {
|
|
|
125
125
|
case 'function_parameter':
|
|
126
126
|
case 'parameter':
|
|
127
127
|
case 'outputField':
|
|
128
|
+
case 'source-reference':
|
|
129
|
+
case 'aggregate_limit':
|
|
130
|
+
case 'aggregate_order_by':
|
|
128
131
|
return fragment;
|
|
129
132
|
case 'sql-string':
|
|
130
133
|
return {
|
|
@@ -231,6 +234,9 @@ function exprWalk(expr, func) {
|
|
|
231
234
|
case 'function_parameter':
|
|
232
235
|
case 'parameter':
|
|
233
236
|
case 'outputField':
|
|
237
|
+
case 'source-reference':
|
|
238
|
+
case 'aggregate_limit':
|
|
239
|
+
case 'aggregate_order_by':
|
|
234
240
|
return fragment;
|
|
235
241
|
case 'function_call':
|
|
236
242
|
return {
|
|
@@ -304,7 +310,7 @@ function exprWalk(expr, func) {
|
|
|
304
310
|
}
|
|
305
311
|
}
|
|
306
312
|
default:
|
|
307
|
-
throw new Error(
|
|
313
|
+
throw new Error(`unexpected fragment type ${fragment.type}`);
|
|
308
314
|
}
|
|
309
315
|
});
|
|
310
316
|
}
|