@malloydata/malloy 0.0.240-dev250311202829 → 0.0.240-dev250311214430
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/api/core.d.ts +1 -0
- package/dist/api/core.js +2 -1
- package/dist/lang/malloy-parse-info.d.ts +2 -10
- package/dist/lang/malloy-to-ast.d.ts +1 -0
- package/dist/lang/malloy-to-ast.js +9 -6
- package/dist/lang/malloy-to-stable-query.d.ts +76 -0
- package/dist/lang/malloy-to-stable-query.js +660 -0
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/parse-malloy.d.ts +2 -9
- package/dist/lang/parse-malloy.js +7 -112
- package/dist/lang/parse-tree-walkers/model-annotation-walker.js +1 -1
- package/dist/lang/run-malloy-parser.d.ts +3 -0
- package/dist/lang/run-malloy-parser.js +53 -0
- package/dist/lang/syntax-errors/malloy-parser-error-listener.d.ts +4 -3
- package/dist/lang/syntax-errors/malloy-parser-error-listener.js +8 -4
- package/dist/lang/utils.d.ts +27 -1
- package/dist/lang/utils.js +78 -1
- package/package.json +3 -3
- package/dist/api/asynchronous.spec.d.ts +0 -1
- package/dist/api/asynchronous.spec.js +0 -240
- package/dist/api/sessioned.spec.d.ts +0 -1
- package/dist/api/sessioned.spec.js +0 -476
- package/dist/api/stateless.spec.d.ts +0 -1
- package/dist/api/stateless.spec.js +0 -400
- package/dist/lang/test/annotation.spec.d.ts +0 -14
- package/dist/lang/test/annotation.spec.js +0 -809
- package/dist/lang/test/composite-field-usage.spec.d.ts +0 -1
- package/dist/lang/test/composite-field-usage.spec.js +0 -261
- package/dist/lang/test/document-help-context-walker.spec.d.ts +0 -1
- package/dist/lang/test/document-help-context-walker.spec.js +0 -55
- package/dist/lang/test/document-symbol-walker.spec.d.ts +0 -1
- package/dist/lang/test/document-symbol-walker.spec.js +0 -209
- package/dist/lang/test/expressions.spec.d.ts +0 -1
- package/dist/lang/test/expressions.spec.js +0 -1181
- package/dist/lang/test/field-symbols.spec.d.ts +0 -1
- package/dist/lang/test/field-symbols.spec.js +0 -194
- package/dist/lang/test/find-table-path-walker.spec.d.ts +0 -1
- package/dist/lang/test/find-table-path-walker.spec.js +0 -50
- package/dist/lang/test/imports.spec.d.ts +0 -1
- package/dist/lang/test/imports.spec.js +0 -283
- package/dist/lang/test/lenses.spec.d.ts +0 -1
- package/dist/lang/test/lenses.spec.js +0 -384
- package/dist/lang/test/literals.spec.d.ts +0 -1
- package/dist/lang/test/literals.spec.js +0 -267
- package/dist/lang/test/locations.spec.d.ts +0 -1
- package/dist/lang/test/locations.spec.js +0 -679
- package/dist/lang/test/model-annotation-walker.spec.d.ts +0 -1
- package/dist/lang/test/model-annotation-walker.spec.js +0 -46
- package/dist/lang/test/model_serialization.spec.d.ts +0 -5
- package/dist/lang/test/model_serialization.spec.js +0 -116
- package/dist/lang/test/parameters.spec.d.ts +0 -1
- package/dist/lang/test/parameters.spec.js +0 -619
- package/dist/lang/test/parse.spec.d.ts +0 -1
- package/dist/lang/test/parse.spec.js +0 -893
- package/dist/lang/test/pretranslate.spec.d.ts +0 -1
- package/dist/lang/test/pretranslate.spec.js +0 -80
- package/dist/lang/test/query.spec.d.ts +0 -1
- package/dist/lang/test/query.spec.js +0 -1149
- package/dist/lang/test/source.spec.d.ts +0 -1
- package/dist/lang/test/source.spec.js +0 -570
- package/dist/lang/test/sql-block.spec.d.ts +0 -1
- package/dist/lang/test/sql-block.spec.js +0 -145
- package/dist/lang/test/syntax-errors.spec.d.ts +0 -1
- package/dist/lang/test/syntax-errors.spec.js +0 -142
- package/dist/model/utils.spec.d.ts +0 -1
- package/dist/model/utils.spec.js +0 -38
|
@@ -1,1149 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/*
|
|
3
|
-
* Copyright 2023 Google LLC
|
|
4
|
-
*
|
|
5
|
-
* Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
-
* a copy of this software and associated documentation files
|
|
7
|
-
* (the "Software"), to deal in the Software without restriction,
|
|
8
|
-
* including without limitation the rights to use, copy, modify, merge,
|
|
9
|
-
* publish, distribute, sublicense, and/or sell copies of the Software,
|
|
10
|
-
* and to permit persons to whom the Software is furnished to do so,
|
|
11
|
-
* subject to the following conditions:
|
|
12
|
-
*
|
|
13
|
-
* The above copyright notice and this permission notice shall be
|
|
14
|
-
* included in all copies or substantial portions of the Software.
|
|
15
|
-
*
|
|
16
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
-
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
-
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
19
|
-
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
20
|
-
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
21
|
-
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
22
|
-
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
|
-
*/
|
|
24
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
const test_translator_1 = require("./test-translator");
|
|
26
|
-
require("./parse-expects");
|
|
27
|
-
const model_1 = require("../../model");
|
|
28
|
-
function getFirstQuerySegment(q) {
|
|
29
|
-
const qSeg = q === null || q === void 0 ? void 0 : q.pipeline[0];
|
|
30
|
-
if (qSeg && (0, model_1.isQuerySegment)(qSeg)) {
|
|
31
|
-
return qSeg;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
function getFirstSegmentFields(q) {
|
|
35
|
-
var _a;
|
|
36
|
-
const qSeg = getFirstQuerySegment(q);
|
|
37
|
-
return (_a = qSeg === null || qSeg === void 0 ? void 0 : qSeg.queryFields) !== null && _a !== void 0 ? _a : [];
|
|
38
|
-
}
|
|
39
|
-
function getFirstSegmentFieldNames(q) {
|
|
40
|
-
const qf = getFirstSegmentFields(q);
|
|
41
|
-
return qf.map(f => f.type === 'fieldref' && f.path.length === 1
|
|
42
|
-
? f.path[0]
|
|
43
|
-
: `expected simple ref, got ${JSON.stringify(f)}`);
|
|
44
|
-
}
|
|
45
|
-
describe('query:', () => {
|
|
46
|
-
describe('basic query syntax', () => {
|
|
47
|
-
test('run:anonymous query', () => expect('run: a -> { group_by: astr }').toTranslate());
|
|
48
|
-
test('query:anonymous query m4 warning', () => {
|
|
49
|
-
expect((0, test_translator_1.markSource) `##! m4warnings=warn
|
|
50
|
-
query: ${'a -> { group_by: astr }'}`).toLog((0, test_translator_1.warningMessage)('Anonymous `query:` statements are deprecated, use `run:` instead'));
|
|
51
|
-
});
|
|
52
|
-
test('named query:', () => expect('query: aq is a -> { group_by: astr }').toTranslate());
|
|
53
|
-
test('run query ref', () => expect(`
|
|
54
|
-
query: foo is a -> { group_by: astr }
|
|
55
|
-
run: foo
|
|
56
|
-
`).toTranslate());
|
|
57
|
-
test('query from query', () => {
|
|
58
|
-
expect(`
|
|
59
|
-
query: q1 is ab->{ group_by: astr limit: 10 }
|
|
60
|
-
query: q2 is q1
|
|
61
|
-
`).toTranslate();
|
|
62
|
-
});
|
|
63
|
-
test('query with refinements from query', () => {
|
|
64
|
-
expect(`
|
|
65
|
-
query: q1 is ab->{ group_by: astr limit: 10 }
|
|
66
|
-
query: q2 is q1 + { aggregate: acount }
|
|
67
|
-
`).toTranslate();
|
|
68
|
-
});
|
|
69
|
-
test('chained query operations', () => {
|
|
70
|
-
expect(`
|
|
71
|
-
run: ab
|
|
72
|
-
-> { group_by: astr; aggregate: acount }
|
|
73
|
-
-> { top: 5; where: astr ~ 'a%' group_by: astr }
|
|
74
|
-
`).toTranslate();
|
|
75
|
-
});
|
|
76
|
-
test('query output refined into another query', () => {
|
|
77
|
-
expect('run: ab -> {group_by: astr} extend { dimension: bigstr is upper(astr) } -> { group_by: bigstr }').toTranslate();
|
|
78
|
-
});
|
|
79
|
-
test('query with shortcut filtered turtle', () => {
|
|
80
|
-
expect(`##! -m4warnings
|
|
81
|
-
query: allA is ab -> aturtle + {where: astr ~ 'a%' }`).toTranslate();
|
|
82
|
-
});
|
|
83
|
-
test('query with filtered turtle', () => {
|
|
84
|
-
expect("query: allA is ab -> aturtle + { where: astr ~ 'a%' }").toTranslate();
|
|
85
|
-
});
|
|
86
|
-
test('nest: in group_by:', () => {
|
|
87
|
-
expect(`
|
|
88
|
-
run: ab -> {
|
|
89
|
-
group_by: astr;
|
|
90
|
-
nest: nested_count is {
|
|
91
|
-
aggregate: acount
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
`).toTranslate();
|
|
95
|
-
});
|
|
96
|
-
test('reduce pipe project', () => {
|
|
97
|
-
expect(`
|
|
98
|
-
run: a -> { aggregate: f is count() } -> { select: f2 is f + 1 }
|
|
99
|
-
`).toTranslate();
|
|
100
|
-
});
|
|
101
|
-
test('refine and extend query', () => {
|
|
102
|
-
expect(`
|
|
103
|
-
query: a_by_str is a -> { group_by: astr }
|
|
104
|
-
run: a_by_str + { aggregate: str_count is count() }
|
|
105
|
-
`).toTranslate();
|
|
106
|
-
});
|
|
107
|
-
test('query refinement preserves original', () => {
|
|
108
|
-
const x = new test_translator_1.TestTranslator(`
|
|
109
|
-
query: q is a -> { aggregate: acount is count() }
|
|
110
|
-
query: nq is q + { group_by: astr }
|
|
111
|
-
`);
|
|
112
|
-
expect(x).toTranslate();
|
|
113
|
-
const q = getFirstSegmentFields(x.getQuery('q'));
|
|
114
|
-
expect(q).toBeDefined();
|
|
115
|
-
if (q) {
|
|
116
|
-
expect(q.length).toBe(1);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
test('query composition preserves original', () => {
|
|
120
|
-
const x = new test_translator_1.TestTranslator(`
|
|
121
|
-
query: q is ab -> { aggregate: acount }
|
|
122
|
-
query: nq is q -> { select: * }
|
|
123
|
-
`);
|
|
124
|
-
expect(x).toTranslate();
|
|
125
|
-
const q = x.getQuery('q');
|
|
126
|
-
expect(q).toBeDefined();
|
|
127
|
-
if (q) {
|
|
128
|
-
expect(q.pipeline.length).toBe(1);
|
|
129
|
-
}
|
|
130
|
-
});
|
|
131
|
-
test('all ungroup with args', () => {
|
|
132
|
-
expect(`
|
|
133
|
-
run: a -> {
|
|
134
|
-
group_by: astr
|
|
135
|
-
nest: by_int is {
|
|
136
|
-
group_by: ai
|
|
137
|
-
aggregate: bi_count is all(count(), ai)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
`).toTranslate();
|
|
141
|
-
});
|
|
142
|
-
test('all ungroup checks args', () => {
|
|
143
|
-
expect(`
|
|
144
|
-
run: a -> {
|
|
145
|
-
group_by: astr
|
|
146
|
-
nest: by_int is {
|
|
147
|
-
group_by: ai
|
|
148
|
-
aggregate: bi_count is all(count(), afloat)
|
|
149
|
-
}
|
|
150
|
-
}`).toLog((0, test_translator_1.errorMessage)("all() 'afloat' is missing from query output"));
|
|
151
|
-
});
|
|
152
|
-
test('exclude ungroup with args', () => {
|
|
153
|
-
expect(`
|
|
154
|
-
run: a -> {
|
|
155
|
-
group_by: aa is 'a'
|
|
156
|
-
nest: by_b is {
|
|
157
|
-
group_by: bb is 'b'
|
|
158
|
-
nest: by_c is {
|
|
159
|
-
group_by: cc is 'c'
|
|
160
|
-
aggregate: bb_count is exclude(count(), aa, cc)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
`).toTranslate();
|
|
165
|
-
});
|
|
166
|
-
test('exclude ungroup checks args', () => {
|
|
167
|
-
expect(`
|
|
168
|
-
run: a -> {
|
|
169
|
-
group_by: aa is 'a'
|
|
170
|
-
nest: by_b is {
|
|
171
|
-
group_by: bb is 'b'
|
|
172
|
-
nest: by_c is {
|
|
173
|
-
group_by: cc is 'c'
|
|
174
|
-
aggregate: bb_count is exclude(count(), aaa, cc)
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
`).toLog((0, test_translator_1.errorMessage)("exclude() 'aaa' is missing from query output"));
|
|
179
|
-
});
|
|
180
|
-
test('exclude problem revealed by production models', () => {
|
|
181
|
-
expect(`
|
|
182
|
-
source: carriers is _db_.table('malloytest.carriers') extend {
|
|
183
|
-
primary_key: code
|
|
184
|
-
}
|
|
185
|
-
source: flights is _db_.table('malloytest.flights') extend {
|
|
186
|
-
primary_key: id2
|
|
187
|
-
join_one: carriers with carrier
|
|
188
|
-
|
|
189
|
-
view: carrier_overview is {
|
|
190
|
-
group_by: carrier_name is carriers.nickname
|
|
191
|
-
nest: top_destinations is {
|
|
192
|
-
group_by: destination
|
|
193
|
-
aggregate:
|
|
194
|
-
flights_to_dest is exclude(count(), carrier_name)*100
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
`).toTranslate();
|
|
199
|
-
});
|
|
200
|
-
test('exclude output checking survives refinement', () => {
|
|
201
|
-
// This was https://github.com/malloydata/malloy/issues/1474
|
|
202
|
-
const nestExclude = (0, test_translator_1.model) `
|
|
203
|
-
source: flights is a extend {
|
|
204
|
-
dimension: carrier is astr, destination is astr
|
|
205
|
-
measure: flight_count is count()
|
|
206
|
-
view: by_dest is {
|
|
207
|
-
group_by: destination
|
|
208
|
-
aggregate: flight_count
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
run: flights -> {
|
|
212
|
-
group_by: carrier
|
|
213
|
-
nest: broken is by_dest + {
|
|
214
|
-
top: 5
|
|
215
|
-
aggregate: flights_to_dest_all_carriers is exclude(flight_count, carrier)
|
|
216
|
-
}
|
|
217
|
-
}`;
|
|
218
|
-
expect(nestExclude).toTranslate();
|
|
219
|
-
});
|
|
220
|
-
});
|
|
221
|
-
describe('query operation typechecking', () => {
|
|
222
|
-
describe('field declarations', () => {
|
|
223
|
-
test('cannot use aggregate in group_by', () => {
|
|
224
|
-
expect('run: a -> { group_by: s is count()}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
|
|
225
|
-
});
|
|
226
|
-
test('cannot use ungrouped_aggregate in group_by', () => {
|
|
227
|
-
expect('run: a -> { group_by: s is all(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
|
|
228
|
-
});
|
|
229
|
-
test('cannot use analytic in group_by', () => {
|
|
230
|
-
expect('run: a -> { group_by: s is row_number()}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a group_by operation, did you mean to use a calculate operation instead?'));
|
|
231
|
-
});
|
|
232
|
-
test('cannot use aggregate in dimension', () => {
|
|
233
|
-
expect('source: a1 is a extend { dimension: s is count()}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'));
|
|
234
|
-
});
|
|
235
|
-
test('cannot use ungrouped_aggregate in dimension', () => {
|
|
236
|
-
expect('source: a1 is a extend { dimension: s is all(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?'));
|
|
237
|
-
});
|
|
238
|
-
test('cannot use analytic in dimension', () => {
|
|
239
|
-
expect('source: a1 is a extend { dimension: s is row_number()}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a dimension declaration'));
|
|
240
|
-
});
|
|
241
|
-
test('cannot use scalar in measure', () => {
|
|
242
|
-
expect('source: a1 is a extend { measure: s is 1}').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?'));
|
|
243
|
-
});
|
|
244
|
-
test('cannot use analytic in measure', () => {
|
|
245
|
-
expect('source: a1 is a extend { measure: s is lag(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a measure declaration'));
|
|
246
|
-
});
|
|
247
|
-
test('cannot use scalar in aggregate', () => {
|
|
248
|
-
expect('run: a -> { aggregate: s is 1}').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?'));
|
|
249
|
-
});
|
|
250
|
-
test('cannot use analytic in aggregate', () => {
|
|
251
|
-
expect('run: a -> { aggregate: s is lag(count())}').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?'));
|
|
252
|
-
});
|
|
253
|
-
test('cannot use scalar in calculate', () => {
|
|
254
|
-
expect('run: a -> { group_by: a is 1; calculate: s is 1 }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
|
|
255
|
-
});
|
|
256
|
-
test('cannot use aggregate in calculate', () => {
|
|
257
|
-
expect('run: a -> { group_by: a is 1; calculate: s is count() }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
|
|
258
|
-
});
|
|
259
|
-
test('cannot use aggregate in project', () => {
|
|
260
|
-
expect('run: a -> { select: s is count() }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a select operation, did you mean to use an aggregate operation instead?'));
|
|
261
|
-
});
|
|
262
|
-
test('cannot use analytic in project', () => {
|
|
263
|
-
expect('run: a -> { select: s is row_number() }').toLog((0, test_translator_1.errorMessage)('Cannot use an analytic field in a select operation, did you mean to use a calculate operation instead?'));
|
|
264
|
-
});
|
|
265
|
-
test('cannot use analytic in extended source', () => {
|
|
266
|
-
expect(`##! -m4warnings
|
|
267
|
-
run: a -> { group_by: a is 1; declare: s is row_number() }`).toLog((0, test_translator_1.errorMessage)('Analytic expressions can not be used in a declare block'));
|
|
268
|
-
});
|
|
269
|
-
test('cannot use aggregate in index', () => {
|
|
270
|
-
expect('run: a extend { measure: acount is count() } -> { index: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in an index operation'));
|
|
271
|
-
});
|
|
272
|
-
test('can use aggregate in except', () => {
|
|
273
|
-
expect(`
|
|
274
|
-
source: b1 is a extend { measure: acount is count() }
|
|
275
|
-
source: c1 is b1 extend { except: acount }
|
|
276
|
-
`).toTranslate();
|
|
277
|
-
});
|
|
278
|
-
});
|
|
279
|
-
describe('field references', () => {
|
|
280
|
-
test('cannot use aggregate in group_by', () => {
|
|
281
|
-
expect('run: a -> { extend: {measure: acount is count()} group_by: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?'));
|
|
282
|
-
});
|
|
283
|
-
test('cannot use query in group_by', () => {
|
|
284
|
-
expect('run: a extend { view: q is { group_by: x is 1 } } -> { group_by: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a group_by operation, did you mean to use a nest operation instead?'));
|
|
285
|
-
});
|
|
286
|
-
test('cannot use scalar in aggregate', () => {
|
|
287
|
-
expect('run: a -> { extend: {dimension: aconst is 1} aggregate: aconst }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or select operation instead?'));
|
|
288
|
-
});
|
|
289
|
-
test('cannot use scalar in calculate', () => {
|
|
290
|
-
expect('run: a -> { extend: {dimension: aconst is 1} group_by: x is 1; calculate: aconst }').toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
|
|
291
|
-
});
|
|
292
|
-
test('cannot use aggregate in calculate', () => {
|
|
293
|
-
expect('run: a -> { extend: {measure: acount is count()} group_by: x is 1; calculate: acount }').toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
|
|
294
|
-
});
|
|
295
|
-
test('cannot use query in project', () => {
|
|
296
|
-
expect('run: a extend { view: q is { group_by: x is 1 } } -> { select: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a select operation, did you mean to use a nest operation instead?'));
|
|
297
|
-
});
|
|
298
|
-
test('cannot use query in index', () => {
|
|
299
|
-
expect('run: a extend { view: q is { group_by: x is 1 } } -> { index: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in an index operation'));
|
|
300
|
-
});
|
|
301
|
-
test('cannot use query in calculate', () => {
|
|
302
|
-
expect('run: a extend { view: q is { group_by: x is 1 } } -> { group_by: x is 1; calculate: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in a calculate operation, did you mean to use a nest operation instead?'));
|
|
303
|
-
});
|
|
304
|
-
test('cannot use query in aggregate', () => {
|
|
305
|
-
expect('run: a extend { view: q is { group_by: x is 1 } } -> { aggregate: q }').toLog((0, test_translator_1.errorMessage)('Cannot use a view field in an aggregate operation, did you mean to use a nest operation instead?'));
|
|
306
|
-
});
|
|
307
|
-
test('cannot use aggregate in calculate, preserved over refinement', () => {
|
|
308
|
-
expect(`query: a1 is a -> {
|
|
309
|
-
aggregate: c is count()
|
|
310
|
-
}
|
|
311
|
-
run: a1 + {
|
|
312
|
-
calculate: b is c
|
|
313
|
-
}`).toLog((0, test_translator_1.errorMessage)('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?'));
|
|
314
|
-
});
|
|
315
|
-
test('cannot use scalar in calculate, preserved over refinement', () => {
|
|
316
|
-
expect(`query: a1 is a -> {
|
|
317
|
-
group_by: c is 1
|
|
318
|
-
}
|
|
319
|
-
run: a1 + {
|
|
320
|
-
calculate: b is c
|
|
321
|
-
}`).toLog((0, test_translator_1.errorMessage)('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or select operation instead?'));
|
|
322
|
-
});
|
|
323
|
-
test('cannot use analytic in group_by, preserved over refinement', () => {
|
|
324
|
-
expect(`query: a1 is a -> {
|
|
325
|
-
group_by: c is 1
|
|
326
|
-
calculate: c2 is lag(c)
|
|
327
|
-
}
|
|
328
|
-
run: a1 + {
|
|
329
|
-
group_by: b is c2
|
|
330
|
-
}`).toLog(
|
|
331
|
-
// c2 is not defined because group_by doesn't know to look in the output space
|
|
332
|
-
(0, test_translator_1.errorMessage)("'c2' is not defined"));
|
|
333
|
-
});
|
|
334
|
-
test('cannot use analytic in order_by, preserved over refinement', () => {
|
|
335
|
-
expect(`query: a1 is a -> {
|
|
336
|
-
group_by: c is 1
|
|
337
|
-
calculate: c2 is lag(c)
|
|
338
|
-
}
|
|
339
|
-
run: a1 + {
|
|
340
|
-
order_by: c2
|
|
341
|
-
}`).toLog((0, test_translator_1.errorMessage)('Illegal order by of analytic field c2'));
|
|
342
|
-
});
|
|
343
|
-
test('cannot ungroup an ungrouped', () => {
|
|
344
|
-
expect(`query: a1 is a -> {
|
|
345
|
-
group_by: c is 1
|
|
346
|
-
aggregate: c2 is all(all(sum(ai)))
|
|
347
|
-
}`).toLog((0, test_translator_1.errorMessage)('all() expression must not already be ungrouped'));
|
|
348
|
-
});
|
|
349
|
-
test('cannot aggregate an ungrouped', () => {
|
|
350
|
-
expect(`query: a1 is a -> {
|
|
351
|
-
group_by: c is 1
|
|
352
|
-
aggregate: c2 is sum(all(sum(ai)))
|
|
353
|
-
}`).toLog((0, test_translator_1.errorMessage)('Aggregate expression cannot be aggregate'));
|
|
354
|
-
});
|
|
355
|
-
test('cannot aggregate an aggregate', () => {
|
|
356
|
-
expect(`query: a1 is a -> {
|
|
357
|
-
group_by: c is 1
|
|
358
|
-
aggregate: c2 is sum(sum(ai))
|
|
359
|
-
}`).toLog((0, test_translator_1.errorMessage)('Aggregate expression cannot be aggregate'));
|
|
360
|
-
});
|
|
361
|
-
test('can use field def in group_by, preserved over refinement', () => {
|
|
362
|
-
expect(`query: a1 is a -> {
|
|
363
|
-
group_by: c is 1
|
|
364
|
-
}
|
|
365
|
-
run: a1 + {
|
|
366
|
-
order_by: c
|
|
367
|
-
}`).toTranslate();
|
|
368
|
-
});
|
|
369
|
-
test('can use field ref in group_by, preserved over refinement', () => {
|
|
370
|
-
expect(`query: a1 is a -> {
|
|
371
|
-
group_by: c is astr
|
|
372
|
-
}
|
|
373
|
-
run: a1 + {
|
|
374
|
-
order_by: c
|
|
375
|
-
}`).toTranslate();
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
});
|
|
379
|
-
describe('function typechecking', () => {
|
|
380
|
-
test('use function correctly', () => {
|
|
381
|
-
expect(`run: a -> {
|
|
382
|
-
group_by: s is concat('a', 'b')
|
|
383
|
-
}`).toTranslate();
|
|
384
|
-
});
|
|
385
|
-
test('function incorrect case', () => {
|
|
386
|
-
expect(`run: a -> {
|
|
387
|
-
group_by: s is CONCAT('a', 'b')
|
|
388
|
-
}`).toLog((0, test_translator_1.warningMessage)("Case insensitivity for function names is deprecated, use 'concat' instead"));
|
|
389
|
-
});
|
|
390
|
-
test('function no matching overload', () => {
|
|
391
|
-
expect(`run: a -> {
|
|
392
|
-
group_by: s is floor('a', 'b')
|
|
393
|
-
}`).toLog((0, test_translator_1.errorMessage)('No matching overload for function floor(string, string)'));
|
|
394
|
-
});
|
|
395
|
-
test('unknown function', () => {
|
|
396
|
-
expect(`run: a -> {
|
|
397
|
-
group_by: s is asdfasdf()
|
|
398
|
-
}`).toLog((0, test_translator_1.errorMessage)("Unknown function 'asdfasdf'. Use 'asdfasdf!(...)' to call a SQL function directly."));
|
|
399
|
-
});
|
|
400
|
-
test('can select different overload', () => {
|
|
401
|
-
expect('run: a -> { group_by: s is concat() }').toTranslate();
|
|
402
|
-
});
|
|
403
|
-
test('can pass different expression types', () => {
|
|
404
|
-
expect(`run: a -> {
|
|
405
|
-
group_by: f1 is sqrt(1)
|
|
406
|
-
aggregate: f2 is sqrt(count())
|
|
407
|
-
aggregate: f3 is sqrt(all(count()))
|
|
408
|
-
calculate: f4 is sqrt(lag(f1))
|
|
409
|
-
calculate: f5 is sqrt(lag(count()))
|
|
410
|
-
}`).toTranslate();
|
|
411
|
-
});
|
|
412
|
-
test('function return type correct', () => {
|
|
413
|
-
expect(`run: a -> {
|
|
414
|
-
group_by: s is floor(1.2) + 1
|
|
415
|
-
}`).toTranslate();
|
|
416
|
-
});
|
|
417
|
-
test('function return type incorrect', () => {
|
|
418
|
-
expect(`run: a -> {
|
|
419
|
-
group_by: s is floor(1.2) + 'a'
|
|
420
|
-
}`).toLog((0, test_translator_1.errorMessage)("The '+' operator requires a number, not a 'string'"));
|
|
421
|
-
});
|
|
422
|
-
test('can use output value in calculate', () => {
|
|
423
|
-
expect(`run: a -> {
|
|
424
|
-
group_by: x is 1
|
|
425
|
-
calculate: s is lag(x)
|
|
426
|
-
}`).toTranslate();
|
|
427
|
-
});
|
|
428
|
-
test('cannot use output value in group_by', () => {
|
|
429
|
-
expect(`run: a -> {
|
|
430
|
-
group_by: x is 1
|
|
431
|
-
group_by: y is x
|
|
432
|
-
}`).toLog((0, test_translator_1.errorMessage)("'x' is not defined"));
|
|
433
|
-
});
|
|
434
|
-
test('lag can check that other args are constant', () => {
|
|
435
|
-
expect(`run: a -> {
|
|
436
|
-
group_by: x is 1
|
|
437
|
-
calculate: s is lag(x, 1, x)
|
|
438
|
-
}`).toLog(
|
|
439
|
-
// TODO improve this error message
|
|
440
|
-
(0, test_translator_1.errorMessage)("Parameter 3 ('default') of lag must be literal or constant, but received output"));
|
|
441
|
-
});
|
|
442
|
-
test('lag can check that other args are literal', () => {
|
|
443
|
-
expect(`run: a -> {
|
|
444
|
-
group_by: x is 1
|
|
445
|
-
calculate: s is lag(x, 1 + 1)
|
|
446
|
-
}`).toLog(
|
|
447
|
-
// TODO improve this error message
|
|
448
|
-
(0, test_translator_1.errorMessage)("Parameter 2 ('offset') of lag must be literal, but received constant"));
|
|
449
|
-
});
|
|
450
|
-
test('lag can check that other args are nonnull', () => {
|
|
451
|
-
expect(`run: a -> {
|
|
452
|
-
group_by: x is 1
|
|
453
|
-
calculate: s is lag(x, null)
|
|
454
|
-
}`).toLog((0, test_translator_1.errorMessage)("Parameter 2 ('offset') of lag must not be a literal null"));
|
|
455
|
-
});
|
|
456
|
-
test('lag can use constant values for other args', () => {
|
|
457
|
-
expect(`run: a -> {
|
|
458
|
-
group_by: x is 1
|
|
459
|
-
calculate: s is lag(x, 2)
|
|
460
|
-
}`).toTranslate();
|
|
461
|
-
});
|
|
462
|
-
test('cannot name top level objects same as functions', () => {
|
|
463
|
-
expect((0, test_translator_1.markSource) `query: ${'concat is a -> { group_by: x is 1 }'}`).toLog(
|
|
464
|
-
// TODO improve this error message
|
|
465
|
-
(0, test_translator_1.errorMessage)("'concat' is already defined, cannot redefine"));
|
|
466
|
-
});
|
|
467
|
-
test('`now` is considered constant`', () => {
|
|
468
|
-
expect(`run: a -> {
|
|
469
|
-
group_by: n is now
|
|
470
|
-
calculate: l is lag(n, 1, now)
|
|
471
|
-
}`).toTranslate();
|
|
472
|
-
});
|
|
473
|
-
// TODO it might be nice to reference a field which is a constant, and be able to
|
|
474
|
-
// use that as a constant param. Same with a literal.
|
|
475
|
-
test('cannot use a field which is a constant in a constant param', () => {
|
|
476
|
-
expect(`run: a -> {
|
|
477
|
-
group_by: ai, pi is pi()
|
|
478
|
-
calculate: l is lag(ai, 1, pi)
|
|
479
|
-
}`).toLog((0, test_translator_1.errorMessage)("Parameter 3 ('default') of lag must be literal or constant, but received output"));
|
|
480
|
-
});
|
|
481
|
-
test('cannot use struct in function arg', () => {
|
|
482
|
-
expect(`run: ab -> {
|
|
483
|
-
group_by: b.astr
|
|
484
|
-
calculate: foo is lag(b)
|
|
485
|
-
}`).toLog((0, test_translator_1.errorMessage)('No matching overload for function lag(table)'));
|
|
486
|
-
});
|
|
487
|
-
// TODO this doesn't work today, we're not rigorous enough with integer
|
|
488
|
-
// subtypes. But we should probably make this typecheck properly.
|
|
489
|
-
test.skip('cannot use float in round precision', () => {
|
|
490
|
-
expect(`run: a -> {
|
|
491
|
-
group_by: x is round(1.5, 1.6)
|
|
492
|
-
}`).toLog(
|
|
493
|
-
// TODO improve this error message
|
|
494
|
-
(0, test_translator_1.errorMessage)("Parameter 2 ('precision') for round must be integer, received float"));
|
|
495
|
-
});
|
|
496
|
-
test('cannot use stddev with no arguments', () => {
|
|
497
|
-
expect(`run: a -> {
|
|
498
|
-
aggregate: x is stddev()
|
|
499
|
-
}`).toLog((0, test_translator_1.errorMessage)('No matching overload for function stddev()'));
|
|
500
|
-
});
|
|
501
|
-
test('can use stddev with postfix syntax', () => {
|
|
502
|
-
expect(`run: a -> {
|
|
503
|
-
extend: { dimension: y is 1 }
|
|
504
|
-
aggregate: x is y.stddev()
|
|
505
|
-
}`).toTranslate();
|
|
506
|
-
});
|
|
507
|
-
test('can use stddev with postfix syntax on join', () => {
|
|
508
|
-
expect(`run: a -> {
|
|
509
|
-
extend: { join_one: b with astr }
|
|
510
|
-
aggregate: x is b.ai.stddev()
|
|
511
|
-
}`).toTranslate();
|
|
512
|
-
});
|
|
513
|
-
test('can use calculate with a measure', () => {
|
|
514
|
-
expect(`run: a extend { measure: c is count() } -> {
|
|
515
|
-
group_by: y is 1
|
|
516
|
-
calculate: x is lag(c)
|
|
517
|
-
}`).toTranslate();
|
|
518
|
-
});
|
|
519
|
-
test('cannot use calculate with input fields', () => {
|
|
520
|
-
expect(`run: a -> {
|
|
521
|
-
group_by: y is 1
|
|
522
|
-
calculate: x is lag(ai)
|
|
523
|
-
}`).toLog(
|
|
524
|
-
// TODO improve this error message:
|
|
525
|
-
// Parameter 1 ('value') of 'lag' must be a constant, an aggregate, or an expression using
|
|
526
|
-
// only fields that appear in the query output. Received an expression which uses a field
|
|
527
|
-
// that is not in the query output.
|
|
528
|
-
(0, test_translator_1.errorMessage)("Parameter 1 ('value') of lag must be literal, constant or output, but received input"));
|
|
529
|
-
});
|
|
530
|
-
test('can use calculate with aggregate field which is not in query', () => {
|
|
531
|
-
expect(`run: a extend { measure: acount is count() } -> {
|
|
532
|
-
group_by: astr
|
|
533
|
-
calculate: pc is lag(acount)
|
|
534
|
-
}`).toTranslate();
|
|
535
|
-
});
|
|
536
|
-
test('cannot use agregate as argument to agg function', () => {
|
|
537
|
-
expect(`run: a -> {
|
|
538
|
-
aggregate: x is stddev(count())
|
|
539
|
-
}`).toLog((0, test_translator_1.errorMessage)("Parameter 1 ('value') of stddev must be scalar, but received aggregate"));
|
|
540
|
-
});
|
|
541
|
-
test('cannot use calculate with no other fields', () => {
|
|
542
|
-
expect(`run: a -> {
|
|
543
|
-
calculate: x is row_number()
|
|
544
|
-
}`).toLog((0, test_translator_1.error)('ambiguous-view-type', {}));
|
|
545
|
-
});
|
|
546
|
-
// TODO someday make it so we can order by an analytic function
|
|
547
|
-
test('today: cannot order by analytic function', () => {
|
|
548
|
-
expect(`run: a -> {
|
|
549
|
-
group_by: astr
|
|
550
|
-
calculate: row_num is row_number()
|
|
551
|
-
order_by: row_num desc
|
|
552
|
-
}`).toLog((0, test_translator_1.errorMessage)('Illegal order by of analytic field row_num'));
|
|
553
|
-
});
|
|
554
|
-
test('cannot use analytic in calculate -- and preserved over refinement', () => {
|
|
555
|
-
expect(`query: a1 is a -> {
|
|
556
|
-
group_by: astr
|
|
557
|
-
calculate: p is lag(astr)
|
|
558
|
-
}
|
|
559
|
-
run: a1 + {
|
|
560
|
-
calculate: p1 is lag(p)
|
|
561
|
-
}`).toLog((0, test_translator_1.errorMessage)("Parameter 1 ('value') of lag must be scalar or aggregate, but received scalar_analytic"));
|
|
562
|
-
});
|
|
563
|
-
test('cannot use aggregate analytic in project', () => {
|
|
564
|
-
expect(`run: a -> {
|
|
565
|
-
select: astr
|
|
566
|
-
calculate: p is lag(count())
|
|
567
|
-
}`).toLog((0, test_translator_1.errorMessage)('Cannot add aggregate analyics to select'));
|
|
568
|
-
});
|
|
569
|
-
test('reference field in join', () => {
|
|
570
|
-
expect(`run: a -> {
|
|
571
|
-
extend: { join_one: b with astr }
|
|
572
|
-
group_by: b.ai
|
|
573
|
-
}`).toTranslate();
|
|
574
|
-
});
|
|
575
|
-
test('reference field in double nested join inside nest', () => {
|
|
576
|
-
expect(`
|
|
577
|
-
source: a_2 is a extend { join_one: b is b extend {
|
|
578
|
-
join_one: c is b on 1 = c.ai
|
|
579
|
-
} with astr }
|
|
580
|
-
|
|
581
|
-
run: a_2 -> {
|
|
582
|
-
nest: x is {
|
|
583
|
-
group_by: ai is b.c.ai
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
`).toTranslate();
|
|
587
|
-
});
|
|
588
|
-
test.skip('reference join as field', () => {
|
|
589
|
-
expect(`run: a -> {
|
|
590
|
-
extend: { join_one: b with astr }
|
|
591
|
-
group_by: b
|
|
592
|
-
}`).toLog((0, test_translator_1.errorMessage)('foo'));
|
|
593
|
-
});
|
|
594
|
-
test('can reference select: inline join.* field in calculate', () => {
|
|
595
|
-
expect(`run: a -> {
|
|
596
|
-
extend: { join_one: b with astr }
|
|
597
|
-
select: b.*
|
|
598
|
-
calculate: s is lag(ai)
|
|
599
|
-
}`).toTranslate();
|
|
600
|
-
});
|
|
601
|
-
test('can reference select: join.* field in calculate', () => {
|
|
602
|
-
expect(`run: a extend { join_one: b with astr } -> {
|
|
603
|
-
select: b.*
|
|
604
|
-
calculate: s is lag(ai)
|
|
605
|
-
}`).toTranslate();
|
|
606
|
-
});
|
|
607
|
-
test('can reference implied output entries in calculate', () => {
|
|
608
|
-
expect(`source: aaa is a extend { dimension: big_i is ai+10 }
|
|
609
|
-
run: a extend { join_one: aaa on astr=aaa.astr } -> {
|
|
610
|
-
group_by: aaa.big_i
|
|
611
|
-
aggregate: s is aaa.big_i.sum()
|
|
612
|
-
calculate: a is lag(big_i)
|
|
613
|
-
}`).toTranslate();
|
|
614
|
-
});
|
|
615
|
-
describe('dialect functions', () => {
|
|
616
|
-
test('can use function enabled in this dialect (standardsql)', () => {
|
|
617
|
-
expect(`run: a -> {
|
|
618
|
-
group_by: d is date_from_unix_date(1000)
|
|
619
|
-
}`).toTranslate();
|
|
620
|
-
});
|
|
621
|
-
test('cannot use function enabled in a different dialect (duckdb)', () => {
|
|
622
|
-
expect(`run: a -> {
|
|
623
|
-
group_by: ts is to_timestamp(1000)
|
|
624
|
-
}`).toLog((0, test_translator_1.errorMessage)(/Unknown function/));
|
|
625
|
-
});
|
|
626
|
-
});
|
|
627
|
-
});
|
|
628
|
-
describe('qops', () => {
|
|
629
|
-
test('group by single', () => {
|
|
630
|
-
expect('run: a->{ group_by: astr }').toTranslate();
|
|
631
|
-
});
|
|
632
|
-
test("group_by x is x'", () => {
|
|
633
|
-
expect('run: a->{ group_by: ai is ai/2 }').toTranslate();
|
|
634
|
-
});
|
|
635
|
-
test('group by multiple', () => {
|
|
636
|
-
expect('run: a->{ group_by: astr,ai }').toTranslate();
|
|
637
|
-
});
|
|
638
|
-
test('aggregate single', () => {
|
|
639
|
-
expect('run: a->{ aggregate: num is count() }').toTranslate();
|
|
640
|
-
});
|
|
641
|
-
test('calculate in reduce', () => {
|
|
642
|
-
expect('run: a->{ group_by: astr, ai calculate: num is lag(ai) }').toTranslate();
|
|
643
|
-
});
|
|
644
|
-
test('calculate in project', () => {
|
|
645
|
-
expect('run: a->{ select: astr, ai calculate: num is lag(ai) }').toTranslate();
|
|
646
|
-
});
|
|
647
|
-
test('aggregate reference', () => {
|
|
648
|
-
const doc = (0, test_translator_1.model) `run: a->{ aggregate: ai.sum() }`;
|
|
649
|
-
expect(doc).toTranslate();
|
|
650
|
-
const q = getFirstSegmentFields(doc.translator.getQuery(0));
|
|
651
|
-
expect(q).toBeDefined();
|
|
652
|
-
expect(q[0]).toMatchObject({
|
|
653
|
-
name: 'ai',
|
|
654
|
-
type: 'number',
|
|
655
|
-
expressionType: 'aggregate',
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
|
-
test('timeunit reference', () => {
|
|
659
|
-
const doc = (0, test_translator_1.model) `run: a->{ group_by: ats.day }`;
|
|
660
|
-
expect(doc).toTranslate();
|
|
661
|
-
const q = getFirstSegmentFields(doc.translator.getQuery(0));
|
|
662
|
-
expect(q).toBeDefined();
|
|
663
|
-
expect(q[0]).toMatchObject({ name: 'ats', type: 'timestamp' });
|
|
664
|
-
});
|
|
665
|
-
test('aggregate multiple', () => {
|
|
666
|
-
expect(`
|
|
667
|
-
run: a->{
|
|
668
|
-
aggregate: num is count(), total is sum(ai)
|
|
669
|
-
}
|
|
670
|
-
`).toTranslate();
|
|
671
|
-
});
|
|
672
|
-
test('project ref', () => {
|
|
673
|
-
expect('run:ab->{ select: b.astr }').toTranslate();
|
|
674
|
-
});
|
|
675
|
-
const afields = test_translator_1.aTableDef.fields
|
|
676
|
-
.filter(f => (0, model_1.isAtomic)(f))
|
|
677
|
-
.map(f => f.name)
|
|
678
|
-
.sort();
|
|
679
|
-
test('expands star correctly', () => {
|
|
680
|
-
const selstar = (0, test_translator_1.model) `run: ab->{select: *}`;
|
|
681
|
-
expect(selstar).toTranslate();
|
|
682
|
-
const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
|
|
683
|
-
expect(fields).toEqual(afields);
|
|
684
|
-
});
|
|
685
|
-
test('expands join dot star correctly', () => {
|
|
686
|
-
const selstar = (0, test_translator_1.model) `run: ab->{select: b.*}`;
|
|
687
|
-
expect(selstar).toTranslate();
|
|
688
|
-
const query = selstar.translator.getQuery(0);
|
|
689
|
-
expect(query).toBeDefined();
|
|
690
|
-
const fields = getFirstSegmentFields(selstar.translator.getQuery(0)).map(f => (f.type === 'fieldref' ? f.path : `wrong field type ${f.type}`));
|
|
691
|
-
expect(fields).toEqual(afields.map(f => ['b', f]));
|
|
692
|
-
});
|
|
693
|
-
test('expands star with exclusions', () => {
|
|
694
|
-
const selstar = (0, test_translator_1.model) `run: ab->{select: * { except: ai, except: aun, aweird }}`;
|
|
695
|
-
const filterdFields = afields.filter(f => f !== 'ai' && f !== 'aun' && f !== 'aweird');
|
|
696
|
-
expect(selstar).toTranslate();
|
|
697
|
-
const fields = getFirstSegmentFieldNames(selstar.translator.getQuery(0));
|
|
698
|
-
expect(fields).toEqual(filterdFields);
|
|
699
|
-
});
|
|
700
|
-
test('array in query is passed into fields', () => {
|
|
701
|
-
const selArray = (0, test_translator_1.model) `run: a -> { select: ais }`;
|
|
702
|
-
expect(selArray).toTranslate();
|
|
703
|
-
const fields = getFirstSegmentFieldNames(selArray.translator.getQuery(0));
|
|
704
|
-
expect(fields).toEqual(['ais']);
|
|
705
|
-
});
|
|
706
|
-
test('star error checking', () => {
|
|
707
|
-
expect((0, test_translator_1.markSource) `run: a->{select: ${'zzz'}.*}`).toLog((0, test_translator_1.errorMessage)("No such field as 'zzz'"));
|
|
708
|
-
expect((0, test_translator_1.markSource) `run: ab->{select: b.${'zzz'}.*}`).toLog((0, test_translator_1.errorMessage)("No such field as 'zzz'"));
|
|
709
|
-
expect((0, test_translator_1.markSource) `run: a->{select: ${'ai'}.*}`).toLog((0, test_translator_1.errorMessage)("Field 'ai' does not contain rows and cannot be expanded with '*'"));
|
|
710
|
-
expect((0, test_translator_1.markSource) `run: a->{select:ai,${'*'}}`).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in '*' because a field with that name already exists"));
|
|
711
|
-
expect((0, test_translator_1.markSource) `run: ab->{select:ai,${'b.*'}}`).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in 'b.*' because a field with that name already exists"));
|
|
712
|
-
const m = `
|
|
713
|
-
source: nab is a extend {
|
|
714
|
-
accept: ai
|
|
715
|
-
join_one: b is a extend {accept: ai} on ai=b.ai
|
|
716
|
-
}
|
|
717
|
-
run: nab->{select: b.*,*}
|
|
718
|
-
`;
|
|
719
|
-
expect(m).toLog((0, test_translator_1.errorMessage)("Cannot expand 'ai' in '*' because a field with that name already exists (conflicts with b.ai)"));
|
|
720
|
-
});
|
|
721
|
-
test('regress check extend: and star', () => {
|
|
722
|
-
const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
|
|
723
|
-
expect(m).toTranslate();
|
|
724
|
-
const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
|
|
725
|
-
expect(fields).toEqual(afields.concat('x'));
|
|
726
|
-
});
|
|
727
|
-
test('project def', () => {
|
|
728
|
-
expect('run: ab->{ select: one is 1 }').toTranslate();
|
|
729
|
-
});
|
|
730
|
-
test('project multiple', () => {
|
|
731
|
-
expect('run: a->{ select: one is 1, astr }').toTranslate();
|
|
732
|
-
});
|
|
733
|
-
test('index single', () => { });
|
|
734
|
-
test('regress check extend: and star', () => {
|
|
735
|
-
const m = (0, test_translator_1.model) `run: ab->{ extend: {dimension: x is 1} select: * }`;
|
|
736
|
-
expect(m).toTranslate();
|
|
737
|
-
const fields = getFirstSegmentFieldNames(m.translator.getQuery(0));
|
|
738
|
-
expect(fields).toEqual(afields.concat('x'));
|
|
739
|
-
});
|
|
740
|
-
test('project def', () => {
|
|
741
|
-
expect('run:ab->{ select: one is 1 }').toTranslate();
|
|
742
|
-
});
|
|
743
|
-
test('project multiple', () => {
|
|
744
|
-
expect(`
|
|
745
|
-
run: a->{
|
|
746
|
-
select: one is 1, astr
|
|
747
|
-
}
|
|
748
|
-
`).toTranslate();
|
|
749
|
-
});
|
|
750
|
-
test('index single', () => {
|
|
751
|
-
expect('run:a->{index: astr}').toTranslate();
|
|
752
|
-
});
|
|
753
|
-
test('index path', () => {
|
|
754
|
-
expect('run:ab->{index: b.astr}').toTranslate();
|
|
755
|
-
});
|
|
756
|
-
test('index unique on path', () => {
|
|
757
|
-
expect('run:ab->{index: astr, b.astr}').toTranslate();
|
|
758
|
-
});
|
|
759
|
-
test('index join.*', () => {
|
|
760
|
-
expect('run:ab->{index: b.*}').toTranslate();
|
|
761
|
-
});
|
|
762
|
-
test('index multiple', () => {
|
|
763
|
-
const model = new test_translator_1.TestTranslator('run:a->{index: af, astr}');
|
|
764
|
-
expect(model).toTranslate();
|
|
765
|
-
const q = model.getQuery(0);
|
|
766
|
-
expect(q).toBeDefined();
|
|
767
|
-
if (q) {
|
|
768
|
-
const index = q.pipeline[0];
|
|
769
|
-
expect(index.type).toBe('index');
|
|
770
|
-
if (index.type === 'index') {
|
|
771
|
-
expect(index.indexFields).toEqual([
|
|
772
|
-
{ type: 'fieldref', path: ['af'] },
|
|
773
|
-
{ type: 'fieldref', path: ['astr'] },
|
|
774
|
-
]);
|
|
775
|
-
}
|
|
776
|
-
}
|
|
777
|
-
});
|
|
778
|
-
test('index by', () => {
|
|
779
|
-
expect('run:a->{index: * by ai}').toTranslate();
|
|
780
|
-
});
|
|
781
|
-
test('index sampled', () => {
|
|
782
|
-
expect('run:a->{index: *; sample: true}').toTranslate();
|
|
783
|
-
});
|
|
784
|
-
test('index unsampled', () => {
|
|
785
|
-
expect('run:a->{index: *; sample: false}').toTranslate();
|
|
786
|
-
});
|
|
787
|
-
test('index sample-percent', () => {
|
|
788
|
-
const model = new test_translator_1.TestTranslator('run:a->{index: *; sample: 42%}');
|
|
789
|
-
expect(model).toTranslate();
|
|
790
|
-
const q = model.getQuery(0);
|
|
791
|
-
expect(q).toBeDefined();
|
|
792
|
-
if (q) {
|
|
793
|
-
const index = q.pipeline[0];
|
|
794
|
-
expect(index.type).toBe('index');
|
|
795
|
-
if (index.type === 'index') {
|
|
796
|
-
expect(index.sample).toEqual({ percent: 42 });
|
|
797
|
-
}
|
|
798
|
-
}
|
|
799
|
-
});
|
|
800
|
-
test('index sample-rows', () => {
|
|
801
|
-
expect('run:a->{index: *; sample: 100000}').toTranslate();
|
|
802
|
-
});
|
|
803
|
-
test('top N', () => {
|
|
804
|
-
expect('run: a->{ top: 5; group_by: astr }').toTranslate();
|
|
805
|
-
});
|
|
806
|
-
test('limit N', () => {
|
|
807
|
-
expect('run: a->{ limit: 5; group_by: astr }').toTranslate();
|
|
808
|
-
});
|
|
809
|
-
describe('order by variations', () => {
|
|
810
|
-
test('order by', () => {
|
|
811
|
-
expect('run: a->{ order_by: astr; group_by: astr }').toTranslate();
|
|
812
|
-
});
|
|
813
|
-
test('order by preserved over refinement', () => {
|
|
814
|
-
expect(`
|
|
815
|
-
query: a1 is a -> { group_by: astr }
|
|
816
|
-
run: a1 + { order_by: astr }
|
|
817
|
-
`).toTranslate();
|
|
818
|
-
});
|
|
819
|
-
test('order by must be in the output space', () => expect('run: a -> { order_by: af; group_by: astr }').toLog((0, test_translator_1.errorMessage)('Unknown field af in output space')));
|
|
820
|
-
test('order by asc', () => {
|
|
821
|
-
expect('run: a->{ order_by: astr asc; group_by: astr }').toTranslate();
|
|
822
|
-
});
|
|
823
|
-
test('order by desc', () => {
|
|
824
|
-
expect('run: a->{ order_by: astr desc; group_by: astr }').toTranslate();
|
|
825
|
-
});
|
|
826
|
-
test('order by N', () => {
|
|
827
|
-
expect('run: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
|
|
828
|
-
});
|
|
829
|
-
test('first aggregate used for default ordering', () => {
|
|
830
|
-
const m = (0, test_translator_1.model) `run: a->{
|
|
831
|
-
group_by: astr
|
|
832
|
-
aggregate: t is ai.sum()
|
|
833
|
-
}`;
|
|
834
|
-
expect(m).toTranslate();
|
|
835
|
-
const runStmt = m.translator.getQuery(0);
|
|
836
|
-
expect(runStmt).toBeDefined();
|
|
837
|
-
const reduce = runStmt.pipeline[0];
|
|
838
|
-
expect(reduce.type).toEqual('reduce');
|
|
839
|
-
if (reduce.type === 'reduce') {
|
|
840
|
-
expect(reduce.defaultOrderBy).toBeTruthy();
|
|
841
|
-
expect(reduce.orderBy).toEqual([{ field: 't', dir: 'desc' }]);
|
|
842
|
-
}
|
|
843
|
-
});
|
|
844
|
-
test('first temporal used for default ordering', () => {
|
|
845
|
-
const m = (0, test_translator_1.model) `run: a->{
|
|
846
|
-
group_by: astr, ats
|
|
847
|
-
}`;
|
|
848
|
-
expect(m).toTranslate();
|
|
849
|
-
const runStmt = m.translator.getQuery(0);
|
|
850
|
-
expect(runStmt).toBeDefined();
|
|
851
|
-
const reduce = runStmt.pipeline[0];
|
|
852
|
-
expect(reduce.type).toEqual('reduce');
|
|
853
|
-
if (reduce.type === 'reduce') {
|
|
854
|
-
expect(reduce.defaultOrderBy).toBeTruthy();
|
|
855
|
-
expect(reduce.orderBy).toEqual([{ field: 'ats', dir: 'desc' }]);
|
|
856
|
-
}
|
|
857
|
-
});
|
|
858
|
-
test('first used for ordering when appropriate', () => {
|
|
859
|
-
const m = (0, test_translator_1.model) `run: a->{
|
|
860
|
-
group_by: astr, big is upper(astr)
|
|
861
|
-
}`;
|
|
862
|
-
expect(m).toTranslate();
|
|
863
|
-
const runStmt = m.translator.getQuery(0);
|
|
864
|
-
expect(runStmt).toBeDefined();
|
|
865
|
-
const reduce = runStmt.pipeline[0];
|
|
866
|
-
expect(reduce.type).toEqual('reduce');
|
|
867
|
-
if (reduce.type === 'reduce') {
|
|
868
|
-
expect(reduce.defaultOrderBy).toBeTruthy();
|
|
869
|
-
expect(reduce.orderBy).toEqual([{ field: 'astr', dir: 'asc' }]);
|
|
870
|
-
}
|
|
871
|
-
});
|
|
872
|
-
});
|
|
873
|
-
test('order by multiple', () => {
|
|
874
|
-
expect(`
|
|
875
|
-
run: a->{
|
|
876
|
-
order_by: 1 asc, af desc
|
|
877
|
-
group_by: astr, af
|
|
878
|
-
}
|
|
879
|
-
`).toTranslate();
|
|
880
|
-
});
|
|
881
|
-
test('agg cannot be used in where', () => {
|
|
882
|
-
expect('run:ab->{ aggregate: acount; group_by: astr; where: acount > 10 }').toLog((0, test_translator_1.errorMessage)('Aggregate expressions are not allowed in `where:`; use `having:`'));
|
|
883
|
-
});
|
|
884
|
-
test('analytic cannot be used in where', () => {
|
|
885
|
-
expect('run:ab->{ calculate: prevc is lag(count()); group_by: astr; where: prevc > 10 }').toLog((0, test_translator_1.errorMessage)("'prevc' is not defined"));
|
|
886
|
-
});
|
|
887
|
-
test('analytic cannot be used in having', () => {
|
|
888
|
-
expect('run:ab->{ calculate: prevc is lag(count()); group_by: astr; having: prevc > 10 }').toLog((0, test_translator_1.errorMessage)('Analytic expressions are not allowed in `having:`'));
|
|
889
|
-
});
|
|
890
|
-
test('where single', () => {
|
|
891
|
-
expect('run:a->{ group_by: astr; where: af > 10 }').toTranslate();
|
|
892
|
-
});
|
|
893
|
-
test('having single', () => {
|
|
894
|
-
expect('run:ab->{ aggregate: acount; group_by: astr; having: acount > 10 }').toTranslate();
|
|
895
|
-
});
|
|
896
|
-
test('compound having still works', () => {
|
|
897
|
-
expect('run:ab->{ aggregate: acount; having: acount > 10 and acount < 100 }').toTranslate();
|
|
898
|
-
});
|
|
899
|
-
test('compound aggregate still works', () => {
|
|
900
|
-
expect('run:ab->{ aggregate: thing is acount > 10 and acount < 100 }').toTranslate();
|
|
901
|
-
});
|
|
902
|
-
test('where multiple', () => {
|
|
903
|
-
expect("run:a->{ group_by: astr; where: af > 10,astr~'a%' }").toTranslate();
|
|
904
|
-
});
|
|
905
|
-
test('filters preserve source formatting in code:', () => {
|
|
906
|
-
const model = new test_translator_1.TestTranslator("source: notb is a extend { where: astr != 'b' }");
|
|
907
|
-
expect(model).toTranslate();
|
|
908
|
-
const notb = model.getSourceDef('notb');
|
|
909
|
-
expect(notb).toBeDefined();
|
|
910
|
-
if (notb) {
|
|
911
|
-
const f = notb.filterList;
|
|
912
|
-
expect(f).toBeDefined();
|
|
913
|
-
if (f) {
|
|
914
|
-
expect(f[0].code).toBe("astr != 'b'");
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
});
|
|
918
|
-
test('field expressions preserve source formatting in code:', () => {
|
|
919
|
-
const model = new test_translator_1.TestTranslator('source: notb is a extend { dimension: d is 1 + 2 }');
|
|
920
|
-
expect(model).toTranslate();
|
|
921
|
-
const notb = model.getSourceDef('notb');
|
|
922
|
-
expect(notb).toBeDefined();
|
|
923
|
-
if (notb) {
|
|
924
|
-
const d = notb.fields.find(f => f.as || f.name === 'd');
|
|
925
|
-
expect(d).toBeDefined();
|
|
926
|
-
expect(d === null || d === void 0 ? void 0 : d.type).toBe('number');
|
|
927
|
-
if ((d === null || d === void 0 ? void 0 : d.type) === 'number') {
|
|
928
|
-
expect(d.code).toBe('1 + 2');
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
});
|
|
932
|
-
test('nest single', () => {
|
|
933
|
-
expect(`
|
|
934
|
-
run: a->{
|
|
935
|
-
group_by: ai
|
|
936
|
-
nest: nestbystr is { group_by: astr; aggregate: N is count() }
|
|
937
|
-
}
|
|
938
|
-
`).toTranslate();
|
|
939
|
-
});
|
|
940
|
-
test('nest multiple', () => expect(`
|
|
941
|
-
run: a->{
|
|
942
|
-
group_by: ai
|
|
943
|
-
nest:
|
|
944
|
-
nestbystr is { group_by: astr; aggregate: N is count() },
|
|
945
|
-
renest is { group_by: astr; aggregate: N is count() }
|
|
946
|
-
}
|
|
947
|
-
`).toTranslate());
|
|
948
|
-
test('nest ref', () => expect('run: ab->{group_by: ai; nest: aturtle}').toTranslate());
|
|
949
|
-
describe('extend block', () => {
|
|
950
|
-
test('works with dimension', () => {
|
|
951
|
-
expect('run: a -> { extend: { dimension: x is 1 }; group_by: x }').toTranslate();
|
|
952
|
-
});
|
|
953
|
-
test('works with measure', () => {
|
|
954
|
-
expect('run: a -> { extend: { measure: x is count() }; aggregate: x }').toTranslate();
|
|
955
|
-
});
|
|
956
|
-
test('works with join_one', () => {
|
|
957
|
-
expect('run: a -> { extend: { join_one: bb is b on bb.astr = astr }; group_by: bb.astr }').toTranslate();
|
|
958
|
-
});
|
|
959
|
-
test('works with join_many', () => {
|
|
960
|
-
expect('run: a -> { extend: { join_many: b on astr = b.astr }; group_by: b.astr }').toTranslate();
|
|
961
|
-
});
|
|
962
|
-
test('works with join_cross', () => {
|
|
963
|
-
expect('run: a -> { extend: { join_cross: b on true }; group_by: b.astr }').toTranslate();
|
|
964
|
-
});
|
|
965
|
-
test('works with multiple in one block', () => {
|
|
966
|
-
expect('run: a -> { extend: { dimension: x is 1, y is 2 }; group_by: x, y }').toTranslate();
|
|
967
|
-
});
|
|
968
|
-
test('works with multiple blocks', () => {
|
|
969
|
-
expect('run: a -> { extend: { dimension: x is 1; dimension: y is 2; measure: c is count() }; group_by: x, y; aggregate: c }').toTranslate();
|
|
970
|
-
});
|
|
971
|
-
});
|
|
972
|
-
describe('declare/query join warnings', () => {
|
|
973
|
-
test('declare warning in query', () => {
|
|
974
|
-
expect((0, test_translator_1.markSource) `##! m4warnings=warn
|
|
975
|
-
run: a -> { ${'declare: x is 1'}; group_by: x }`).toLog((0, test_translator_1.warningMessage)('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block'));
|
|
976
|
-
});
|
|
977
|
-
test('declare warning in source', () => {
|
|
978
|
-
expect((0, test_translator_1.markSource) `##! m4warnings=warn
|
|
979
|
-
source: a2 is a extend { ${'declare: x is 1'} }`).toLog((0, test_translator_1.warningMessage)('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block'));
|
|
980
|
-
});
|
|
981
|
-
test('joins in query', () => {
|
|
982
|
-
expect((0, test_translator_1.markSource) `
|
|
983
|
-
run: a -> {
|
|
984
|
-
join_one: b on true
|
|
985
|
-
join_many: c is b on true
|
|
986
|
-
join_cross: d is b on true
|
|
987
|
-
group_by: b.astr
|
|
988
|
-
}`).toTranslate();
|
|
989
|
-
});
|
|
990
|
-
});
|
|
991
|
-
test('refine query with extended source', () => {
|
|
992
|
-
const m = new test_translator_1.TestTranslator(`
|
|
993
|
-
run: ab extend {
|
|
994
|
-
view: xturtle is aturtle + {
|
|
995
|
-
extend: { measure: aratio is ai / acount }
|
|
996
|
-
}
|
|
997
|
-
} -> xturtle + { aggregate: aratio }
|
|
998
|
-
`);
|
|
999
|
-
expect(m).toTranslate();
|
|
1000
|
-
const t = m.translate();
|
|
1001
|
-
if (t.modelDef) {
|
|
1002
|
-
const q = t.modelDef.queryList[0].pipeline[0];
|
|
1003
|
-
expect(q).toBeDefined();
|
|
1004
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1005
|
-
expect(q.extendSource.length).toBe(1);
|
|
1006
|
-
const f = q.extendSource[0];
|
|
1007
|
-
expect(f.type).toBe('number');
|
|
1008
|
-
if (f.type === 'number') {
|
|
1009
|
-
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
else {
|
|
1013
|
-
fail('Did not generate extendSource');
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
});
|
|
1017
|
-
test('refine query source with field', () => {
|
|
1018
|
-
const m = new test_translator_1.TestTranslator(`
|
|
1019
|
-
run: ab -> aturtle + {
|
|
1020
|
-
extend: { measure: aratio is ai / acount }
|
|
1021
|
-
aggregate: aratio
|
|
1022
|
-
}
|
|
1023
|
-
`);
|
|
1024
|
-
expect(m).toTranslate();
|
|
1025
|
-
const t = m.translate();
|
|
1026
|
-
if (t.modelDef) {
|
|
1027
|
-
const q = t.modelDef.queryList[0].pipeline[0];
|
|
1028
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1029
|
-
expect(q.extendSource.length).toBe(1);
|
|
1030
|
-
const f = q.extendSource[0];
|
|
1031
|
-
expect(f.type).toBe('number');
|
|
1032
|
-
if (f.type === 'number') {
|
|
1033
|
-
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
else {
|
|
1037
|
-
fail('Did not generate extendSource');
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
});
|
|
1041
|
-
test('refine query source with join', () => {
|
|
1042
|
-
const m = new test_translator_1.TestTranslator(`
|
|
1043
|
-
run: ab -> aturtle + {
|
|
1044
|
-
extend: { join_one: bb is b on bb.astr = astr }
|
|
1045
|
-
group_by: foo is bb.astr
|
|
1046
|
-
}
|
|
1047
|
-
`);
|
|
1048
|
-
expect(m).toTranslate();
|
|
1049
|
-
const t = m.translate();
|
|
1050
|
-
if (t.modelDef) {
|
|
1051
|
-
const q = t.modelDef.queryList[0].pipeline[0];
|
|
1052
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1053
|
-
expect(q.extendSource.length).toBe(1);
|
|
1054
|
-
expect((0, model_1.isJoined)(q.extendSource[0])).toBeTruthy();
|
|
1055
|
-
expect(q.extendSource[0].type).toBe('table');
|
|
1056
|
-
}
|
|
1057
|
-
else {
|
|
1058
|
-
fail('Did not generate extendSource');
|
|
1059
|
-
}
|
|
1060
|
-
}
|
|
1061
|
-
});
|
|
1062
|
-
});
|
|
1063
|
-
describe('refinement location rules', () => {
|
|
1064
|
-
test('where clauses go into the first segment', () => {
|
|
1065
|
-
const doc = (0, test_translator_1.model) `
|
|
1066
|
-
query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1067
|
-
query: checkme is refineme + { where: astr = 'a' }`;
|
|
1068
|
-
expect(doc).toTranslate();
|
|
1069
|
-
const checkme = doc.translator.getQuery('checkme');
|
|
1070
|
-
expect(checkme).toBeDefined();
|
|
1071
|
-
if (checkme) {
|
|
1072
|
-
const whereClause = checkme.pipeline[0].filterList;
|
|
1073
|
-
expect(whereClause).toBeDefined();
|
|
1074
|
-
if (whereClause) {
|
|
1075
|
-
expect(whereClause.length).toBe(1);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1079
|
-
test('having clauses go into the last segment', () => {
|
|
1080
|
-
const doc = (0, test_translator_1.model) `
|
|
1081
|
-
query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
|
|
1082
|
-
query: checkme is refineme + { having: ac > 0 }`;
|
|
1083
|
-
expect(doc).toTranslate();
|
|
1084
|
-
const checkme = doc.translator.getQuery('checkme');
|
|
1085
|
-
expect(checkme).toBeDefined();
|
|
1086
|
-
if (checkme) {
|
|
1087
|
-
const havingClause = checkme.pipeline[1].filterList;
|
|
1088
|
-
expect(havingClause).toBeDefined();
|
|
1089
|
-
if (havingClause) {
|
|
1090
|
-
expect(havingClause.length).toBe(1);
|
|
1091
|
-
}
|
|
1092
|
-
}
|
|
1093
|
-
});
|
|
1094
|
-
test('limit goes into the last segment', () => {
|
|
1095
|
-
const doc = (0, test_translator_1.model) `
|
|
1096
|
-
query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
|
|
1097
|
-
query: checkme is refineme + { limit: 1 }`;
|
|
1098
|
-
expect(doc).toTranslate();
|
|
1099
|
-
const checkme = doc.translator.getQuery('checkme');
|
|
1100
|
-
expect(checkme).toBeDefined();
|
|
1101
|
-
if (checkme) {
|
|
1102
|
-
expect(checkme.pipeline[1]).toMatchObject({ limit: 1 });
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
test('order_by goes into the last segment', () => {
|
|
1106
|
-
const doc = (0, test_translator_1.model) `
|
|
1107
|
-
query: refineme is a -> { group_by: ai,astr } -> { group_by: ai, aggregate: ac is count() }
|
|
1108
|
-
query: checkme is refineme + { order_by: 2 }`;
|
|
1109
|
-
expect(doc).toTranslate();
|
|
1110
|
-
const checkme = doc.translator.getQuery('checkme');
|
|
1111
|
-
expect(checkme).toBeDefined();
|
|
1112
|
-
if (checkme) {
|
|
1113
|
-
expect(checkme.pipeline[1]).toHaveProperty('orderBy');
|
|
1114
|
-
}
|
|
1115
|
-
});
|
|
1116
|
-
const stageErr = 'Illegal in refinement of a query with more than one stage';
|
|
1117
|
-
test('group_by illegal in long pipes', () => {
|
|
1118
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1119
|
-
query: checkme is refineme + { ${'group_by: stage'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
|
|
1120
|
-
});
|
|
1121
|
-
test('aggregate illegal in long pipes', () => {
|
|
1122
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1123
|
-
query: checkme is refineme + { ${'aggregate: c is count()'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
|
|
1124
|
-
});
|
|
1125
|
-
test('calcluate illegal in long pipes', () => {
|
|
1126
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1127
|
-
query: checkme is refineme + { ${'calculate: c is count()'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
|
|
1128
|
-
});
|
|
1129
|
-
test('extend illegal in long pipes', () => {
|
|
1130
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1131
|
-
query: checkme is refineme + { ${'extend: {measure: c is count()}'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
|
|
1132
|
-
});
|
|
1133
|
-
test('nest illegal in long pipes', () => {
|
|
1134
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { select: stage is "stage1" } -> { select: stage is "stage2" }
|
|
1135
|
-
query: checkme is refineme + { ${'nest: b is {group_by: stage}'} }`).toLog((0, test_translator_1.errorMessage)(stageErr));
|
|
1136
|
-
});
|
|
1137
|
-
test('all single stage refinements are accepted', () => {
|
|
1138
|
-
expect((0, test_translator_1.markSource) `query: refineme is a -> { group_by: stage is "stage1" }
|
|
1139
|
-
query: checkme is refineme + {
|
|
1140
|
-
extend: { dimension: red is 'red' }
|
|
1141
|
-
nest: nestRed is { group_by: red }
|
|
1142
|
-
group_by: ai
|
|
1143
|
-
aggregate: counted is count()
|
|
1144
|
-
calculate: l is lag(ai)
|
|
1145
|
-
}`).toTranslate();
|
|
1146
|
-
});
|
|
1147
|
-
});
|
|
1148
|
-
});
|
|
1149
|
-
//# sourceMappingURL=query.spec.js.map
|