@malloydata/malloy 0.0.80-dev230830211713 → 0.0.80-dev230903192621
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/lang/test/expressions.spec.js +415 -0
- package/dist/lang/test/literals.spec.js +274 -0
- package/dist/lang/test/locations.spec.d.ts +1 -0
- package/dist/lang/test/locations.spec.js +722 -0
- package/dist/lang/test/parse.spec.js +93 -2417
- package/dist/lang/test/query.spec.d.ts +1 -0
- package/dist/lang/test/query.spec.js +998 -0
- package/dist/lang/test/source.spec.d.ts +1 -0
- package/dist/lang/test/source.spec.js +241 -0
- package/dist/lang/test/test-translator.d.ts +2 -1
- package/dist/lang/test/test-translator.js +24 -1
- package/package.json +1 -1
- package/dist/lang/test/refine.spec.js +0 -112
- package/dist/lang/test/strings.spec.js +0 -155
- /package/dist/lang/test/{refine.spec.d.ts → expressions.spec.d.ts} +0 -0
- /package/dist/lang/test/{strings.spec.d.ts → literals.spec.d.ts} +0 -0
|
@@ -23,9 +23,7 @@
|
|
|
23
23
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
const model_1 = require("../../model");
|
|
27
26
|
const test_translator_1 = require("./test-translator");
|
|
28
|
-
const granular_result_1 = require("../ast/types/granular-result");
|
|
29
27
|
require("./parse-expects");
|
|
30
28
|
describe('model statements', () => {
|
|
31
29
|
describe('table method', () => {
|
|
@@ -58,2446 +56,124 @@ describe('model statements', () => {
|
|
|
58
56
|
`).toTranslateWithWarnings("`table('connection_name:table_path')` is deprecated; use `connection_name.table('table_path')`");
|
|
59
57
|
});
|
|
60
58
|
});
|
|
61
|
-
describe('source:', () => {
|
|
62
|
-
test('table', () => {
|
|
63
|
-
expect("source: testA is table('aTable')").toTranslate();
|
|
64
|
-
});
|
|
65
|
-
test('shorcut fitlered table', () => {
|
|
66
|
-
expect("source: xA is table('aTable') {? astr ~ 'a%' }").toTranslate();
|
|
67
|
-
});
|
|
68
|
-
test('shorcut fitlered table m4warning', () => {
|
|
69
|
-
expect(`
|
|
70
|
-
##! m4warnings
|
|
71
|
-
source: xA is conn.table('aTable') extend {? astr ~ 'a%' }
|
|
72
|
-
`).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
|
|
73
|
-
});
|
|
74
|
-
test('fitlered table', () => {
|
|
75
|
-
expect("source: testA is table('aTable') { where: astr ~ 'a%' }").toTranslate();
|
|
76
|
-
});
|
|
77
|
-
test('ref source with no refinement', () => {
|
|
78
|
-
expect('source: testA is a').toTranslate();
|
|
79
|
-
});
|
|
80
|
-
test('from(query)', () => {
|
|
81
|
-
expect('source: testA is from(a->{group_by: astr})').toTranslate();
|
|
82
|
-
});
|
|
83
|
-
test('refine source', () => {
|
|
84
|
-
expect('source: aa is a { dimension: a is astr }').toTranslate();
|
|
85
|
-
});
|
|
86
|
-
test('source refinement preserves original', () => {
|
|
87
|
-
const x = new test_translator_1.TestTranslator('source: na is a + { dimension: one is 1 }');
|
|
88
|
-
expect(x).toTranslate();
|
|
89
|
-
const a = x.getSourceDef('a');
|
|
90
|
-
if (a) {
|
|
91
|
-
const aFields = a.fields.map(f => f.as || f.name);
|
|
92
|
-
expect(aFields).toContain('astr');
|
|
93
|
-
expect(aFields).not.toContain('one');
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
});
|
|
97
|
-
describe('query:', () => {
|
|
98
|
-
test('anonymous query', () => {
|
|
99
|
-
expect((0, test_translator_1.markSource) `query: ${"table('aTable') -> { group_by: astr }"}`).toTranslate();
|
|
100
|
-
});
|
|
101
|
-
test('anonymous query', () => {
|
|
102
|
-
expect((0, test_translator_1.markSource) `##! m4warnings
|
|
103
|
-
query: ${"conn.table('aTable') -> { group_by: astr }"}`).toTranslateWithWarnings('Anonymous `query:` statements are deprecated, use `run:` instead');
|
|
104
|
-
});
|
|
105
|
-
test('run query', () => expect("run: table('aTable') -> { group_by: astr }").toTranslate());
|
|
106
|
-
test('run query ref', () => expect(`
|
|
107
|
-
query: foo is table('aTable') -> { group_by: astr }
|
|
108
|
-
run: foo
|
|
109
|
-
`).toTranslate());
|
|
110
|
-
test('query', () => expect("query: name is table('aTable') -> { group_by: astr }").toTranslate());
|
|
111
|
-
test('query', () => {
|
|
112
|
-
expect("query: name is table('aTable') -> { group_by: astr }").toTranslate();
|
|
113
|
-
});
|
|
114
|
-
test('query from query', () => {
|
|
115
|
-
expect(`
|
|
116
|
-
query: q1 is ab->{ group_by: astr limit: 10 }
|
|
117
|
-
query: q2 is ->q1
|
|
118
|
-
`).toTranslate();
|
|
119
|
-
});
|
|
120
|
-
test('query with refinements from query', () => {
|
|
121
|
-
expect(`
|
|
122
|
-
query: q1 is ab->{ group_by: astr limit: 10 }
|
|
123
|
-
query: q2 is ->q1 { aggregate: acount }
|
|
124
|
-
`).toTranslate();
|
|
125
|
-
});
|
|
126
|
-
test('chained query operations', () => {
|
|
127
|
-
expect(`
|
|
128
|
-
query: ab
|
|
129
|
-
-> { group_by: astr; aggregate: acount }
|
|
130
|
-
-> { top: 5; where: astr ~ 'a%' group_by: astr }
|
|
131
|
-
`).toTranslate();
|
|
132
|
-
});
|
|
133
|
-
test('from(query) refined into query', () => {
|
|
134
|
-
expect('query: from(ab -> {group_by: astr}) { dimension: bigstr is upper(astr) } -> { group_by: bigstr }').toTranslate();
|
|
135
|
-
});
|
|
136
|
-
test('query with shortcut filtered turtle', () => {
|
|
137
|
-
expect("query: allA is ab->aturtle {? astr ~ 'a%' }").toTranslate();
|
|
138
|
-
});
|
|
139
|
-
test('query with shortcut filtered turtle m4warning', () => {
|
|
140
|
-
expect(`
|
|
141
|
-
##! m4warnings
|
|
142
|
-
query: allA is ab->aturtle refine {? astr ~ 'a%' }
|
|
143
|
-
`).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
|
|
144
|
-
});
|
|
145
|
-
test('query with filtered turtle', () => {
|
|
146
|
-
expect("query: allA is ab->aturtle { where: astr ~ 'a%' }").toTranslate();
|
|
147
|
-
});
|
|
148
|
-
test('nest: in group_by:', () => {
|
|
149
|
-
expect(`
|
|
150
|
-
query: ab -> {
|
|
151
|
-
group_by: astr;
|
|
152
|
-
nest: nested_count is {
|
|
153
|
-
aggregate: acount
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
`).toTranslate();
|
|
157
|
-
});
|
|
158
|
-
test('reduce pipe project', () => {
|
|
159
|
-
expect(`
|
|
160
|
-
query: a -> { aggregate: f is count() } -> { project: f2 is f + 1 }
|
|
161
|
-
`).toTranslate();
|
|
162
|
-
});
|
|
163
|
-
test('refine and extend query', () => {
|
|
164
|
-
expect(`
|
|
165
|
-
query: a_by_str is a -> { group_by: astr }
|
|
166
|
-
query: -> a_by_str { aggregate: str_count is count() }
|
|
167
|
-
`).toTranslate();
|
|
168
|
-
});
|
|
169
|
-
test('query refinement preserves original', () => {
|
|
170
|
-
const x = new test_translator_1.TestTranslator(`
|
|
171
|
-
query: q is a -> { aggregate: acount is count() }
|
|
172
|
-
query: nq is -> q + { group_by: astr }
|
|
173
|
-
`);
|
|
174
|
-
expect(x).toTranslate();
|
|
175
|
-
const q = x.getQuery('q');
|
|
176
|
-
expect(q).toBeDefined();
|
|
177
|
-
if (q) {
|
|
178
|
-
const qFields = q.pipeline[0].fields;
|
|
179
|
-
expect(qFields.length).toBe(1);
|
|
180
|
-
}
|
|
181
|
-
});
|
|
182
|
-
test('query composition preserves original', () => {
|
|
183
|
-
const x = new test_translator_1.TestTranslator(`
|
|
184
|
-
query: q is ab -> { aggregate: acount }
|
|
185
|
-
query: nq is -> q -> { project: * }
|
|
186
|
-
`);
|
|
187
|
-
expect(x).toTranslate();
|
|
188
|
-
const q = x.getQuery('q');
|
|
189
|
-
expect(q).toBeDefined();
|
|
190
|
-
if (q) {
|
|
191
|
-
expect(q.pipeline.length).toBe(1);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
test('all ungroup with args', () => {
|
|
195
|
-
expect(`
|
|
196
|
-
query: a -> {
|
|
197
|
-
group_by: astr
|
|
198
|
-
nest: by_int is {
|
|
199
|
-
group_by: ai
|
|
200
|
-
aggregate: bi_count is all(count(), ai)
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
`).toTranslate();
|
|
204
|
-
});
|
|
205
|
-
test('all ungroup checks args', () => {
|
|
206
|
-
expect(`
|
|
207
|
-
query: a -> {
|
|
208
|
-
group_by: astr
|
|
209
|
-
nest: by_int is {
|
|
210
|
-
group_by: ai
|
|
211
|
-
aggregate: bi_count is all(count(), afloat)
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
`).translationToFailWith("all() 'afloat' is missing from query output");
|
|
215
|
-
});
|
|
216
|
-
test('exclude ungroup with args', () => {
|
|
217
|
-
expect(`
|
|
218
|
-
query: a -> {
|
|
219
|
-
group_by: aa is 'a'
|
|
220
|
-
nest: by_b is {
|
|
221
|
-
group_by: bb is 'b'
|
|
222
|
-
nest: by_c is {
|
|
223
|
-
group_by: cc is 'c'
|
|
224
|
-
aggregate: bb_count is exclude(count(), aa, cc)
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
`).toTranslate();
|
|
229
|
-
});
|
|
230
|
-
test('exclude ungroup checks args', () => {
|
|
231
|
-
expect(`
|
|
232
|
-
query: a -> {
|
|
233
|
-
group_by: aa is 'a'
|
|
234
|
-
nest: by_b is {
|
|
235
|
-
group_by: bb is 'b'
|
|
236
|
-
nest: by_c is {
|
|
237
|
-
group_by: cc is 'c'
|
|
238
|
-
aggregate: bb_count is exclude(count(), aaa, cc)
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
`).translationToFailWith("exclude() 'aaa' is missing from query output");
|
|
243
|
-
});
|
|
244
|
-
test('exclude problem revealed by production models', () => {
|
|
245
|
-
expect(`
|
|
246
|
-
source: carriers is table('malloytest.carriers') {
|
|
247
|
-
primary_key: code
|
|
248
|
-
}
|
|
249
|
-
source: flights is table('malloytest.flights') {
|
|
250
|
-
primary_key: id2
|
|
251
|
-
join_one: carriers with carrier
|
|
252
|
-
|
|
253
|
-
query: carrier_overview is {
|
|
254
|
-
group_by: carrier_name is carriers.nickname
|
|
255
|
-
nest: top_destinations is {
|
|
256
|
-
group_by: destination
|
|
257
|
-
aggregate:
|
|
258
|
-
flights_to_dest is exclude(count(), carrier_name)*100
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
`).toTranslate();
|
|
263
|
-
});
|
|
264
|
-
describe('query operation typechecking', () => {
|
|
265
|
-
describe('field declarations', () => {
|
|
266
|
-
test('cannot use aggregate in group_by', () => {
|
|
267
|
-
expect('query: a -> { group_by: s is count()}').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
|
|
268
|
-
});
|
|
269
|
-
test('cannot use ungrouped_aggregate in group_by', () => {
|
|
270
|
-
expect('query: a -> { group_by: s is all(count())}').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
|
|
271
|
-
});
|
|
272
|
-
test('cannot use analytic in group_by', () => {
|
|
273
|
-
expect('query: a -> { group_by: s is row_number()}').translationToFailWith('Cannot use an analytic field in a group_by operation, did you mean to use a calculate operation instead?');
|
|
274
|
-
});
|
|
275
|
-
test('cannot use aggregate in dimension', () => {
|
|
276
|
-
expect('source: a1 is a { dimension: s is count()}').translationToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
|
|
277
|
-
});
|
|
278
|
-
test('cannot use ungrouped_aggregate in dimension', () => {
|
|
279
|
-
expect('source: a1 is a { dimension: s is all(count())}').translationToFailWith('Cannot use an aggregate field in a dimension declaration, did you mean to use a measure declaration instead?');
|
|
280
|
-
});
|
|
281
|
-
test('cannot use analytic in dimension', () => {
|
|
282
|
-
expect('source: a1 is a { dimension: s is row_number()}').translationToFailWith('Cannot use an analytic field in a dimension declaration');
|
|
283
|
-
});
|
|
284
|
-
test('cannot use scalar in measure', () => {
|
|
285
|
-
expect('source: a1 is a { measure: s is 1}').translationToFailWith('Cannot use a scalar field in a measure declaration, did you mean to use a dimension declaration instead?');
|
|
286
|
-
});
|
|
287
|
-
test('cannot use analytic in measure', () => {
|
|
288
|
-
expect('source: a1 is a { measure: s is lag(count())}').translationToFailWith('Cannot use an analytic field in a measure declaration');
|
|
289
|
-
});
|
|
290
|
-
test('cannot use scalar in aggregate', () => {
|
|
291
|
-
expect('query: a -> { aggregate: s is 1}').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
|
|
292
|
-
});
|
|
293
|
-
test('cannot use analytic in aggregate', () => {
|
|
294
|
-
expect('query: a -> { aggregate: s is lag(count())}').translationToFailWith('Cannot use an analytic field in an aggregate operation, did you mean to use a calculate operation instead?');
|
|
295
|
-
});
|
|
296
|
-
test('cannot use scalar in calculate', () => {
|
|
297
|
-
expect('query: a -> { group_by: a is 1; calculate: s is 1 }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
|
|
298
|
-
});
|
|
299
|
-
test('cannot use aggregate in calculate', () => {
|
|
300
|
-
expect('query: a -> { group_by: a is 1; calculate: s is count() }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
|
|
301
|
-
});
|
|
302
|
-
test('cannot use aggregate in project', () => {
|
|
303
|
-
expect('query: a -> { project: s is count() }').translationToFailWith('Cannot use an aggregate field in a project operation, did you mean to use an aggregate operation instead?');
|
|
304
|
-
});
|
|
305
|
-
test('cannot use analytic in project', () => {
|
|
306
|
-
expect('query: a -> { project: s is row_number() }').translationToFailWith('Cannot use an analytic field in a project operation, did you mean to use a calculate operation instead?');
|
|
307
|
-
});
|
|
308
|
-
test('cannot use analytic in declare', () => {
|
|
309
|
-
expect('query: a -> { group_by: a is 1; declare: s is row_number() }').translationToFailWith('Analytic expressions can not be used in a declare block');
|
|
310
|
-
});
|
|
311
|
-
test('cannot use aggregate in index', () => {
|
|
312
|
-
expect('query: a { measure: acount is count() } -> { index: acount }').translationToFailWith('Cannot use an aggregate field in an index operation');
|
|
313
|
-
});
|
|
314
|
-
test('can use aggregate in except', () => {
|
|
315
|
-
expect(`
|
|
316
|
-
source: b1 is a { measure: acount is count() }
|
|
317
|
-
source: c1 is b1 { except: acount }
|
|
318
|
-
`).toTranslate();
|
|
319
|
-
});
|
|
320
|
-
});
|
|
321
|
-
describe('field references', () => {
|
|
322
|
-
test('cannot use aggregate in group_by', () => {
|
|
323
|
-
expect('query: a -> { declare: acount is count(); group_by: acount }').translationToFailWith('Cannot use an aggregate field in a group_by operation, did you mean to use an aggregate operation instead?');
|
|
324
|
-
});
|
|
325
|
-
test('cannot use query in group_by', () => {
|
|
326
|
-
expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: q }').translationToFailWith('Cannot use a query field in a group_by operation, did you mean to use a nest operation instead?');
|
|
327
|
-
});
|
|
328
|
-
test('cannot use scalar in aggregate', () => {
|
|
329
|
-
expect('query: a -> { declare: aconst is 1; aggregate: aconst }').translationToFailWith('Cannot use a scalar field in an aggregate operation, did you mean to use a group_by or project operation instead?');
|
|
330
|
-
});
|
|
331
|
-
test('cannot use scalar in calculate', () => {
|
|
332
|
-
expect('query: a -> { declare: aconst is 1; group_by: x is 1; calculate: aconst }').translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
|
|
333
|
-
});
|
|
334
|
-
test('cannot use aggregate in calculate', () => {
|
|
335
|
-
expect('query: a -> { declare: acount is count(); group_by: x is 1; calculate: acount }').translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
|
|
336
|
-
});
|
|
337
|
-
test('cannot use query in project', () => {
|
|
338
|
-
expect('query: a { query: q is { group_by: x is 1 } } -> { project: q }').translationToFailWith('Cannot use a query field in a project operation, did you mean to use a nest operation instead?');
|
|
339
|
-
});
|
|
340
|
-
test('cannot use query in index', () => {
|
|
341
|
-
expect('query: a { query: q is { group_by: x is 1 } } -> { index: q }').translationToFailWith('Cannot use a query field in an index operation');
|
|
342
|
-
});
|
|
343
|
-
test('cannot use query in calculate', () => {
|
|
344
|
-
expect('query: a { query: q is { group_by: x is 1 } } -> { group_by: x is 1; calculate: q }').translationToFailWith('Cannot use a query field in a calculate operation, did you mean to use a nest operation instead?');
|
|
345
|
-
});
|
|
346
|
-
test('cannot use query in aggregate', () => {
|
|
347
|
-
expect('query: a { query: q is { group_by: x is 1 } } -> { aggregate: q }').translationToFailWith('Cannot use a query field in an aggregate operation, did you mean to use a nest operation instead?');
|
|
348
|
-
});
|
|
349
|
-
test('cannot use aggregate in calculate, preserved over refinement', () => {
|
|
350
|
-
expect(`query: a1 is a -> {
|
|
351
|
-
aggregate: c is count()
|
|
352
|
-
}
|
|
353
|
-
query: -> a1 {
|
|
354
|
-
calculate: b is c
|
|
355
|
-
}`).translationToFailWith('Cannot use an aggregate field in a calculate operation, did you mean to use an aggregate operation instead?');
|
|
356
|
-
});
|
|
357
|
-
test('cannot use scalar in calculate, preserved over refinement', () => {
|
|
358
|
-
expect(`query: a1 is a -> {
|
|
359
|
-
group_by: c is 1
|
|
360
|
-
}
|
|
361
|
-
query: -> a1 {
|
|
362
|
-
calculate: b is c
|
|
363
|
-
}`).translationToFailWith('Cannot use a scalar field in a calculate operation, did you mean to use a group_by or project operation instead?');
|
|
364
|
-
});
|
|
365
|
-
test('cannot use analytic in group_by, preserved over refinement', () => {
|
|
366
|
-
expect(`query: a1 is a -> {
|
|
367
|
-
group_by: c is 1
|
|
368
|
-
calculate: c2 is lag(c)
|
|
369
|
-
}
|
|
370
|
-
query: -> a1 {
|
|
371
|
-
group_by: b is c2
|
|
372
|
-
}`).translationToFailWith(
|
|
373
|
-
// c2 is not defined because group_by doesn't know to look in the output space
|
|
374
|
-
"'c2' is not defined");
|
|
375
|
-
});
|
|
376
|
-
test('cannot use analytic in order_by, preserved over refinement', () => {
|
|
377
|
-
expect(`query: a1 is a -> {
|
|
378
|
-
group_by: c is 1
|
|
379
|
-
calculate: c2 is lag(c)
|
|
380
|
-
}
|
|
381
|
-
query: -> a1 {
|
|
382
|
-
order_by: c2
|
|
383
|
-
}`).translationToFailWith('Illegal order by of analytic field c2');
|
|
384
|
-
});
|
|
385
|
-
test('cannot ungroup an ungrouped', () => {
|
|
386
|
-
expect(`query: a1 is a -> {
|
|
387
|
-
group_by: c is 1
|
|
388
|
-
aggregate: c2 is all(all(sum(ai)))
|
|
389
|
-
}`).translationToFailWith('all() expression must not already be ungrouped');
|
|
390
|
-
});
|
|
391
|
-
test('cannot aggregate an ungrouped', () => {
|
|
392
|
-
expect(`query: a1 is a -> {
|
|
393
|
-
group_by: c is 1
|
|
394
|
-
aggregate: c2 is sum(all(sum(ai)))
|
|
395
|
-
}`).translationToFailWith('Aggregate expression cannot be aggregate');
|
|
396
|
-
});
|
|
397
|
-
test('cannot aggregate an aggregate', () => {
|
|
398
|
-
expect(`query: a1 is a -> {
|
|
399
|
-
group_by: c is 1
|
|
400
|
-
aggregate: c2 is sum(sum(ai))
|
|
401
|
-
}`).translationToFailWith('Aggregate expression cannot be aggregate');
|
|
402
|
-
});
|
|
403
|
-
test('can use field def in group_by, preserved over refinement', () => {
|
|
404
|
-
expect(`query: a1 is a -> {
|
|
405
|
-
group_by: c is 1
|
|
406
|
-
}
|
|
407
|
-
query: -> a1 {
|
|
408
|
-
order_by: c
|
|
409
|
-
}`).toTranslate();
|
|
410
|
-
});
|
|
411
|
-
test('can use field ref in group_by, preserved over refinement', () => {
|
|
412
|
-
expect(`query: a1 is a -> {
|
|
413
|
-
group_by: c is astr
|
|
414
|
-
}
|
|
415
|
-
query: -> a1 {
|
|
416
|
-
order_by: c
|
|
417
|
-
}`).toTranslate();
|
|
418
|
-
});
|
|
419
|
-
});
|
|
420
|
-
});
|
|
421
|
-
describe('function typechecking', () => {
|
|
422
|
-
test('use function correctly', () => {
|
|
423
|
-
expect(`query: a -> {
|
|
424
|
-
group_by: s is concat('a', 'b')
|
|
425
|
-
}`).toTranslate();
|
|
426
|
-
});
|
|
427
|
-
test('function incorrect case', () => {
|
|
428
|
-
expect(`query: a -> {
|
|
429
|
-
group_by: s is CONCAT('a', 'b')
|
|
430
|
-
}`).toTranslateWithWarnings("Case insensitivity for function names is deprecated, use 'concat' instead");
|
|
431
|
-
});
|
|
432
|
-
test('function no matching overload', () => {
|
|
433
|
-
expect(`query: a -> {
|
|
434
|
-
group_by: s is floor('a', 'b')
|
|
435
|
-
}`).translationToFailWith('No matching overload for function floor(string, string)');
|
|
436
|
-
});
|
|
437
|
-
test('unknown function', () => {
|
|
438
|
-
expect(`query: a -> {
|
|
439
|
-
group_by: s is asdfasdf()
|
|
440
|
-
}`).translationToFailWith("Unknown function 'asdfasdf'. Use 'asdfasdf!(...)' to call a SQL function directly.");
|
|
441
|
-
});
|
|
442
|
-
test('can select different overload', () => {
|
|
443
|
-
expect('query: a -> { group_by: s is concat() }').toTranslate();
|
|
444
|
-
});
|
|
445
|
-
test('can pass different expression types', () => {
|
|
446
|
-
expect(`query: a -> {
|
|
447
|
-
group_by: f1 is sqrt(1)
|
|
448
|
-
aggregate: f2 is sqrt(count())
|
|
449
|
-
aggregate: f3 is sqrt(all(count()))
|
|
450
|
-
calculate: f4 is sqrt(lag(f1))
|
|
451
|
-
calculate: f5 is sqrt(lag(count()))
|
|
452
|
-
}`).toTranslate();
|
|
453
|
-
});
|
|
454
|
-
test('function return type correct', () => {
|
|
455
|
-
expect(`query: a -> {
|
|
456
|
-
group_by: s is floor(1.2) + 1
|
|
457
|
-
}`).toTranslate();
|
|
458
|
-
});
|
|
459
|
-
test('function return type incorrect', () => {
|
|
460
|
-
expect(`query: a -> {
|
|
461
|
-
group_by: s is floor(1.2) + 'a'
|
|
462
|
-
}`).translationToFailWith("Non numeric('number,string') value with '+'");
|
|
463
|
-
});
|
|
464
|
-
test('can use output value in calculate', () => {
|
|
465
|
-
expect(`query: a -> {
|
|
466
|
-
group_by: x is 1
|
|
467
|
-
calculate: s is lag(x)
|
|
468
|
-
}`).toTranslate();
|
|
469
|
-
});
|
|
470
|
-
test('cannot use output value in group_by', () => {
|
|
471
|
-
expect(`query: a -> {
|
|
472
|
-
group_by: x is 1
|
|
473
|
-
group_by: y is x
|
|
474
|
-
}`).translationToFailWith("'x' is not defined");
|
|
475
|
-
});
|
|
476
|
-
test('lag can check that other args are constant', () => {
|
|
477
|
-
expect(`query: a -> {
|
|
478
|
-
group_by: x is 1
|
|
479
|
-
calculate: s is lag(x, 1, x)
|
|
480
|
-
}`).translationToFailWith(
|
|
481
|
-
// TODO improve this error message
|
|
482
|
-
"Parameter 3 ('default') of lag must be literal or constant, but received output");
|
|
483
|
-
});
|
|
484
|
-
test('lag can check that other args are literal', () => {
|
|
485
|
-
expect(`query: a -> {
|
|
486
|
-
group_by: x is 1
|
|
487
|
-
calculate: s is lag(x, 1 + 1)
|
|
488
|
-
}`).translationToFailWith(
|
|
489
|
-
// TODO improve this error message
|
|
490
|
-
"Parameter 2 ('offset') of lag must be literal, but received constant");
|
|
491
|
-
});
|
|
492
|
-
test('lag can check that other args are nonnull', () => {
|
|
493
|
-
expect(`query: a -> {
|
|
494
|
-
group_by: x is 1
|
|
495
|
-
calculate: s is lag(x, null)
|
|
496
|
-
}`).translationToFailWith("Parameter 2 ('offset') of lag must not be a literal null");
|
|
497
|
-
});
|
|
498
|
-
test('lag can use constant values for other args', () => {
|
|
499
|
-
expect(`query: a -> {
|
|
500
|
-
group_by: x is 1
|
|
501
|
-
calculate: s is lag(x, 2)
|
|
502
|
-
}`).toTranslate();
|
|
503
|
-
});
|
|
504
|
-
test('cannot name top level objects same as functions', () => {
|
|
505
|
-
expect((0, test_translator_1.markSource) `query: ${'concat is a -> { group_by: x is 1 }'}`).translationToFailWith(
|
|
506
|
-
// TODO improve this error message
|
|
507
|
-
"'concat' is already defined, cannot redefine");
|
|
508
|
-
});
|
|
509
|
-
test('`now` is considered constant`', () => {
|
|
510
|
-
expect(`query: a -> {
|
|
511
|
-
group_by: n is now
|
|
512
|
-
calculate: l is lag(n, 1, now)
|
|
513
|
-
}`).toTranslate();
|
|
514
|
-
});
|
|
515
|
-
// TODO it might be nice to reference a field which is a constant, and be able to
|
|
516
|
-
// use that as a constant param. Same with a literal.
|
|
517
|
-
test('cannot use a field which is a constant in a constant param', () => {
|
|
518
|
-
expect(`query: a -> {
|
|
519
|
-
group_by: ai, pi is pi()
|
|
520
|
-
calculate: l is lag(ai, 1, pi)
|
|
521
|
-
}`).translationToFailWith("Parameter 3 ('default') of lag must be literal or constant, but received output");
|
|
522
|
-
});
|
|
523
|
-
// TODO we don't handle referencing a join as a field correctly in all cases today.
|
|
524
|
-
// For now, it at least is considered type `struct` and therefore fails to parse
|
|
525
|
-
// as a function argument.
|
|
526
|
-
// We add <join_name>_id to the query, but it's not included in the output space
|
|
527
|
-
test('cannot use struct in function arg', () => {
|
|
528
|
-
expect(`query: a {join_one: b with astr } -> {
|
|
529
|
-
group_by: b
|
|
530
|
-
calculate: foo is lag(b)
|
|
531
|
-
}`).translationToFailWith('No matching overload for function lag(struct)');
|
|
532
|
-
});
|
|
533
|
-
// TODO this doesn't work today, we're not rigorous enough with integer
|
|
534
|
-
// subtypes. But we should probably make this typecheck properly.
|
|
535
|
-
test.skip('cannot use float in round precision', () => {
|
|
536
|
-
expect(`query: a -> {
|
|
537
|
-
group_by: x is round(1.5, 1.6)
|
|
538
|
-
}`).translationToFailWith(
|
|
539
|
-
// TODO improve this error message
|
|
540
|
-
"Parameter 2 ('precision') for round must be integer, received float");
|
|
541
|
-
});
|
|
542
|
-
test('cannot use stddev with no arguments', () => {
|
|
543
|
-
expect(`query: a -> {
|
|
544
|
-
aggregate: x is stddev()
|
|
545
|
-
}`).translationToFailWith('No matching overload for function stddev()');
|
|
546
|
-
});
|
|
547
|
-
test('can use stddev with postfix syntax', () => {
|
|
548
|
-
expect(`query: a -> {
|
|
549
|
-
declare: y is 1
|
|
550
|
-
aggregate: x is y.stddev()
|
|
551
|
-
}`).toTranslate();
|
|
552
|
-
});
|
|
553
|
-
test('can use stddev with postfix syntax on join', () => {
|
|
554
|
-
expect(`query: a -> {
|
|
555
|
-
join_one: b with astr
|
|
556
|
-
aggregate: x is b.ai.stddev()
|
|
557
|
-
}`).toTranslate();
|
|
558
|
-
});
|
|
559
|
-
test('can use calculate with a measure', () => {
|
|
560
|
-
expect(`query: a { measure: c is count() } -> {
|
|
561
|
-
group_by: y is 1
|
|
562
|
-
calculate: x is lag(c)
|
|
563
|
-
}`).toTranslate();
|
|
564
|
-
});
|
|
565
|
-
test('cannot use calculate with input fields', () => {
|
|
566
|
-
expect(`query: a -> {
|
|
567
|
-
group_by: y is 1
|
|
568
|
-
calculate: x is lag(ai)
|
|
569
|
-
}`).translationToFailWith(
|
|
570
|
-
// TODO improve this error message:
|
|
571
|
-
// Parameter 1 ('value') of 'lag' must be a constant, an aggregate, or an expression using
|
|
572
|
-
// only fields that appear in the query output. Received an expression which uses a field
|
|
573
|
-
// that is not in the query output.
|
|
574
|
-
"Parameter 1 ('value') of lag must be literal, constant or output, but received input");
|
|
575
|
-
});
|
|
576
|
-
test('can use calculate with aggregate field which is not in query', () => {
|
|
577
|
-
expect(`query: a { measure: acount is count() } -> {
|
|
578
|
-
group_by: astr
|
|
579
|
-
calculate: pc is lag(acount)
|
|
580
|
-
}`).toTranslate();
|
|
581
|
-
});
|
|
582
|
-
test('cannot use agregate as argument to agg function', () => {
|
|
583
|
-
expect(`query: a -> {
|
|
584
|
-
aggregate: x is stddev(count())
|
|
585
|
-
}`).translationToFailWith("Parameter 1 ('value') of stddev must be scalar, but received aggregate");
|
|
586
|
-
});
|
|
587
|
-
test('cannot use calculate with no other fields', () => {
|
|
588
|
-
expect(`query: a -> {
|
|
589
|
-
calculate: x is row_number()
|
|
590
|
-
}`).translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
|
|
591
|
-
});
|
|
592
|
-
// TODO someday make it so we can order by an analytic function
|
|
593
|
-
test('today: cannot order by analytic function', () => {
|
|
594
|
-
expect(`query: a -> {
|
|
595
|
-
group_by: astr
|
|
596
|
-
calculate: row_num is row_number()
|
|
597
|
-
order_by: row_num desc
|
|
598
|
-
}`).translationToFailWith('Illegal order by of analytic field row_num');
|
|
599
|
-
});
|
|
600
|
-
test('cannot use analytic in calculate -- and preserved over refinement', () => {
|
|
601
|
-
expect(`query: a1 is a -> {
|
|
602
|
-
group_by: astr
|
|
603
|
-
calculate: p is lag(astr)
|
|
604
|
-
}
|
|
605
|
-
query: -> a1 {
|
|
606
|
-
calculate: p1 is lag(p)
|
|
607
|
-
}`).translationToFailWith("Parameter 1 ('value') of lag must be scalar or aggregate, but received scalar_analytic");
|
|
608
|
-
});
|
|
609
|
-
test('cannot use aggregate analytic in project', () => {
|
|
610
|
-
expect(`query: a -> {
|
|
611
|
-
project: astr
|
|
612
|
-
calculate: p is lag(count())
|
|
613
|
-
}`).translationToFailWith('Cannot add aggregate analyics to project');
|
|
614
|
-
});
|
|
615
|
-
test('reference field in join', () => {
|
|
616
|
-
expect(`query: a -> {
|
|
617
|
-
join_one: b with astr
|
|
618
|
-
group_by: b.ai
|
|
619
|
-
}`).toTranslate();
|
|
620
|
-
});
|
|
621
|
-
// TODO decide whether this syntax should be legal, really...
|
|
622
|
-
test('reference join name in group_by', () => {
|
|
623
|
-
expect(`query: a -> {
|
|
624
|
-
join_one: b with astr
|
|
625
|
-
group_by: b
|
|
626
|
-
}`).toTranslate();
|
|
627
|
-
});
|
|
628
|
-
test('can reference project: inline join.* field in calculate', () => {
|
|
629
|
-
expect(`query: a -> {
|
|
630
|
-
join_one: b with astr
|
|
631
|
-
project: b.*
|
|
632
|
-
calculate: s is lag(ai)
|
|
633
|
-
}`).toTranslate();
|
|
634
|
-
});
|
|
635
|
-
test('can reference project: join.* field in calculate', () => {
|
|
636
|
-
expect(`query: a { join_one: b with astr } -> {
|
|
637
|
-
project: b.*
|
|
638
|
-
calculate: s is lag(ai)
|
|
639
|
-
}`).toTranslate();
|
|
640
|
-
});
|
|
641
|
-
test('can reference implied output entries in calculate', () => {
|
|
642
|
-
expect(`source: aaa is a extend { dimension: big_i is ai+10 }
|
|
643
|
-
query: a extend { join_one: aaa on astr=aaa.astr } -> {
|
|
644
|
-
group_by: aaa.big_i
|
|
645
|
-
aggregate: s is aaa.big_i.sum()
|
|
646
|
-
calculate: a is lag(big_i.sum())
|
|
647
|
-
}`).toTranslate();
|
|
648
|
-
});
|
|
649
|
-
});
|
|
650
|
-
});
|
|
651
59
|
test('errors on redefinition of query', () => {
|
|
652
60
|
expect('query: q1 is a -> { project: * }, q1 is a -> { project: * }').translationToFailWith("'q1' is already defined, cannot redefine");
|
|
653
61
|
});
|
|
654
62
|
});
|
|
655
|
-
describe('
|
|
656
|
-
test('
|
|
657
|
-
expect('source: aa is a { dimension: x is 1 }').toTranslate();
|
|
658
|
-
});
|
|
659
|
-
test('multiple dimensions', () => {
|
|
63
|
+
describe('error handling', () => {
|
|
64
|
+
test('field and query with same name does not overflow', () => {
|
|
660
65
|
expect(`
|
|
661
|
-
source:
|
|
662
|
-
|
|
663
|
-
x is 1
|
|
664
|
-
y is 2
|
|
66
|
+
source: flights is table('malloytest.flights') {
|
|
67
|
+
query: carrier is { group_by: carrier }
|
|
665
68
|
}
|
|
666
|
-
`).
|
|
667
|
-
});
|
|
668
|
-
test('single declare', () => {
|
|
669
|
-
expect('source: aa is a { declare: x is 1 }').toTranslate();
|
|
69
|
+
`).translationToFailWith("Cannot redefine 'carrier'");
|
|
670
70
|
});
|
|
671
|
-
test('
|
|
672
|
-
expect(`
|
|
673
|
-
source:
|
|
674
|
-
|
|
675
|
-
x is 1
|
|
676
|
-
y is 2
|
|
71
|
+
test('redefine source', () => {
|
|
72
|
+
expect((0, test_translator_1.markSource) `
|
|
73
|
+
source: airports is table('malloytest.airports') + {
|
|
74
|
+
primary_key: code
|
|
677
75
|
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
test('single measure', () => {
|
|
681
|
-
expect('source: aa is a { measure: x is count() }').toTranslate();
|
|
682
|
-
});
|
|
683
|
-
test('multiple measures', () => {
|
|
684
|
-
expect(`
|
|
685
|
-
source: aa is a {
|
|
686
|
-
measure:
|
|
687
|
-
x is count()
|
|
688
|
-
y is x * x
|
|
76
|
+
source: airports is table('malloytest.airports') + {
|
|
77
|
+
primary_key: code
|
|
689
78
|
}
|
|
690
|
-
`).
|
|
79
|
+
`).translationToFailWith("Cannot redefine 'airports'");
|
|
691
80
|
});
|
|
692
|
-
test('
|
|
693
|
-
expect(
|
|
81
|
+
test('query from undefined source', () => {
|
|
82
|
+
expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined query or source 'x'");
|
|
694
83
|
});
|
|
695
|
-
test('
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
ai > 10,
|
|
700
|
-
af < 1000
|
|
701
|
-
}
|
|
702
|
-
`).toTranslate();
|
|
84
|
+
test('query with expression from undefined source', () => {
|
|
85
|
+
// Regression check: Once upon a time this died with an exception even
|
|
86
|
+
// when "query: x->{ group_by: y}" (above) generated the correct error.
|
|
87
|
+
expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined query or source 'x'");
|
|
703
88
|
});
|
|
704
|
-
test('
|
|
705
|
-
expect(`
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
where: carriers.code = 'WN' | 'AA'
|
|
710
|
-
group_by: carriers.nickname
|
|
711
|
-
aggregate: flight_count is count()
|
|
712
|
-
}
|
|
713
|
-
}`).toTranslate();
|
|
89
|
+
test('join reference before definition', () => {
|
|
90
|
+
expect((0, test_translator_1.markSource) `
|
|
91
|
+
source: newAB is a { join_one: newB is ${'bb'} on astring }
|
|
92
|
+
source: newB is b
|
|
93
|
+
`).translationToFailWith("Undefined query or source 'bb'");
|
|
714
94
|
});
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
expect('source: x is a { join_one: b with astr }').toTranslate();
|
|
718
|
-
});
|
|
719
|
-
test('with', () => {
|
|
720
|
-
expect((0, test_translator_1.model) `source: x is a { join_one: y is b with astr }`).toTranslate();
|
|
721
|
-
});
|
|
722
|
-
test('with dotted ref', () => {
|
|
723
|
-
expect((0, test_translator_1.model) `source: x is ab { join_one: xz is a with b.astr }`).toTranslate();
|
|
724
|
-
});
|
|
725
|
-
test('one on', () => {
|
|
726
|
-
expect((0, test_translator_1.model) `source: x is a { join_one: b on astr = b.astr }`).toTranslate();
|
|
727
|
-
});
|
|
728
|
-
test('one is on', () => {
|
|
729
|
-
expect('source: x is a { join_one: y is b on astr = y.astr }').toTranslate();
|
|
730
|
-
});
|
|
731
|
-
test('many on', () => {
|
|
732
|
-
expect('source: nab is a { join_many: b on astr = b.astr }').toTranslate();
|
|
733
|
-
});
|
|
734
|
-
test('many is on', () => {
|
|
735
|
-
expect('source: y is a { join_many: x is b on astr = x.astr }').toTranslate();
|
|
736
|
-
});
|
|
737
|
-
test('cross', () => {
|
|
738
|
-
expect('source: nab is a { join_cross: b }').toTranslate();
|
|
739
|
-
});
|
|
740
|
-
test('cross is', () => {
|
|
741
|
-
expect('source: nab is a { join_cross: xb is b }').toTranslate();
|
|
742
|
-
});
|
|
743
|
-
test('cross on', () => {
|
|
744
|
-
expect('source: nab is a { join_cross: b on true}').toTranslate();
|
|
745
|
-
});
|
|
746
|
-
test('multiple joins', () => {
|
|
747
|
-
expect(`
|
|
748
|
-
source: nab is a {
|
|
749
|
-
join_one:
|
|
750
|
-
b with astr,
|
|
751
|
-
br is b with astr
|
|
752
|
-
}
|
|
753
|
-
`).toTranslate();
|
|
754
|
-
});
|
|
755
|
-
test('with requires primary key', () => {
|
|
756
|
-
expect((0, test_translator_1.markSource) `
|
|
757
|
-
source: nb is b {
|
|
758
|
-
join_one: ${"bb is table('aTable') with astr"}
|
|
759
|
-
}
|
|
760
|
-
`).translationToFailWith('join_one: Cannot use with unless source has a primary key');
|
|
761
|
-
});
|
|
95
|
+
test('non-rename rename', () => {
|
|
96
|
+
expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
|
|
762
97
|
});
|
|
763
|
-
test('
|
|
764
|
-
expect('source:
|
|
98
|
+
test('reference to field in its definition', () => {
|
|
99
|
+
expect('source: na is a { dimension: ustr is upper(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
|
|
765
100
|
});
|
|
766
|
-
test('
|
|
767
|
-
expect('
|
|
101
|
+
test('empty model', () => {
|
|
102
|
+
expect('').toTranslate();
|
|
768
103
|
});
|
|
769
|
-
test('
|
|
770
|
-
|
|
771
|
-
expect(onlyAstr).toTranslate();
|
|
772
|
-
const c = onlyAstr.getSourceDef('c');
|
|
773
|
-
if (c) {
|
|
774
|
-
expect(c.fields.length).toBe(1);
|
|
775
|
-
}
|
|
104
|
+
test('one line model ', () => {
|
|
105
|
+
expect('\n').toTranslate();
|
|
776
106
|
});
|
|
777
|
-
test('
|
|
778
|
-
expect('
|
|
107
|
+
test('query without fields', () => {
|
|
108
|
+
expect('query: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
|
|
779
109
|
});
|
|
780
|
-
test('
|
|
781
|
-
|
|
782
|
-
expect(noAstr).toTranslate();
|
|
783
|
-
const c = noAstr.getSourceDef('c');
|
|
784
|
-
if (c) {
|
|
785
|
-
const foundAstr = c.fields.find(f => f.name === 'astr');
|
|
786
|
-
expect(foundAstr).toBeUndefined();
|
|
787
|
-
}
|
|
110
|
+
test('refine cannot change query type', () => {
|
|
111
|
+
expect('query: ab -> aturtle { project: astr }').translationToFailWith(/Not legal in grouping query/);
|
|
788
112
|
});
|
|
789
|
-
test('
|
|
790
|
-
expect('
|
|
113
|
+
test('undefined field ref in query', () => {
|
|
114
|
+
expect('query: ab -> { aggregate: xyzzy }').translationToFailWith("'xyzzy' is not defined");
|
|
791
115
|
});
|
|
792
|
-
test('
|
|
793
|
-
expect(
|
|
116
|
+
test('query on source with errors', () => {
|
|
117
|
+
expect((0, test_translator_1.markSource) `
|
|
118
|
+
source: na is a { join_one: ${'n'} on astr }
|
|
119
|
+
`).translationToFailWith("Undefined source 'n'");
|
|
794
120
|
});
|
|
795
|
-
test('
|
|
796
|
-
expect(`
|
|
797
|
-
source: abNew is ab {
|
|
798
|
-
query: for1 is aturtle {? ai = 1 }
|
|
799
|
-
}
|
|
800
|
-
`).toTranslate();
|
|
121
|
+
test('detect duplicate output field names', () => {
|
|
122
|
+
expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'astr'} }`).translationToFailWith("Output already has a field named 'astr'");
|
|
801
123
|
});
|
|
802
|
-
test('
|
|
803
|
-
expect(`
|
|
804
|
-
##! m4warnings
|
|
805
|
-
source: abNew is ab extend {
|
|
806
|
-
query: for1 is aturtle refine {? ai = 1 }
|
|
807
|
-
}
|
|
808
|
-
`).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
|
|
124
|
+
test('detect join tail overlap existing ref', () => {
|
|
125
|
+
expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'b.astr'} }`).translationToFailWith("Output already has a field named 'astr'");
|
|
809
126
|
});
|
|
810
|
-
test('
|
|
127
|
+
test('undefined in expression with regex compare', () => {
|
|
811
128
|
expect(`
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
group_by: astr
|
|
815
|
-
} -> {
|
|
816
|
-
top: 10; order_by: astr
|
|
817
|
-
project: *
|
|
129
|
+
source: c is a {
|
|
130
|
+
dimension: d is meaning_of_life ~ r'(forty two|fifty four)'
|
|
818
131
|
}
|
|
819
|
-
|
|
820
|
-
`).toTranslate();
|
|
821
|
-
});
|
|
822
|
-
test('multiple explore-query', () => {
|
|
823
|
-
expect(`
|
|
824
|
-
source: abNew is ab {
|
|
825
|
-
query:
|
|
826
|
-
q1 is { group_by: astr },
|
|
827
|
-
q2 is { group_by: ai }
|
|
828
|
-
}
|
|
829
|
-
`).toTranslate();
|
|
830
|
-
});
|
|
831
|
-
});
|
|
832
|
-
describe('qops', () => {
|
|
833
|
-
test('group by single', () => {
|
|
834
|
-
expect('query: a->{ group_by: astr }').toTranslate();
|
|
835
|
-
});
|
|
836
|
-
test("group_by x is x'", () => {
|
|
837
|
-
expect('query: a->{ group_by: ai is ai/2 }').toTranslate();
|
|
838
|
-
});
|
|
839
|
-
test('group by multiple', () => {
|
|
840
|
-
expect('query: a->{ group_by: astr,ai }').toTranslate();
|
|
841
|
-
});
|
|
842
|
-
test('aggregate single', () => {
|
|
843
|
-
expect('query: a->{ aggregate: num is count() }').toTranslate();
|
|
844
|
-
});
|
|
845
|
-
test('calculate in reduce', () => {
|
|
846
|
-
expect('query: a->{ group_by: astr, ai calculate: num is lag(ai) }').toTranslate();
|
|
847
|
-
});
|
|
848
|
-
test('calculate in project', () => {
|
|
849
|
-
expect('query: a->{ project: astr, ai calculate: num is lag(ai) }').toTranslate();
|
|
850
|
-
});
|
|
851
|
-
test('aggregate reference', () => {
|
|
852
|
-
var _a;
|
|
853
|
-
const doc = (0, test_translator_1.model) `query: a->{ aggregate: ai.sum() }`;
|
|
854
|
-
expect(doc).toTranslate();
|
|
855
|
-
const q = doc.translator.getQuery(0);
|
|
856
|
-
expect(q).toBeDefined();
|
|
857
|
-
const ai = (_a = q === null || q === void 0 ? void 0 : q.pipeline[0]) === null || _a === void 0 ? void 0 : _a.fields[0];
|
|
858
|
-
expect(ai).toMatchObject({
|
|
859
|
-
name: 'ai',
|
|
860
|
-
type: 'number',
|
|
861
|
-
expressionType: 'aggregate',
|
|
862
|
-
});
|
|
863
|
-
});
|
|
864
|
-
test('timeunit reference', () => {
|
|
865
|
-
var _a;
|
|
866
|
-
const doc = (0, test_translator_1.model) `query: a->{ group_by: ats.day }`;
|
|
867
|
-
expect(doc).toTranslate();
|
|
868
|
-
const q = doc.translator.getQuery(0);
|
|
869
|
-
expect(q).toBeDefined();
|
|
870
|
-
const ats = (_a = q === null || q === void 0 ? void 0 : q.pipeline[0]) === null || _a === void 0 ? void 0 : _a.fields[0];
|
|
871
|
-
expect(ats).toMatchObject({ name: 'ats', type: 'timestamp' });
|
|
132
|
+
`).translationToFailWith("'meaning_of_life' is not defined");
|
|
872
133
|
});
|
|
873
|
-
test('
|
|
134
|
+
test('detect output collision on join references', () => {
|
|
874
135
|
expect(`
|
|
875
|
-
query:
|
|
876
|
-
|
|
136
|
+
query: ab -> {
|
|
137
|
+
group_by: astr, b.astr
|
|
877
138
|
}
|
|
878
|
-
`).
|
|
879
|
-
});
|
|
880
|
-
test('project ref', () => {
|
|
881
|
-
expect('query:ab->{ project: b.astr }').toTranslate();
|
|
882
|
-
});
|
|
883
|
-
test('project *', () => {
|
|
884
|
-
expect('query:ab->{ project: * }').toTranslate();
|
|
885
|
-
});
|
|
886
|
-
test('project def', () => {
|
|
887
|
-
expect('query:ab->{ project: one is 1 }').toTranslate();
|
|
139
|
+
`).translationToFailWith("Output already has a field named 'astr'");
|
|
888
140
|
});
|
|
889
|
-
test('
|
|
141
|
+
test('rejoin a query is renamed', () => {
|
|
890
142
|
expect(`
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
});
|
|
896
|
-
test('index single', () => {
|
|
897
|
-
expect('query:a->{index: astr}').toTranslate();
|
|
898
|
-
});
|
|
899
|
-
test('index path', () => {
|
|
900
|
-
expect('query:ab->{index: ab.astr}').toTranslate();
|
|
901
|
-
});
|
|
902
|
-
test('index unique on path', () => {
|
|
903
|
-
expect('query:ab->{index: b.astr, ab.astr}').toTranslate();
|
|
904
|
-
});
|
|
905
|
-
test('index join.*', () => {
|
|
906
|
-
expect('query:ab->{index: ab.*}').toTranslate();
|
|
907
|
-
});
|
|
908
|
-
test('index multiple', () => {
|
|
909
|
-
const model = new test_translator_1.TestTranslator('query:a->{index: af, astr}');
|
|
910
|
-
expect(model).toTranslate();
|
|
911
|
-
const q = model.getQuery(0);
|
|
912
|
-
expect(q).toBeDefined();
|
|
913
|
-
if (q) {
|
|
914
|
-
const index = q.pipeline[0];
|
|
915
|
-
expect(index.type).toBe('index');
|
|
916
|
-
expect(index.fields).toEqual(['af', 'astr']);
|
|
917
|
-
}
|
|
918
|
-
});
|
|
919
|
-
test('index star', () => {
|
|
920
|
-
const model = new test_translator_1.TestTranslator('query:a->{index: *, astr}');
|
|
921
|
-
expect(model).toTranslate();
|
|
922
|
-
const q = model.getQuery(0);
|
|
923
|
-
expect(q).toBeDefined();
|
|
924
|
-
if (q) {
|
|
925
|
-
const index = q.pipeline[0];
|
|
926
|
-
expect(index.type).toBe('index');
|
|
927
|
-
expect(index.fields).toEqual(['*', 'astr']);
|
|
928
|
-
}
|
|
929
|
-
});
|
|
930
|
-
test('index by', () => {
|
|
931
|
-
expect('query:a->{index: * by ai}').toTranslate();
|
|
932
|
-
});
|
|
933
|
-
test('index sampled', () => {
|
|
934
|
-
expect('query:a->{index: *; sample: true}').toTranslate();
|
|
935
|
-
});
|
|
936
|
-
test('index unsampled', () => {
|
|
937
|
-
expect('query:a->{index: *; sample: false}').toTranslate();
|
|
938
|
-
});
|
|
939
|
-
test('index sample-percent', () => {
|
|
940
|
-
const model = new test_translator_1.TestTranslator('query:a->{index: *; sample: 42%}');
|
|
941
|
-
expect(model).toTranslate();
|
|
942
|
-
const q = model.getQuery(0);
|
|
943
|
-
expect(q).toBeDefined();
|
|
944
|
-
if (q) {
|
|
945
|
-
const index = q.pipeline[0];
|
|
946
|
-
expect(index.type).toBe('index');
|
|
947
|
-
if (index.type === 'index') {
|
|
948
|
-
expect(index.sample).toEqual({ percent: 42 });
|
|
949
|
-
}
|
|
143
|
+
source: querySrc is from(
|
|
144
|
+
table('malloytest.flights')->{
|
|
145
|
+
group_by: origin
|
|
146
|
+
nest: nested is { group_by: destination }
|
|
950
147
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
});
|
|
958
|
-
test('top N by field', () => {
|
|
959
|
-
expect('query: a->{top: 5 by astr; group_by: astr}').toTranslate();
|
|
960
|
-
});
|
|
961
|
-
test('top N by expression', () => {
|
|
962
|
-
expect('query: ab->{top: 5 by ai + 1; group_by: ai}').toTranslate();
|
|
963
|
-
});
|
|
964
|
-
test('top N by field must be in the output space', () => expect('query: a->{top: 5 by af; group_by: astr}').translationToFailWith('Unknown field af in output space'));
|
|
965
|
-
test('limit N', () => {
|
|
966
|
-
expect('query: a->{ limit: 5; group_by: astr }').toTranslate();
|
|
967
|
-
});
|
|
968
|
-
test('order by', () => {
|
|
969
|
-
expect('query: a->{ order_by: astr; group_by: astr }').toTranslate();
|
|
970
|
-
});
|
|
971
|
-
test('order by preserved over refinement', () => {
|
|
972
|
-
expect(`
|
|
973
|
-
query: a1 is a -> { group_by: astr }
|
|
974
|
-
query: -> a1 { order_by: astr }
|
|
975
|
-
`).toTranslate();
|
|
976
|
-
});
|
|
977
|
-
test('order by must be in the output space', () => expect('query: a -> { order_by: af; group_by: astr }').translationToFailWith('Unknown field af in output space'));
|
|
978
|
-
test('order by asc', () => {
|
|
979
|
-
expect('query: a->{ order_by: astr asc; group_by: astr }').toTranslate();
|
|
980
|
-
});
|
|
981
|
-
test('order by desc', () => {
|
|
982
|
-
expect('query: a->{ order_by: astr desc; group_by: astr }').toTranslate();
|
|
983
|
-
});
|
|
984
|
-
test('order by N', () => {
|
|
985
|
-
expect('query: a->{ order_by: 1 asc; group_by: astr }').toTranslate();
|
|
986
|
-
});
|
|
987
|
-
test('order by multiple', () => {
|
|
988
|
-
expect(`
|
|
989
|
-
query: a->{
|
|
990
|
-
order_by: 1 asc, af desc
|
|
991
|
-
group_by: astr, af
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
source: refineQuerySrc is querySrc {
|
|
151
|
+
join_one: rejoin is querySrc on 7=8
|
|
152
|
+
query: broken is {
|
|
153
|
+
group_by: rejoin.nested.destination
|
|
992
154
|
}
|
|
155
|
+
}
|
|
993
156
|
`).toTranslate();
|
|
994
157
|
});
|
|
995
|
-
test('
|
|
996
|
-
expect('
|
|
997
|
-
});
|
|
998
|
-
test('having single', () => {
|
|
999
|
-
expect('query:ab->{ aggregate: acount; group_by: astr; having: acount > 10 }').toTranslate();
|
|
1000
|
-
});
|
|
1001
|
-
test('compound having still works', () => {
|
|
1002
|
-
expect('query:ab->{ aggregate: acount; having: acount > 10 and acount < 100 }').toTranslate();
|
|
158
|
+
test('popping out of embedding when not embedded', () => {
|
|
159
|
+
expect('}%').translationToFailWith(/extraneous input '}%' expecting/);
|
|
1003
160
|
});
|
|
1004
|
-
test('
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
const f = notb.filterList;
|
|
1017
|
-
expect(f).toBeDefined();
|
|
1018
|
-
if (f) {
|
|
1019
|
-
expect(f[0].code).toBe("astr != 'b'");
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
});
|
|
1023
|
-
test('field expressions preserve source formatting in code:', () => {
|
|
1024
|
-
const model = new test_translator_1.TestTranslator('source: notb is a + { dimension: d is 1 + 2 }');
|
|
1025
|
-
expect(model).toTranslate();
|
|
1026
|
-
const notb = model.getSourceDef('notb');
|
|
1027
|
-
expect(notb).toBeDefined();
|
|
1028
|
-
if (notb) {
|
|
1029
|
-
const d = notb.fields.find(f => f.as || f.name === 'd');
|
|
1030
|
-
expect(d).toBeDefined();
|
|
1031
|
-
expect(d === null || d === void 0 ? void 0 : d.type).toBe('number');
|
|
1032
|
-
if ((d === null || d === void 0 ? void 0 : d.type) === 'number') {
|
|
1033
|
-
expect(d.code).toBe('1 + 2');
|
|
1034
|
-
}
|
|
1035
|
-
}
|
|
1036
|
-
});
|
|
1037
|
-
test('nest single', () => {
|
|
1038
|
-
expect(`
|
|
1039
|
-
query: a->{
|
|
1040
|
-
group_by: ai
|
|
1041
|
-
nest: nestbystr is { group_by: astr; aggregate: N is count() }
|
|
1042
|
-
}
|
|
1043
|
-
`).toTranslate();
|
|
1044
|
-
});
|
|
1045
|
-
test('nest multiple', () => expect(`
|
|
1046
|
-
query: a->{
|
|
1047
|
-
group_by: ai
|
|
1048
|
-
nest:
|
|
1049
|
-
nestbystr is { group_by: astr; aggregate: N is count() },
|
|
1050
|
-
renest is { group_by: astr; aggregate: N is count() }
|
|
1051
|
-
}
|
|
1052
|
-
`).toTranslate());
|
|
1053
|
-
test('nest ref', () => expect('query: ab->{group_by: ai; nest: aturtle}').toTranslate());
|
|
1054
|
-
describe('extend block', () => {
|
|
1055
|
-
test('works with dimension', () => {
|
|
1056
|
-
expect('query: a -> { extend: { dimension: x is 1 }; group_by: x }').toTranslate();
|
|
1057
|
-
});
|
|
1058
|
-
test('works with measure', () => {
|
|
1059
|
-
expect('query: a -> { extend: { measure: x is count() }; aggregate: x }').toTranslate();
|
|
1060
|
-
});
|
|
1061
|
-
test('works with join_one', () => {
|
|
1062
|
-
expect('query: a -> { extend: { join_one: bb is b on bb.astr = astr }; group_by: bb.astr }').toTranslate();
|
|
1063
|
-
});
|
|
1064
|
-
test('works with join_many', () => {
|
|
1065
|
-
expect('query: a -> { extend: { join_many: b on astr = b.astr }; group_by: b.astr }').toTranslate();
|
|
1066
|
-
});
|
|
1067
|
-
test('works with join_cross', () => {
|
|
1068
|
-
expect('query: a -> { extend: { join_cross: b on true }; group_by: b.astr }').toTranslate();
|
|
1069
|
-
});
|
|
1070
|
-
test('works with multiple in one block', () => {
|
|
1071
|
-
expect('query: a -> { extend: { dimension: x is 1, y is 2 }; group_by: x, y }').toTranslate();
|
|
1072
|
-
});
|
|
1073
|
-
test('works with multiple blocks', () => {
|
|
1074
|
-
expect('query: a -> { extend: { dimension: x is 1; dimension: y is 2; measure: c is count() }; group_by: x, y; aggregate: c }').toTranslate();
|
|
1075
|
-
});
|
|
1076
|
-
});
|
|
1077
|
-
describe('declare/query join warnings', () => {
|
|
1078
|
-
test('declare warning in query', () => {
|
|
1079
|
-
expect((0, test_translator_1.markSource) `##! m4warnings
|
|
1080
|
-
run: a -> { ${'declare: x is 1'}; group_by: x }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
|
|
1081
|
-
});
|
|
1082
|
-
test('declare warning in source', () => {
|
|
1083
|
-
expect((0, test_translator_1.markSource) `##! m4warnings
|
|
1084
|
-
source: a2 is a extend { ${'declare: x is 1'} }`).toTranslateWithWarnings('`declare:` is deprecated; use `dimension:` or `measure:` inside a source or `extend:` block');
|
|
1085
|
-
});
|
|
1086
|
-
test('joins in query', () => {
|
|
1087
|
-
expect((0, test_translator_1.markSource) `##! m4warnings
|
|
1088
|
-
run: a -> { ${'join_one: b on true'}; ${'join_many: c is b on true'}; ${'join_cross: d is b on true'}; group_by: b.astr }`).toTranslateWithWarnings('Joins in queries are deprecated, move into an `extend:` block.', 'Joins in queries are deprecated, move into an `extend:` block.', 'Joins in queries are deprecated, move into an `extend:` block.');
|
|
1089
|
-
});
|
|
1090
|
-
});
|
|
1091
|
-
test('refine query with extended source', () => {
|
|
1092
|
-
const m = new test_translator_1.TestTranslator(`
|
|
1093
|
-
source: nab is ab {
|
|
1094
|
-
query: xturtle is aturtle + {
|
|
1095
|
-
declare: aratio is ai / acount
|
|
1096
|
-
}
|
|
1097
|
-
}
|
|
1098
|
-
query: nab -> xturtle + { aggregate: aratio }
|
|
1099
|
-
`);
|
|
1100
|
-
expect(m).toTranslate();
|
|
1101
|
-
const t = m.translate();
|
|
1102
|
-
if (t.translated) {
|
|
1103
|
-
const q = t.translated.queryList[0].pipeline[0];
|
|
1104
|
-
expect(q).toBeDefined();
|
|
1105
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1106
|
-
expect(q.extendSource.length).toBe(1);
|
|
1107
|
-
const f = q.extendSource[0];
|
|
1108
|
-
expect(f.type).toBe('number');
|
|
1109
|
-
if (f.type === 'number') {
|
|
1110
|
-
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
else {
|
|
1114
|
-
fail('Did not generate extendSource');
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
});
|
|
1118
|
-
test('refine query source with field', () => {
|
|
1119
|
-
const m = new test_translator_1.TestTranslator(`
|
|
1120
|
-
query: ab -> aturtle + {
|
|
1121
|
-
declare: aratio is ai / acount
|
|
1122
|
-
aggregate: aratio
|
|
1123
|
-
}
|
|
1124
|
-
`);
|
|
1125
|
-
expect(m).toTranslate();
|
|
1126
|
-
const t = m.translate();
|
|
1127
|
-
if (t.translated) {
|
|
1128
|
-
const q = t.translated.queryList[0].pipeline[0];
|
|
1129
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1130
|
-
expect(q.extendSource.length).toBe(1);
|
|
1131
|
-
const f = q.extendSource[0];
|
|
1132
|
-
expect(f.type).toBe('number');
|
|
1133
|
-
if (f.type === 'number') {
|
|
1134
|
-
expect((0, model_1.expressionIsCalculation)(f.expressionType)).toBeTruthy();
|
|
1135
|
-
}
|
|
1136
|
-
}
|
|
1137
|
-
else {
|
|
1138
|
-
fail('Did not generate extendSource');
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
});
|
|
1142
|
-
test('refine query source with join', () => {
|
|
1143
|
-
const m = new test_translator_1.TestTranslator(`
|
|
1144
|
-
query: ab -> aturtle + {
|
|
1145
|
-
join_one: bb is b on bb.astr = astr
|
|
1146
|
-
group_by: foo is bb.astr
|
|
1147
|
-
}
|
|
1148
|
-
`);
|
|
1149
|
-
expect(m).toTranslate();
|
|
1150
|
-
const t = m.translate();
|
|
1151
|
-
if (t.translated) {
|
|
1152
|
-
const q = t.translated.queryList[0].pipeline[0];
|
|
1153
|
-
if (q.type === 'reduce' && q.extendSource) {
|
|
1154
|
-
expect(q.extendSource.length).toBe(1);
|
|
1155
|
-
expect(q.extendSource[0].type).toBe('struct');
|
|
1156
|
-
}
|
|
1157
|
-
else {
|
|
1158
|
-
fail('Did not generate extendSource');
|
|
1159
|
-
}
|
|
1160
|
-
}
|
|
1161
|
-
});
|
|
1162
|
-
});
|
|
1163
|
-
describe('literals', () => {
|
|
1164
|
-
test('integer', () => {
|
|
1165
|
-
expect((0, test_translator_1.expr) `42`).toTranslate();
|
|
1166
|
-
});
|
|
1167
|
-
test('string', () => {
|
|
1168
|
-
const m = new test_translator_1.BetaExpression("'forty two'");
|
|
1169
|
-
expect(m).toTranslate();
|
|
1170
|
-
const m42 = m.generated().value[0];
|
|
1171
|
-
expect(m42).toMatchObject({ literal: 'forty two' });
|
|
1172
|
-
});
|
|
1173
|
-
test('string with quoted quote', () => {
|
|
1174
|
-
const str = "'Isn" + '\\' + "'t this nice'";
|
|
1175
|
-
expect(new test_translator_1.BetaExpression(str)).toTranslate();
|
|
1176
|
-
});
|
|
1177
|
-
test('string with quoted backslash', () => {
|
|
1178
|
-
const str = "'Is " + '\\' + '\\' + " nice'";
|
|
1179
|
-
expect(new test_translator_1.BetaExpression(str)).toTranslate();
|
|
1180
|
-
});
|
|
1181
|
-
const literalTimes = [
|
|
1182
|
-
['@1960', 'date', 'year', { literal: '1960-01-01' }],
|
|
1183
|
-
['@1960-Q2', 'date', 'quarter', { literal: '1960-04-01' }],
|
|
1184
|
-
['@1960-06', 'date', 'month', { literal: '1960-06-01' }],
|
|
1185
|
-
['@1960-06-26-WK', 'date', 'week', { literal: '1960-06-26' }],
|
|
1186
|
-
['@1960-06-30', 'date', 'day', { literal: '1960-06-30' }],
|
|
1187
|
-
['@1960-06-30 10', 'timestamp', 'hour', { literal: '1960-06-30 10:00:00' }],
|
|
1188
|
-
[
|
|
1189
|
-
'@1960-06-30 10:30',
|
|
1190
|
-
'timestamp',
|
|
1191
|
-
'minute',
|
|
1192
|
-
{ literal: '1960-06-30 10:30:00' },
|
|
1193
|
-
],
|
|
1194
|
-
[
|
|
1195
|
-
'@1960-06-30 10:30:00',
|
|
1196
|
-
'timestamp',
|
|
1197
|
-
undefined,
|
|
1198
|
-
{ literal: '1960-06-30 10:30:00' },
|
|
1199
|
-
],
|
|
1200
|
-
[
|
|
1201
|
-
'@1960-06-30 10:30:00.123',
|
|
1202
|
-
'timestamp',
|
|
1203
|
-
undefined,
|
|
1204
|
-
{ literal: '1960-06-30 10:30:00.123' },
|
|
1205
|
-
],
|
|
1206
|
-
[
|
|
1207
|
-
'@1960-06-30T10:30:00',
|
|
1208
|
-
'timestamp',
|
|
1209
|
-
undefined,
|
|
1210
|
-
{ literal: '1960-06-30 10:30:00' },
|
|
1211
|
-
],
|
|
1212
|
-
[
|
|
1213
|
-
'@1960-06-30 10:30:00[America/Los_Angeles]',
|
|
1214
|
-
'timestamp',
|
|
1215
|
-
undefined,
|
|
1216
|
-
{
|
|
1217
|
-
literal: '1960-06-30 10:30:00',
|
|
1218
|
-
timezone: 'America/Los_Angeles',
|
|
1219
|
-
},
|
|
1220
|
-
],
|
|
1221
|
-
];
|
|
1222
|
-
test.each(literalTimes)('%s', (expr, timeType, timeframe, result) => {
|
|
1223
|
-
const exprModel = new test_translator_1.BetaExpression(expr);
|
|
1224
|
-
expect(exprModel).toTranslate();
|
|
1225
|
-
const ir = exprModel.generated();
|
|
1226
|
-
expect(ir.dataType).toEqual(timeType);
|
|
1227
|
-
if (timeframe) {
|
|
1228
|
-
expect((0, granular_result_1.isGranularResult)(ir)).toBeTruthy();
|
|
1229
|
-
if ((0, granular_result_1.isGranularResult)(ir)) {
|
|
1230
|
-
expect(ir.timeframe).toEqual(timeframe);
|
|
1231
|
-
}
|
|
1232
|
-
}
|
|
1233
|
-
else {
|
|
1234
|
-
expect((0, granular_result_1.isGranularResult)(ir)).toBeFalsy();
|
|
1235
|
-
}
|
|
1236
|
-
expect(ir.value[0]).toEqual(expect.objectContaining(result));
|
|
1237
|
-
});
|
|
1238
|
-
const morphicLiterals = [
|
|
1239
|
-
['@1960', '1960-01-01 00:00:00'],
|
|
1240
|
-
['@1960-Q2', '1960-04-01 00:00:00'],
|
|
1241
|
-
['@1960-06', '1960-06-01 00:00:00'],
|
|
1242
|
-
['@1960-06-26-Wk', '1960-06-26 00:00:00'],
|
|
1243
|
-
['@1960-06-30', '1960-06-30 00:00:00'],
|
|
1244
|
-
['@1960-06-30 00:00', undefined],
|
|
1245
|
-
];
|
|
1246
|
-
test.each(morphicLiterals)('morphic value for %s is %s', (expr, morphic) => {
|
|
1247
|
-
const exprModel = new test_translator_1.BetaExpression(expr);
|
|
1248
|
-
expect(exprModel).toTranslate();
|
|
1249
|
-
const ir = exprModel.generated();
|
|
1250
|
-
const morphTo = ir.morphic && ir.morphic['timestamp'];
|
|
1251
|
-
if (morphic) {
|
|
1252
|
-
expect(morphTo).toBeDefined();
|
|
1253
|
-
if (morphTo) {
|
|
1254
|
-
expect(morphTo[0]).toEqual(expect.objectContaining({ literal: morphic }));
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
else {
|
|
1258
|
-
expect(morphTo).toBeUndefined();
|
|
1259
|
-
}
|
|
1260
|
-
});
|
|
1261
|
-
test('minute+locale', () => {
|
|
1262
|
-
expect((0, test_translator_1.expr) `@1960-06-30 10:30[America/Los_Angeles]`).toTranslate();
|
|
1263
|
-
});
|
|
1264
|
-
test('second 8601', () => {
|
|
1265
|
-
expect((0, test_translator_1.expr) `@1960-06-30T10:30:31`).toTranslate();
|
|
1266
|
-
});
|
|
1267
|
-
test('null', () => {
|
|
1268
|
-
expect((0, test_translator_1.expr) `null`).toTranslate();
|
|
1269
|
-
});
|
|
1270
|
-
test('now', () => {
|
|
1271
|
-
expect((0, test_translator_1.expr) `now`).toTranslate();
|
|
1272
|
-
});
|
|
1273
|
-
test('true', () => {
|
|
1274
|
-
expect((0, test_translator_1.expr) `true`).toTranslate();
|
|
1275
|
-
});
|
|
1276
|
-
test('false', () => {
|
|
1277
|
-
expect((0, test_translator_1.expr) `false`).toTranslate();
|
|
1278
|
-
});
|
|
1279
|
-
test('regex', () => {
|
|
1280
|
-
expect((0, test_translator_1.expr) `r'RegularExpression'`).toTranslate();
|
|
1281
|
-
});
|
|
1282
|
-
});
|
|
1283
|
-
describe('expressions', () => {
|
|
1284
|
-
describe('timeframes', () => {
|
|
1285
|
-
const timeframes = [
|
|
1286
|
-
'second',
|
|
1287
|
-
'minute',
|
|
1288
|
-
'hour',
|
|
1289
|
-
'day',
|
|
1290
|
-
'week',
|
|
1291
|
-
'month',
|
|
1292
|
-
'quarter',
|
|
1293
|
-
'year',
|
|
1294
|
-
];
|
|
1295
|
-
test.each(timeframes.map(x => [x]))('truncate %s', unit => {
|
|
1296
|
-
expect(new test_translator_1.BetaExpression(`ats.${unit}`)).toParse();
|
|
1297
|
-
});
|
|
1298
|
-
// mtoy todo units missing: implement, or document
|
|
1299
|
-
const diffable = ['second', 'minute', 'hour', 'day'];
|
|
1300
|
-
test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
|
|
1301
|
-
expect(new test_translator_1.BetaExpression(`${unit}(@2021 to ats)`)).toParse();
|
|
1302
|
-
});
|
|
1303
|
-
test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
|
|
1304
|
-
expect(new test_translator_1.BetaExpression(`${unit}(ats to @2030)`)).toParse();
|
|
1305
|
-
});
|
|
1306
|
-
});
|
|
1307
|
-
test('field name', () => {
|
|
1308
|
-
expect((0, test_translator_1.expr) `astr`).toTranslate();
|
|
1309
|
-
});
|
|
1310
|
-
test('function call', () => {
|
|
1311
|
-
expect((0, test_translator_1.expr) `concat('foo')`).toTranslate();
|
|
1312
|
-
});
|
|
1313
|
-
describe('operators', () => {
|
|
1314
|
-
test('addition', () => {
|
|
1315
|
-
expect((0, test_translator_1.expr) `42 + 7`).toTranslate();
|
|
1316
|
-
});
|
|
1317
|
-
test('subtraction', () => {
|
|
1318
|
-
expect((0, test_translator_1.expr) `42 - 7`).toTranslate();
|
|
1319
|
-
});
|
|
1320
|
-
test('multiplication', () => {
|
|
1321
|
-
expect((0, test_translator_1.expr) `42 * 7`).toTranslate();
|
|
1322
|
-
});
|
|
1323
|
-
test('mod', () => {
|
|
1324
|
-
expect((0, test_translator_1.expr) `42 % 7`).toTranslate();
|
|
1325
|
-
});
|
|
1326
|
-
test('division', () => {
|
|
1327
|
-
expect((0, test_translator_1.expr) `42 / 7`).toTranslate();
|
|
1328
|
-
});
|
|
1329
|
-
test('unary negation', () => {
|
|
1330
|
-
expect((0, test_translator_1.expr) `- ai`).toTranslate();
|
|
1331
|
-
});
|
|
1332
|
-
test('equal', () => {
|
|
1333
|
-
expect((0, test_translator_1.expr) `42 = 7`).toTranslate();
|
|
1334
|
-
});
|
|
1335
|
-
test('not equal', () => {
|
|
1336
|
-
expect((0, test_translator_1.expr) `42 != 7`).toTranslate();
|
|
1337
|
-
});
|
|
1338
|
-
test('greater than', () => {
|
|
1339
|
-
expect((0, test_translator_1.expr) `42 > 7`).toTranslate();
|
|
1340
|
-
});
|
|
1341
|
-
test('greater than or equal', () => {
|
|
1342
|
-
expect((0, test_translator_1.expr) `42 >= 7`).toTranslate();
|
|
1343
|
-
});
|
|
1344
|
-
test('less than or equal', () => {
|
|
1345
|
-
expect((0, test_translator_1.expr) `42 <= 7`).toTranslate();
|
|
1346
|
-
});
|
|
1347
|
-
test('less than', () => {
|
|
1348
|
-
expect((0, test_translator_1.expr) `42 < 7`).toTranslate();
|
|
1349
|
-
});
|
|
1350
|
-
test('match', () => {
|
|
1351
|
-
expect((0, test_translator_1.expr) `'forty-two' ~ 'fifty-four'`).toTranslate();
|
|
1352
|
-
});
|
|
1353
|
-
test('not match', () => {
|
|
1354
|
-
expect((0, test_translator_1.expr) `'forty-two' !~ 'fifty-four'`).toTranslate();
|
|
1355
|
-
});
|
|
1356
|
-
test('apply', () => {
|
|
1357
|
-
expect((0, test_translator_1.expr) `'forty-two' ? 'fifty-four'`).toTranslate();
|
|
1358
|
-
});
|
|
1359
|
-
test('not', () => {
|
|
1360
|
-
expect((0, test_translator_1.expr) `not true`).toTranslate();
|
|
1361
|
-
});
|
|
1362
|
-
test('and', () => {
|
|
1363
|
-
expect((0, test_translator_1.expr) `true and false`).toTranslate();
|
|
1364
|
-
});
|
|
1365
|
-
test('or', () => {
|
|
1366
|
-
expect((0, test_translator_1.expr) `true or false`).toTranslate();
|
|
1367
|
-
});
|
|
1368
|
-
test('null-check (??)', () => {
|
|
1369
|
-
expect((0, test_translator_1.expr) `ai ?? 7`).toTranslate();
|
|
1370
|
-
});
|
|
1371
|
-
test('coalesce type mismatch', () => {
|
|
1372
|
-
expect(new test_translator_1.BetaExpression('ai ?? @2003')).translationToFailWith('Mismatched types for coalesce (number, date)');
|
|
1373
|
-
});
|
|
1374
|
-
test('disallow date OP number', () => {
|
|
1375
|
-
expect(new test_translator_1.BetaExpression('@2001 = 7')).translationToFailWith('Cannot compare a date to a number');
|
|
1376
|
-
});
|
|
1377
|
-
test('disallow date OP timestamp', () => {
|
|
1378
|
-
expect(new test_translator_1.BetaExpression('ad = ats')).translationToFailWith('Cannot compare a date to a timestamp');
|
|
1379
|
-
});
|
|
1380
|
-
test('disallow interval from date to timestamp', () => {
|
|
1381
|
-
expect(new test_translator_1.BetaExpression('days(ad to ats)')).translationToFailWith('Cannot measure from date to timestamp');
|
|
1382
|
-
});
|
|
1383
|
-
test('comparison promotes date literal to timestamp', () => {
|
|
1384
|
-
expect((0, test_translator_1.expr) `@2001 = ats`).toTranslate();
|
|
1385
|
-
});
|
|
1386
|
-
test('can apply range to date', () => {
|
|
1387
|
-
expect((0, test_translator_1.expr) `ad ? @2001 for 1 day`).toTranslate();
|
|
1388
|
-
});
|
|
1389
|
-
const noOffset = ['second', 'minute', 'hour'];
|
|
1390
|
-
test.each(noOffset.map(x => [x]))('disallow date delta %s', unit => {
|
|
1391
|
-
expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).translationToFailWith(`Cannot offset date by ${unit}`);
|
|
1392
|
-
});
|
|
1393
|
-
test('apply with parens', () => {
|
|
1394
|
-
expect((0, test_translator_1.expr) `ai ? (> 1 & < 100)`).toTranslate();
|
|
1395
|
-
});
|
|
1396
|
-
});
|
|
1397
|
-
test('filtered measure', () => {
|
|
1398
|
-
expect((0, test_translator_1.expr) `acount {? astr = 'why?' }`).toTranslate();
|
|
1399
|
-
});
|
|
1400
|
-
test('filtered ungrouped aggregate', () => {
|
|
1401
|
-
expect(`
|
|
1402
|
-
query: a -> {
|
|
1403
|
-
group_by: ai
|
|
1404
|
-
aggregate: x is all(avg(ai)) { where: true }
|
|
1405
|
-
}
|
|
1406
|
-
`).toTranslate();
|
|
1407
|
-
});
|
|
1408
|
-
test('shortcut filtered measure m4warning', () => {
|
|
1409
|
-
expect(`
|
|
1410
|
-
##! m4warnings
|
|
1411
|
-
run: a -> {
|
|
1412
|
-
group_by: ai
|
|
1413
|
-
aggregate: x is avg(ai) {? astr = 'why?' }
|
|
1414
|
-
}
|
|
1415
|
-
`).toTranslateWithWarnings('Filter shortcut `{? condition }` is deprecated; use `{ where: condition } instead');
|
|
1416
|
-
});
|
|
1417
|
-
test('correctly flags filtered scalar', () => {
|
|
1418
|
-
const e = new test_translator_1.BetaExpression('ai { where: true }');
|
|
1419
|
-
expect(e).translationToFailWith('Filtered expression requires an aggregate computation');
|
|
1420
|
-
});
|
|
1421
|
-
test('correctly flags filtered analytic', () => {
|
|
1422
|
-
expect((0, test_translator_1.markSource) `
|
|
1423
|
-
query: a -> {
|
|
1424
|
-
group_by: ai
|
|
1425
|
-
calculate: l is lag(ai) { where: true }
|
|
1426
|
-
}
|
|
1427
|
-
`).translationToFailWith('Filtered expression requires an aggregate computation');
|
|
1428
|
-
});
|
|
1429
|
-
describe('aggregate forms', () => {
|
|
1430
|
-
test('count', () => {
|
|
1431
|
-
expect((0, test_translator_1.expr) `count()`).toTranslate();
|
|
1432
|
-
});
|
|
1433
|
-
test('count distinct', () => {
|
|
1434
|
-
expect((0, test_translator_1.expr) `count(distinct astr)`).toTranslate();
|
|
1435
|
-
});
|
|
1436
|
-
test('join.count()', () => {
|
|
1437
|
-
expect((0, test_translator_1.expr) `b.count()`).toTranslate();
|
|
1438
|
-
});
|
|
1439
|
-
for (const f of ['sum', 'min', 'max', 'avg']) {
|
|
1440
|
-
const fOfT = `${f}(af)`;
|
|
1441
|
-
test(fOfT, () => {
|
|
1442
|
-
expect(new test_translator_1.BetaExpression(fOfT)).toTranslate();
|
|
1443
|
-
});
|
|
1444
|
-
if (f !== 'min' && f !== 'max') {
|
|
1445
|
-
const joinDot = `b.af.${f}()`;
|
|
1446
|
-
test(joinDot, () => {
|
|
1447
|
-
expect(new test_translator_1.BetaExpression(joinDot)).toTranslate();
|
|
1448
|
-
});
|
|
1449
|
-
const joinAgg = `b.${f}(af)`;
|
|
1450
|
-
test(joinAgg, () => {
|
|
1451
|
-
expect(new test_translator_1.BetaExpression(joinAgg)).toTranslate();
|
|
1452
|
-
});
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
});
|
|
1456
|
-
describe('pick statements', () => {
|
|
1457
|
-
test('full', () => {
|
|
1458
|
-
expect((0, test_translator_1.expr) `
|
|
1459
|
-
pick 'the answer' when ai = 42
|
|
1460
|
-
pick 'the questionable answer' when ai = 54
|
|
1461
|
-
else 'random'
|
|
1462
|
-
`).toTranslate();
|
|
1463
|
-
});
|
|
1464
|
-
test('applied', () => {
|
|
1465
|
-
expect((0, test_translator_1.expr) `
|
|
1466
|
-
astr ?
|
|
1467
|
-
pick 'the answer' when = '42'
|
|
1468
|
-
pick 'the questionable answer' when = '54'
|
|
1469
|
-
else 'random'
|
|
1470
|
-
`).toTranslate();
|
|
1471
|
-
});
|
|
1472
|
-
test('filtering', () => {
|
|
1473
|
-
expect((0, test_translator_1.expr) `astr ? pick 'missing value' when NULL`).toTranslate();
|
|
1474
|
-
});
|
|
1475
|
-
test('null branch with else', () => {
|
|
1476
|
-
expect("astr ? pick null when = '42' else 3").toReturnType('number');
|
|
1477
|
-
});
|
|
1478
|
-
test('null branch no else', () => {
|
|
1479
|
-
expect("astr ? pick null when = '42'").toReturnType('string');
|
|
1480
|
-
});
|
|
1481
|
-
test('null branch no apply', () => {
|
|
1482
|
-
expect('pick null when 1 = 1 else 3').toReturnType('number');
|
|
1483
|
-
});
|
|
1484
|
-
test('tiering', () => {
|
|
1485
|
-
expect((0, test_translator_1.expr) `
|
|
1486
|
-
ai ?
|
|
1487
|
-
pick 1 when < 10
|
|
1488
|
-
pick 10 when < 100
|
|
1489
|
-
pick 100 when < 1000
|
|
1490
|
-
else 10000
|
|
1491
|
-
`).toTranslate();
|
|
1492
|
-
});
|
|
1493
|
-
test('transforming', () => {
|
|
1494
|
-
expect((0, test_translator_1.expr) `
|
|
1495
|
-
ai ?
|
|
1496
|
-
pick 'small' when < 10
|
|
1497
|
-
pick 'medium' when < 100
|
|
1498
|
-
else 'large'
|
|
1499
|
-
`).toTranslate();
|
|
1500
|
-
});
|
|
1501
|
-
test('when single values', () => {
|
|
1502
|
-
expect((0, test_translator_1.expr) `
|
|
1503
|
-
ai ?
|
|
1504
|
-
pick 'one' when 1
|
|
1505
|
-
else 'a lot'
|
|
1506
|
-
`).toTranslate();
|
|
1507
|
-
});
|
|
1508
|
-
test('n-ary without else', () => {
|
|
1509
|
-
expect(`
|
|
1510
|
-
source: na is a + { dimension: d is
|
|
1511
|
-
pick 7 when true and true
|
|
1512
|
-
}
|
|
1513
|
-
`).translationToFailWith("pick incomplete, missing 'else'");
|
|
1514
|
-
});
|
|
1515
|
-
test('n-ary with mismatch when clauses', () => {
|
|
1516
|
-
expect((0, test_translator_1.markSource) `
|
|
1517
|
-
source: na is a + { dimension: d is
|
|
1518
|
-
pick 7 when true and true
|
|
1519
|
-
pick '7' when true or true
|
|
1520
|
-
else 7
|
|
1521
|
-
}
|
|
1522
|
-
`).translationToFailWith("pick type 'string', expected 'number'");
|
|
1523
|
-
});
|
|
1524
|
-
test('n-ary with mismatched else clause', () => {
|
|
1525
|
-
expect((0, test_translator_1.markSource) `
|
|
1526
|
-
source: na is a + { dimension: d is
|
|
1527
|
-
pick 7 when true and true
|
|
1528
|
-
else '7'
|
|
1529
|
-
}
|
|
1530
|
-
`).translationToFailWith("else type 'string', expected 'number'");
|
|
1531
|
-
});
|
|
1532
|
-
test('applied else mismatch', () => {
|
|
1533
|
-
expect((0, test_translator_1.markSource) `
|
|
1534
|
-
source: na is a + { dimension: d is
|
|
1535
|
-
7 ? pick 7 when 7 else 'not seven'
|
|
1536
|
-
}
|
|
1537
|
-
`).translationToFailWith("else type 'string', expected 'number'");
|
|
1538
|
-
});
|
|
1539
|
-
test('applied default mismatch', () => {
|
|
1540
|
-
expect((0, test_translator_1.markSource) `
|
|
1541
|
-
source: na is a + { dimension: d is
|
|
1542
|
-
7 ? pick 'seven' when 7
|
|
1543
|
-
}
|
|
1544
|
-
`).translationToFailWith("pick default type 'number', expected 'string'");
|
|
1545
|
-
});
|
|
1546
|
-
test('applied when mismatch', () => {
|
|
1547
|
-
expect((0, test_translator_1.markSource) `
|
|
1548
|
-
source: na is a + { dimension: d is
|
|
1549
|
-
7 ? pick 'seven' when 7 pick 6 when 6
|
|
1550
|
-
}
|
|
1551
|
-
`).translationToFailWith("pick type 'number', expected 'string'");
|
|
1552
|
-
});
|
|
1553
|
-
});
|
|
1554
|
-
test('paren and applied div', () => {
|
|
1555
|
-
var _a, _b;
|
|
1556
|
-
const modelSrc = 'query: z is a -> { group_by: x is 1+(3/4) }';
|
|
1557
|
-
const m = new test_translator_1.TestTranslator(modelSrc);
|
|
1558
|
-
expect(m).toTranslate();
|
|
1559
|
-
const queryDef = (_b = (_a = m.translate()) === null || _a === void 0 ? void 0 : _a.translated) === null || _b === void 0 ? void 0 : _b.modelDef.contents['z'];
|
|
1560
|
-
expect(queryDef).toBeDefined();
|
|
1561
|
-
expect(queryDef === null || queryDef === void 0 ? void 0 : queryDef.type).toBe('query');
|
|
1562
|
-
if (queryDef && queryDef.type === 'query') {
|
|
1563
|
-
const x = queryDef.pipeline[0].fields[0];
|
|
1564
|
-
if (typeof x !== 'string' &&
|
|
1565
|
-
!(0, model_1.isFilteredAliasedName)(x) &&
|
|
1566
|
-
(0, model_1.isFieldTypeDef)(x) &&
|
|
1567
|
-
x.type === 'number' &&
|
|
1568
|
-
x.e) {
|
|
1569
|
-
expect(x).toMatchObject({
|
|
1570
|
-
'e': [
|
|
1571
|
-
{
|
|
1572
|
-
'function': 'numberLiteral',
|
|
1573
|
-
'literal': '1',
|
|
1574
|
-
'type': 'dialect',
|
|
1575
|
-
},
|
|
1576
|
-
// TODO not sure why there are TWO sets of parentheses... A previous version of this test
|
|
1577
|
-
// just checked that there were ANY parens, so that went under the radar. Not fixing now.
|
|
1578
|
-
'+((',
|
|
1579
|
-
{
|
|
1580
|
-
'denominator': [
|
|
1581
|
-
{
|
|
1582
|
-
'function': 'numberLiteral',
|
|
1583
|
-
'literal': '4',
|
|
1584
|
-
'type': 'dialect',
|
|
1585
|
-
},
|
|
1586
|
-
],
|
|
1587
|
-
'function': 'div',
|
|
1588
|
-
'numerator': [
|
|
1589
|
-
{
|
|
1590
|
-
'function': 'numberLiteral',
|
|
1591
|
-
'literal': '3',
|
|
1592
|
-
'type': 'dialect',
|
|
1593
|
-
},
|
|
1594
|
-
],
|
|
1595
|
-
'type': 'dialect',
|
|
1596
|
-
},
|
|
1597
|
-
'))',
|
|
1598
|
-
],
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
else {
|
|
1602
|
-
fail('expression with parens compiled oddly');
|
|
1603
|
-
}
|
|
1604
|
-
}
|
|
1605
|
-
});
|
|
1606
|
-
test.each([
|
|
1607
|
-
['ats', 'timestamp'],
|
|
1608
|
-
['ad', 'date'],
|
|
1609
|
-
['ai', 'number'],
|
|
1610
|
-
['astr', 'string'],
|
|
1611
|
-
['abool', 'boolean'],
|
|
1612
|
-
])('Can compare field %s (type %s) to NULL', (name, _datatype) => {
|
|
1613
|
-
expect((0, test_translator_1.expr) `${name} = NULL`).toTranslate();
|
|
1614
|
-
});
|
|
1615
|
-
});
|
|
1616
|
-
describe('unspported fields in schema', () => {
|
|
1617
|
-
test('unsupported reference in result allowed', () => {
|
|
1618
|
-
const uModel = new test_translator_1.TestTranslator('query: a->{ group_by: aun }');
|
|
1619
|
-
expect(uModel).toTranslate();
|
|
1620
|
-
});
|
|
1621
|
-
test('unsupported reference can be compared to NULL', () => {
|
|
1622
|
-
const uModel = new test_translator_1.TestTranslator('query: a->{ where: aun != NULL; project: * }');
|
|
1623
|
-
expect(uModel).toTranslate();
|
|
1624
|
-
});
|
|
1625
|
-
test('flag unsupported equality', () => {
|
|
1626
|
-
// because we don't know if the two unsupported types are comparable
|
|
1627
|
-
const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun = b.aun project: * }');
|
|
1628
|
-
expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
|
|
1629
|
-
});
|
|
1630
|
-
test('flag unsupported compare', () => {
|
|
1631
|
-
// because we don't know if the two unsupported types are comparable
|
|
1632
|
-
const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aun > b.aun project: * }');
|
|
1633
|
-
expect(uModel).translationToFailWith('Unsupported type not allowed in expression');
|
|
1634
|
-
});
|
|
1635
|
-
test('allow unsupported equality when raw types match', () => {
|
|
1636
|
-
const uModel = new test_translator_1.TestTranslator('query: ab->{ where: aweird = b.aweird project: * }');
|
|
1637
|
-
expect(uModel).toTranslate();
|
|
1638
|
-
});
|
|
1639
|
-
test('flag not applied to unsupported', () => {
|
|
1640
|
-
const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is not aun }');
|
|
1641
|
-
expect(uModel).translationToFailWith("'not' Can't use type unsupported");
|
|
1642
|
-
});
|
|
1643
|
-
test('allow unsupported to be cast', () => {
|
|
1644
|
-
const uModel = new test_translator_1.TestTranslator('source: x is a { dimension: notUn is aun::string }');
|
|
1645
|
-
expect(uModel).toTranslate();
|
|
1646
|
-
});
|
|
1647
|
-
});
|
|
1648
|
-
describe('cast', () => {
|
|
1649
|
-
// The "+ 1"s are there to make sure the result is of type 'number'
|
|
1650
|
-
test('sql cast', () => {
|
|
1651
|
-
expect((0, test_translator_1.expr) `ai::'integer' + 1`).toTranslate();
|
|
1652
|
-
expect((0, test_translator_1.expr) `ai::"integer" + 1`).toTranslate();
|
|
1653
|
-
expect((0, test_translator_1.expr) `ai::"""integer""" + 1`).toTranslate();
|
|
1654
|
-
});
|
|
1655
|
-
test('sql safe cast', () => {
|
|
1656
|
-
expect((0, test_translator_1.expr) `astr:::'integer' + 1`).toTranslate();
|
|
1657
|
-
expect((0, test_translator_1.expr) `astr:::"integer" + 1`).toTranslate();
|
|
1658
|
-
expect((0, test_translator_1.expr) `astr:::"""integer""" + 1`).toTranslate();
|
|
1659
|
-
});
|
|
1660
|
-
test('malloy cast', () => {
|
|
1661
|
-
expect((0, test_translator_1.expr) `astr::number + 1`).toTranslate();
|
|
1662
|
-
});
|
|
1663
|
-
test('malloy safe cast', () => {
|
|
1664
|
-
expect((0, test_translator_1.expr) `astr:::number + 1`).toTranslate();
|
|
1665
|
-
});
|
|
1666
|
-
test('sql cast illegal type name', () => {
|
|
1667
|
-
expect((0, test_translator_1.expr) `astr::"stuff 'n' things"`).translationToFailWith("Cast type `stuff 'n' things` is invalid for standardsql dialect");
|
|
1668
|
-
});
|
|
1669
|
-
});
|
|
1670
|
-
describe('error handling', () => {
|
|
1671
|
-
test('field and query with same name does not overflow', () => {
|
|
1672
|
-
expect(`
|
|
1673
|
-
source: flights is table('malloytest.flights') {
|
|
1674
|
-
query: carrier is { group_by: carrier }
|
|
1675
|
-
}
|
|
1676
|
-
`).translationToFailWith("Cannot redefine 'carrier'");
|
|
1677
|
-
});
|
|
1678
|
-
test('redefine source', () => {
|
|
1679
|
-
expect((0, test_translator_1.markSource) `
|
|
1680
|
-
source: airports is table('malloytest.airports') + {
|
|
1681
|
-
primary_key: code
|
|
1682
|
-
}
|
|
1683
|
-
source: airports is table('malloytest.airports') + {
|
|
1684
|
-
primary_key: code
|
|
1685
|
-
}
|
|
1686
|
-
`).translationToFailWith("Cannot redefine 'airports'");
|
|
1687
|
-
});
|
|
1688
|
-
test('query from undefined source', () => {
|
|
1689
|
-
expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y }`).translationToFailWith("Undefined query or source 'x'");
|
|
1690
|
-
});
|
|
1691
|
-
test('query with expression from undefined source', () => {
|
|
1692
|
-
// Regression check: Once upon a time this died with an exception even
|
|
1693
|
-
// when "query: x->{ group_by: y}" (above) generated the correct error.
|
|
1694
|
-
expect((0, test_translator_1.markSource) `query: ${'x'}->{ project: y is z / 2 }`).translationToFailWith("Undefined query or source 'x'");
|
|
1695
|
-
});
|
|
1696
|
-
test('join reference before definition', () => {
|
|
1697
|
-
expect((0, test_translator_1.markSource) `
|
|
1698
|
-
source: newAB is a { join_one: newB is ${'bb'} on astring }
|
|
1699
|
-
source: newB is b
|
|
1700
|
-
`).translationToFailWith("Undefined query or source 'bb'");
|
|
1701
|
-
});
|
|
1702
|
-
test('non-rename rename', () => {
|
|
1703
|
-
expect('source: na is a { rename: astr is astr }').translationToFailWith("Can't rename field to itself");
|
|
1704
|
-
});
|
|
1705
|
-
test('reference to field in its definition', () => {
|
|
1706
|
-
expect('source: na is a { dimension: ustr is upper(ustr) } ').translationToFailWith("Circular reference to 'ustr' in definition");
|
|
1707
|
-
});
|
|
1708
|
-
test('empty model', () => {
|
|
1709
|
-
expect('').toTranslate();
|
|
1710
|
-
});
|
|
1711
|
-
test('one line model ', () => {
|
|
1712
|
-
expect('\n').toTranslate();
|
|
1713
|
-
});
|
|
1714
|
-
test('query without fields', () => {
|
|
1715
|
-
expect('query: a -> { top: 5 }').translationToFailWith("Can't determine query type (group_by/aggregate/nest,project,index)");
|
|
1716
|
-
});
|
|
1717
|
-
test('refine cannot change query type', () => {
|
|
1718
|
-
expect('query: ab -> aturtle { project: astr }').translationToFailWith(/Not legal in grouping query/);
|
|
1719
|
-
});
|
|
1720
|
-
test('undefined field ref in query', () => {
|
|
1721
|
-
expect('query: ab -> { aggregate: xyzzy }').translationToFailWith("'xyzzy' is not defined");
|
|
1722
|
-
});
|
|
1723
|
-
test('query on source with errors', () => {
|
|
1724
|
-
expect((0, test_translator_1.markSource) `
|
|
1725
|
-
source: na is a { join_one: ${'n'} on astr }
|
|
1726
|
-
`).translationToFailWith("Undefined source 'n'");
|
|
1727
|
-
});
|
|
1728
|
-
test('detect duplicate output field names', () => {
|
|
1729
|
-
expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'astr'} }`).translationToFailWith("Output already has a field named 'astr'");
|
|
1730
|
-
});
|
|
1731
|
-
test('detect join tail overlap existing ref', () => {
|
|
1732
|
-
expect((0, test_translator_1.markSource) `query: ab -> { group_by: astr, ${'b.astr'} }`).translationToFailWith("Output already has a field named 'astr'");
|
|
1733
|
-
});
|
|
1734
|
-
test('undefined in expression with regex compare', () => {
|
|
1735
|
-
expect(`
|
|
1736
|
-
source: c is a {
|
|
1737
|
-
dimension: d is meaning_of_life ~ r'(forty two|fifty four)'
|
|
1738
|
-
}
|
|
1739
|
-
`).translationToFailWith("'meaning_of_life' is not defined");
|
|
1740
|
-
});
|
|
1741
|
-
test('detect output collision on join references', () => {
|
|
1742
|
-
expect(`
|
|
1743
|
-
query: ab -> {
|
|
1744
|
-
group_by: astr, b.astr
|
|
1745
|
-
}
|
|
1746
|
-
`).translationToFailWith("Output already has a field named 'astr'");
|
|
1747
|
-
});
|
|
1748
|
-
test('rejoin a query is renamed', () => {
|
|
1749
|
-
expect(`
|
|
1750
|
-
source: querySrc is from(
|
|
1751
|
-
table('malloytest.flights')->{
|
|
1752
|
-
group_by: origin
|
|
1753
|
-
nest: nested is { group_by: destination }
|
|
1754
|
-
}
|
|
1755
|
-
)
|
|
1756
|
-
|
|
1757
|
-
source: refineQuerySrc is querySrc {
|
|
1758
|
-
join_one: rejoin is querySrc on 7=8
|
|
1759
|
-
query: broken is {
|
|
1760
|
-
group_by: rejoin.nested.destination
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
`).toTranslate();
|
|
1764
|
-
});
|
|
1765
|
-
test('popping out of embedding when not embedded', () => {
|
|
1766
|
-
expect('}%').translationToFailWith(/extraneous input '}%' expecting/);
|
|
1767
|
-
});
|
|
1768
|
-
test('bad sql in sql block', () => {
|
|
1769
|
-
const badSelect = (0, test_translator_1.model) `sql: someName is { select: """)""" }`;
|
|
1770
|
-
const badTrans = badSelect.translator;
|
|
1771
|
-
expect(badSelect).toParse();
|
|
1772
|
-
const needSchema = badTrans.translate();
|
|
1773
|
-
expect(needSchema.compileSQL).toBeDefined();
|
|
1774
|
-
if (needSchema.compileSQL) {
|
|
1775
|
-
badTrans.update({
|
|
1776
|
-
errors: {
|
|
1777
|
-
compileSQL: {
|
|
1778
|
-
[needSchema.compileSQL.name]: 'ZZZZ',
|
|
1779
|
-
},
|
|
1780
|
-
},
|
|
1781
|
-
});
|
|
1782
|
-
}
|
|
1783
|
-
expect(badTrans).translationToFailWith('Invalid SQL, ZZZZ');
|
|
1784
|
-
});
|
|
1785
|
-
});
|
|
1786
|
-
function getSelectOneStruct(sqlBlock) {
|
|
1787
|
-
const selectThis = sqlBlock.select[0];
|
|
1788
|
-
if (!(0, model_1.isSQLFragment)(selectThis)) {
|
|
1789
|
-
throw new Error('weird test support error sorry');
|
|
1790
|
-
}
|
|
1791
|
-
return {
|
|
1792
|
-
type: 'struct',
|
|
1793
|
-
name: sqlBlock.name,
|
|
1794
|
-
dialect: 'standardsql',
|
|
1795
|
-
structSource: {
|
|
1796
|
-
type: 'sql',
|
|
1797
|
-
method: 'subquery',
|
|
1798
|
-
sqlBlock: {
|
|
1799
|
-
type: 'sqlBlock',
|
|
1800
|
-
name: sqlBlock.name,
|
|
1801
|
-
selectStr: selectThis.sql,
|
|
1802
|
-
},
|
|
1803
|
-
},
|
|
1804
|
-
structRelationship: { type: 'basetable', connectionName: 'bigquery' },
|
|
1805
|
-
fields: [{ type: 'number', name: 'one' }],
|
|
1806
|
-
};
|
|
1807
|
-
}
|
|
1808
|
-
describe('source locations', () => {
|
|
1809
|
-
test('renamed source location', () => {
|
|
1810
|
-
const source = (0, test_translator_1.markSource) `source: ${'na is a'}`;
|
|
1811
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1812
|
-
expect(m).toParse();
|
|
1813
|
-
expect((0, test_translator_1.getExplore)(m.modelDef, 'na').location).toMatchObject(source.locations[0]);
|
|
1814
|
-
});
|
|
1815
|
-
test('refined source location', () => {
|
|
1816
|
-
const source = (0, test_translator_1.markSource) `source: ${'na is a {}'}`;
|
|
1817
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1818
|
-
expect(m).toParse();
|
|
1819
|
-
expect((0, test_translator_1.getExplore)(m.modelDef, 'na').location).toMatchObject(source.locations[0]);
|
|
1820
|
-
});
|
|
1821
|
-
test('location of defined dimension', () => {
|
|
1822
|
-
const source = (0, test_translator_1.markSource) `source: na is a { dimension: ${'x is 1'} }`;
|
|
1823
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1824
|
-
expect(m).toTranslate();
|
|
1825
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1826
|
-
const x = (0, test_translator_1.getFieldDef)(na, 'x');
|
|
1827
|
-
expect(x.location).toMatchObject(source.locations[0]);
|
|
1828
|
-
});
|
|
1829
|
-
test('location of defined measure', () => {
|
|
1830
|
-
const source = (0, test_translator_1.markSource) `source: na is a { measure: ${'x is count()'} }`;
|
|
1831
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1832
|
-
expect(m).toTranslate();
|
|
1833
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1834
|
-
const x = (0, test_translator_1.getFieldDef)(na, 'x');
|
|
1835
|
-
expect(x.location).toMatchObject(source.locations[0]);
|
|
1836
|
-
});
|
|
1837
|
-
test('location of defined query', () => {
|
|
1838
|
-
const source = (0, test_translator_1.markSource) `source: na is a { query: ${'x is { group_by: y is 1 }'} }`;
|
|
1839
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1840
|
-
expect(m).toTranslate();
|
|
1841
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1842
|
-
const x = (0, test_translator_1.getFieldDef)(na, 'x');
|
|
1843
|
-
expect(x.location).toMatchObject(source.locations[0]);
|
|
1844
|
-
});
|
|
1845
|
-
test('location of defined field inside a query', () => {
|
|
1846
|
-
const source = (0, test_translator_1.markSource) `
|
|
1847
|
-
source: na is a {
|
|
1848
|
-
query: x is {
|
|
1849
|
-
group_by: ${'y is 1'}
|
|
1850
|
-
}
|
|
1851
|
-
}`;
|
|
1852
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1853
|
-
expect(m).toTranslate();
|
|
1854
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1855
|
-
const x = (0, test_translator_1.getQueryField)(na, 'x');
|
|
1856
|
-
const y = (0, test_translator_1.getFieldDef)(x.pipeline[0], 'y');
|
|
1857
|
-
expect(y.location).toMatchObject(source.locations[0]);
|
|
1858
|
-
});
|
|
1859
|
-
test('location of filtered field inside a query', () => {
|
|
1860
|
-
const source = (0, test_translator_1.markSource) `
|
|
1861
|
-
source: na is a {
|
|
1862
|
-
measure: y is count()
|
|
1863
|
-
query: x is {
|
|
1864
|
-
aggregate: ${'z is y { where: true }'}
|
|
1865
|
-
}
|
|
1866
|
-
}`;
|
|
1867
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1868
|
-
expect(m).toTranslate();
|
|
1869
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1870
|
-
const x = (0, test_translator_1.getQueryField)(na, 'x');
|
|
1871
|
-
const z = (0, test_translator_1.getFieldDef)(x.pipeline[0], 'z');
|
|
1872
|
-
expect(z.location).toMatchObject(source.locations[0]);
|
|
1873
|
-
});
|
|
1874
|
-
test('location of field inherited from table', () => {
|
|
1875
|
-
const source = (0, test_translator_1.markSource) `source: na is ${"table('aTable')"}`;
|
|
1876
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1877
|
-
expect(m).toTranslate();
|
|
1878
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1879
|
-
const abool = (0, test_translator_1.getFieldDef)(na, 'abool');
|
|
1880
|
-
expect(abool.location).toMatchObject(source.locations[0]);
|
|
1881
|
-
});
|
|
1882
|
-
test('location of field inherited from sql block', () => {
|
|
1883
|
-
const source = (0, test_translator_1.markSource) `--- comment
|
|
1884
|
-
sql: s is { select: ${'"""SELECT 1 as one """'} }
|
|
1885
|
-
source: na is from_sql(s)
|
|
1886
|
-
`;
|
|
1887
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1888
|
-
expect(m).toParse();
|
|
1889
|
-
const compileSql = m.translate().compileSQL;
|
|
1890
|
-
expect(compileSql).toBeDefined();
|
|
1891
|
-
if (compileSql) {
|
|
1892
|
-
m.update({
|
|
1893
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
1894
|
-
});
|
|
1895
|
-
expect(m).toTranslate();
|
|
1896
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1897
|
-
const one = (0, test_translator_1.getFieldDef)(na, 'one');
|
|
1898
|
-
expect(one.location).isLocationIn(source.locations[0], source.code);
|
|
1899
|
-
}
|
|
1900
|
-
});
|
|
1901
|
-
test('location of fields inherited from a query', () => {
|
|
1902
|
-
const source = (0, test_translator_1.markSource) `
|
|
1903
|
-
source: na is from(
|
|
1904
|
-
${"table('aTable')"} -> {
|
|
1905
|
-
group_by:
|
|
1906
|
-
abool
|
|
1907
|
-
${'y is 1'}
|
|
1908
|
-
}
|
|
1909
|
-
)
|
|
1910
|
-
`;
|
|
1911
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1912
|
-
expect(m).toTranslate();
|
|
1913
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1914
|
-
const abool = (0, test_translator_1.getFieldDef)(na, 'abool');
|
|
1915
|
-
expect(abool.location).toMatchObject(source.locations[0]);
|
|
1916
|
-
const y = (0, test_translator_1.getFieldDef)(na, 'y');
|
|
1917
|
-
expect(y.location).toMatchObject(source.locations[1]);
|
|
1918
|
-
});
|
|
1919
|
-
test('location of named query', () => {
|
|
1920
|
-
const source = (0, test_translator_1.markSource) `query: ${'q is a -> { project: * }'}`;
|
|
1921
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1922
|
-
expect(m).toTranslate();
|
|
1923
|
-
const q = (0, test_translator_1.getExplore)(m.modelDef, 'q');
|
|
1924
|
-
expect(q.location).toMatchObject(source.locations[0]);
|
|
1925
|
-
});
|
|
1926
|
-
test('location of field in named query', () => {
|
|
1927
|
-
const source = (0, test_translator_1.markSource) `query: q is a -> { group_by: ${'b is 1'} }`;
|
|
1928
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1929
|
-
expect(m).toTranslate();
|
|
1930
|
-
const q = (0, test_translator_1.getModelQuery)(m.modelDef, 'q');
|
|
1931
|
-
const a = (0, test_translator_1.getFieldDef)(q.pipeline[0], 'b');
|
|
1932
|
-
expect(a.location).toMatchObject(source.locations[0]);
|
|
1933
|
-
});
|
|
1934
|
-
test('location of named SQL block', () => {
|
|
1935
|
-
const source = (0, test_translator_1.markSource) `${'sql: s is { select: """SELECT 1 as one""" }'}`;
|
|
1936
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1937
|
-
expect(m).toParse();
|
|
1938
|
-
const compileSql = m.translate().compileSQL;
|
|
1939
|
-
expect(compileSql).toBeDefined();
|
|
1940
|
-
if (compileSql) {
|
|
1941
|
-
m.update({
|
|
1942
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
1943
|
-
});
|
|
1944
|
-
expect(m).toTranslate();
|
|
1945
|
-
const s = m.sqlBlocks[0];
|
|
1946
|
-
expect(s.location).isLocationIn(source.locations[0], source.code);
|
|
1947
|
-
}
|
|
1948
|
-
});
|
|
1949
|
-
test('location of renamed field', () => {
|
|
1950
|
-
const source = (0, test_translator_1.markSource) `
|
|
1951
|
-
source: na is a {
|
|
1952
|
-
rename: ${'bbool is abool'}
|
|
1953
|
-
}
|
|
1954
|
-
`;
|
|
1955
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1956
|
-
expect(m).toTranslate();
|
|
1957
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1958
|
-
const bbool = (0, test_translator_1.getFieldDef)(na, 'bbool');
|
|
1959
|
-
expect(bbool.location).toMatchObject(source.locations[0]);
|
|
1960
|
-
});
|
|
1961
|
-
test('location of join on', () => {
|
|
1962
|
-
const source = (0, test_translator_1.markSource) `
|
|
1963
|
-
source: na is a {
|
|
1964
|
-
join_one: ${'x is a { primary_key: abool } on abool'}
|
|
1965
|
-
}
|
|
1966
|
-
`;
|
|
1967
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1968
|
-
expect(m).toTranslate();
|
|
1969
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1970
|
-
const x = (0, test_translator_1.getFieldDef)(na, 'x');
|
|
1971
|
-
expect(x.location).toMatchObject(source.locations[0]);
|
|
1972
|
-
});
|
|
1973
|
-
test('location of join with', () => {
|
|
1974
|
-
const source = (0, test_translator_1.markSource) `
|
|
1975
|
-
source: na is a {
|
|
1976
|
-
join_one: ${'x is a { primary_key: astr } with astr'}
|
|
1977
|
-
}
|
|
1978
|
-
`;
|
|
1979
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1980
|
-
expect(m).toTranslate();
|
|
1981
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1982
|
-
const x = (0, test_translator_1.getFieldDef)(na, 'x');
|
|
1983
|
-
expect(x.location).toMatchObject(source.locations[0]);
|
|
1984
|
-
});
|
|
1985
|
-
test('location of field in join', () => {
|
|
1986
|
-
const source = (0, test_translator_1.markSource) `
|
|
1987
|
-
source: na is a {
|
|
1988
|
-
join_one: x is a {
|
|
1989
|
-
primary_key: abool
|
|
1990
|
-
dimension: ${'y is 1'}
|
|
1991
|
-
} on abool
|
|
1992
|
-
}
|
|
1993
|
-
`;
|
|
1994
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
1995
|
-
expect(m).toTranslate();
|
|
1996
|
-
const na = (0, test_translator_1.getExplore)(m.modelDef, 'na');
|
|
1997
|
-
const x = (0, test_translator_1.getJoinField)(na, 'x');
|
|
1998
|
-
const y = (0, test_translator_1.getFieldDef)(x, 'y');
|
|
1999
|
-
expect(y.location).toMatchObject(source.locations[0]);
|
|
2000
|
-
});
|
|
2001
|
-
// Since """ strings are not single tokens, I don't know how to do this.
|
|
2002
|
-
// test("multi line sql block token span is correct", () => {
|
|
2003
|
-
// const sqlSource = `sql: { select: """// line 0\n//line 1\n// line 2""" }`;
|
|
2004
|
-
// const m = new TestTranslator(sqlSource);
|
|
2005
|
-
// expect(m).not.toParse();
|
|
2006
|
-
// const errList = m.errors().errors;
|
|
2007
|
-
// expect(errList[0].at?.range.end).toEqual({ line: 2, character: 11 });
|
|
2008
|
-
// });
|
|
2009
|
-
test('undefined query location', () => {
|
|
2010
|
-
expect((0, test_translator_1.model) `query: ${'-> xyz'}`).translationToFailWith("Reference to undefined query 'xyz'");
|
|
2011
|
-
});
|
|
2012
|
-
test('undefined field reference', () => {
|
|
2013
|
-
expect((0, test_translator_1.model) `query: a -> { group_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
|
|
2014
|
-
});
|
|
2015
|
-
test('bad query', () => {
|
|
2016
|
-
expect((0, test_translator_1.model) `query: a -> { group_by: astr; ${'project: *'} }`).translationToFailWith(/Not legal in grouping query/);
|
|
2017
|
-
});
|
|
2018
|
-
test.skip('undefined field reference in top', () => {
|
|
2019
|
-
expect((0, test_translator_1.model) `query: a -> { group_by: one is 1; top: 1 by ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
|
|
2020
|
-
});
|
|
2021
|
-
test.skip('undefined field reference in order_by', () => {
|
|
2022
|
-
expect((0, test_translator_1.model) `query: a -> { group_by: one is 1; order_by: ${'xyz'} }`).translationToFailWith("'xyz' is not defined");
|
|
2023
|
-
});
|
|
2024
|
-
});
|
|
2025
|
-
describe('source references', () => {
|
|
2026
|
-
test('reference to explore', () => {
|
|
2027
|
-
const source = (0, test_translator_1.markSource) `
|
|
2028
|
-
source: ${'na is a'}
|
|
2029
|
-
query: ${'na'} -> { project: * }
|
|
2030
|
-
`;
|
|
2031
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2032
|
-
expect(m).toTranslate();
|
|
2033
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2034
|
-
location: source.locations[1],
|
|
2035
|
-
type: 'exploreReference',
|
|
2036
|
-
text: 'na',
|
|
2037
|
-
definition: {
|
|
2038
|
-
location: source.locations[0],
|
|
2039
|
-
},
|
|
2040
|
-
});
|
|
2041
|
-
});
|
|
2042
|
-
test('reference to query in query', () => {
|
|
2043
|
-
const source = (0, test_translator_1.markSource) `
|
|
2044
|
-
source: t is a {
|
|
2045
|
-
query: ${'q is { project: * }'}
|
|
2046
|
-
}
|
|
2047
|
-
query: t -> ${'q'}
|
|
2048
|
-
`;
|
|
2049
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2050
|
-
expect(m).toTranslate();
|
|
2051
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2052
|
-
location: source.locations[1],
|
|
2053
|
-
type: 'fieldReference',
|
|
2054
|
-
text: 'q',
|
|
2055
|
-
definition: {
|
|
2056
|
-
location: source.locations[0],
|
|
2057
|
-
},
|
|
2058
|
-
});
|
|
2059
|
-
});
|
|
2060
|
-
test('reference to query in query (version 2)', () => {
|
|
2061
|
-
const source = (0, test_translator_1.markSource) `
|
|
2062
|
-
source: na is a { query: ${'x is { group_by: y is 1 }'} }
|
|
2063
|
-
query: na -> ${'x'}
|
|
2064
|
-
`;
|
|
2065
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2066
|
-
expect(m).toTranslate();
|
|
2067
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2068
|
-
location: source.locations[1],
|
|
2069
|
-
type: 'fieldReference',
|
|
2070
|
-
text: 'x',
|
|
2071
|
-
definition: {
|
|
2072
|
-
location: source.locations[0],
|
|
2073
|
-
},
|
|
2074
|
-
});
|
|
2075
|
-
});
|
|
2076
|
-
test('reference to sql block', () => {
|
|
2077
|
-
const source = (0, test_translator_1.markSource) `
|
|
2078
|
-
${'sql: s is {select:"""SELECT 1 as one"""}'}
|
|
2079
|
-
source: na is from_sql(${'s'})
|
|
2080
|
-
`;
|
|
2081
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2082
|
-
expect(m).toParse();
|
|
2083
|
-
const compileSql = m.translate().compileSQL;
|
|
2084
|
-
expect(compileSql).toBeDefined();
|
|
2085
|
-
if (compileSql) {
|
|
2086
|
-
m.update({
|
|
2087
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
2088
|
-
});
|
|
2089
|
-
expect(m).toTranslate();
|
|
2090
|
-
const ref = m.referenceAt(pos(source.locations[1]));
|
|
2091
|
-
expect(ref).toMatchObject({
|
|
2092
|
-
location: source.locations[1],
|
|
2093
|
-
type: 'sqlBlockReference',
|
|
2094
|
-
text: 's',
|
|
2095
|
-
definition: {
|
|
2096
|
-
...getSelectOneStruct(compileSql),
|
|
2097
|
-
location: source.locations[0],
|
|
161
|
+
test('bad sql in sql block', () => {
|
|
162
|
+
const badSelect = (0, test_translator_1.model) `sql: someName is { select: """)""" }`;
|
|
163
|
+
const badTrans = badSelect.translator;
|
|
164
|
+
expect(badSelect).toParse();
|
|
165
|
+
const needSchema = badTrans.translate();
|
|
166
|
+
expect(needSchema.compileSQL).toBeDefined();
|
|
167
|
+
if (needSchema.compileSQL) {
|
|
168
|
+
badTrans.update({
|
|
169
|
+
errors: {
|
|
170
|
+
compileSQL: {
|
|
171
|
+
[needSchema.compileSQL.name]: 'ZZZZ',
|
|
172
|
+
},
|
|
2098
173
|
},
|
|
2099
174
|
});
|
|
2100
175
|
}
|
|
2101
|
-
|
|
2102
|
-
test('reference to query in from', () => {
|
|
2103
|
-
const source = (0, test_translator_1.markSource) `
|
|
2104
|
-
query: ${'q is a -> { project: * }'}
|
|
2105
|
-
source: na is from(-> ${'q'})
|
|
2106
|
-
`;
|
|
2107
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2108
|
-
expect(m).toTranslate();
|
|
2109
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2110
|
-
location: source.locations[1],
|
|
2111
|
-
type: 'queryReference',
|
|
2112
|
-
text: 'q',
|
|
2113
|
-
definition: {
|
|
2114
|
-
location: source.locations[0],
|
|
2115
|
-
},
|
|
2116
|
-
});
|
|
2117
|
-
});
|
|
2118
|
-
test('reference to query in query head', () => {
|
|
2119
|
-
const source = (0, test_translator_1.markSource) `
|
|
2120
|
-
query: ${'q is a -> { project: * }'}
|
|
2121
|
-
query: q2 is -> ${'q'} -> { project: * }
|
|
2122
|
-
`;
|
|
2123
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2124
|
-
expect(m).toTranslate();
|
|
2125
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2126
|
-
location: source.locations[1],
|
|
2127
|
-
type: 'queryReference',
|
|
2128
|
-
text: 'q',
|
|
2129
|
-
definition: {
|
|
2130
|
-
location: source.locations[0],
|
|
2131
|
-
},
|
|
2132
|
-
});
|
|
2133
|
-
});
|
|
2134
|
-
test('reference to query in refined query', () => {
|
|
2135
|
-
const source = (0, test_translator_1.markSource) `
|
|
2136
|
-
query: ${'q is a -> { project: * }'}
|
|
2137
|
-
query: q2 is -> ${'q'} { limit: 10 }
|
|
2138
|
-
`;
|
|
2139
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2140
|
-
expect(m).toTranslate();
|
|
2141
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2142
|
-
location: source.locations[1],
|
|
2143
|
-
type: 'queryReference',
|
|
2144
|
-
text: 'q',
|
|
2145
|
-
definition: {
|
|
2146
|
-
location: source.locations[0],
|
|
2147
|
-
},
|
|
2148
|
-
});
|
|
2149
|
-
});
|
|
2150
|
-
test('reference to field in expression', () => {
|
|
2151
|
-
const source = (0, test_translator_1.markSource) `
|
|
2152
|
-
source: na is ${"table('aTable')"}
|
|
2153
|
-
query: na -> { project: bbool is not ${'abool'} }
|
|
2154
|
-
`;
|
|
2155
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2156
|
-
expect(m).toTranslate();
|
|
2157
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2158
|
-
location: source.locations[1],
|
|
2159
|
-
type: 'fieldReference',
|
|
2160
|
-
text: 'abool',
|
|
2161
|
-
definition: {
|
|
2162
|
-
location: source.locations[0],
|
|
2163
|
-
},
|
|
2164
|
-
});
|
|
2165
|
-
});
|
|
2166
|
-
test('reference to quoted field in expression', () => {
|
|
2167
|
-
const source = (0, test_translator_1.markSource) `
|
|
2168
|
-
source: na is a {
|
|
2169
|
-
dimension: ${"`name` is 'name'"}
|
|
2170
|
-
}
|
|
2171
|
-
query: na -> { project: ${'`name`'} }
|
|
2172
|
-
`;
|
|
2173
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2174
|
-
expect(m).toTranslate();
|
|
2175
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2176
|
-
location: source.locations[1],
|
|
2177
|
-
type: 'fieldReference',
|
|
2178
|
-
text: 'name',
|
|
2179
|
-
definition: {
|
|
2180
|
-
location: source.locations[0],
|
|
2181
|
-
},
|
|
2182
|
-
});
|
|
2183
|
-
});
|
|
2184
|
-
test('reference to joined field in expression', () => {
|
|
2185
|
-
const source = (0, test_translator_1.markSource) `
|
|
2186
|
-
source: na is a {
|
|
2187
|
-
join_one: self is ${"table('aTable')"}
|
|
2188
|
-
on astr = self.astr
|
|
2189
|
-
}
|
|
2190
|
-
query: na -> { project: bstr is self.${'astr'} }
|
|
2191
|
-
`;
|
|
2192
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2193
|
-
expect(m).toTranslate();
|
|
2194
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2195
|
-
location: source.locations[1],
|
|
2196
|
-
type: 'fieldReference',
|
|
2197
|
-
text: 'astr',
|
|
2198
|
-
definition: {
|
|
2199
|
-
location: source.locations[0],
|
|
2200
|
-
},
|
|
2201
|
-
});
|
|
2202
|
-
});
|
|
2203
|
-
test('reference to joined join in expression', () => {
|
|
2204
|
-
const source = (0, test_translator_1.markSource) `
|
|
2205
|
-
source: na is a {
|
|
2206
|
-
join_one: ${'self is a on astr = self.astr'}
|
|
2207
|
-
}
|
|
2208
|
-
query: na -> { project: bstr is ${'self'}.astr }
|
|
2209
|
-
`;
|
|
2210
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2211
|
-
expect(m).toTranslate();
|
|
2212
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2213
|
-
location: source.locations[1],
|
|
2214
|
-
type: 'joinReference',
|
|
2215
|
-
text: 'self',
|
|
2216
|
-
definition: {
|
|
2217
|
-
location: source.locations[0],
|
|
2218
|
-
},
|
|
2219
|
-
});
|
|
2220
|
-
});
|
|
2221
|
-
test('reference to field not in expression (group by)', () => {
|
|
2222
|
-
const source = (0, test_translator_1.markSource) `
|
|
2223
|
-
query: ${"table('aTable')"} -> { group_by: ${'abool'} }
|
|
2224
|
-
`;
|
|
2225
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2226
|
-
expect(m).toTranslate();
|
|
2227
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2228
|
-
location: source.locations[1],
|
|
2229
|
-
type: 'fieldReference',
|
|
2230
|
-
text: 'abool',
|
|
2231
|
-
definition: {
|
|
2232
|
-
location: source.locations[0],
|
|
2233
|
-
},
|
|
2234
|
-
});
|
|
2235
|
-
});
|
|
2236
|
-
test('reference to field not in expression (project)', () => {
|
|
2237
|
-
const source = (0, test_translator_1.markSource) `
|
|
2238
|
-
source: na is ${"table('aTable')"}
|
|
2239
|
-
query: na -> { project: ${'abool'} }
|
|
2240
|
-
`;
|
|
2241
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2242
|
-
expect(m).toTranslate();
|
|
2243
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2244
|
-
location: source.locations[1],
|
|
2245
|
-
type: 'fieldReference',
|
|
2246
|
-
text: 'abool',
|
|
2247
|
-
definition: {
|
|
2248
|
-
location: source.locations[0],
|
|
2249
|
-
},
|
|
2250
|
-
});
|
|
2251
|
-
});
|
|
2252
|
-
test.skip('reference to field in order by', () => {
|
|
2253
|
-
const source = (0, test_translator_1.markSource) `
|
|
2254
|
-
query: ${"table('aTable')"} -> {
|
|
2255
|
-
group_by: abool
|
|
2256
|
-
order_by: ${'abool'}
|
|
2257
|
-
}
|
|
2258
|
-
`;
|
|
2259
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2260
|
-
expect(m).toTranslate();
|
|
2261
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2262
|
-
location: source.locations[1],
|
|
2263
|
-
type: 'fieldReference',
|
|
2264
|
-
text: 'abool',
|
|
2265
|
-
definition: {
|
|
2266
|
-
location: source.locations[0],
|
|
2267
|
-
},
|
|
2268
|
-
});
|
|
2269
|
-
});
|
|
2270
|
-
test.skip('reference to field in order by (output space)', () => {
|
|
2271
|
-
const source = (0, test_translator_1.markSource) `
|
|
2272
|
-
query: a -> {
|
|
2273
|
-
group_by: ${'one is 1'}
|
|
2274
|
-
order_by: ${'one'}
|
|
2275
|
-
}
|
|
2276
|
-
`;
|
|
2277
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2278
|
-
expect(m).toTranslate();
|
|
2279
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2280
|
-
location: source.locations[1],
|
|
2281
|
-
type: 'fieldReference',
|
|
2282
|
-
text: 'abool',
|
|
2283
|
-
definition: {
|
|
2284
|
-
location: source.locations[0],
|
|
2285
|
-
},
|
|
2286
|
-
});
|
|
2287
|
-
});
|
|
2288
|
-
test('reference to field in aggregate', () => {
|
|
2289
|
-
const source = (0, test_translator_1.markSource) `
|
|
2290
|
-
query: a { measure: ${'c is count()'} } -> {
|
|
2291
|
-
group_by: abool
|
|
2292
|
-
aggregate: ${'c'}
|
|
2293
|
-
}
|
|
2294
|
-
`;
|
|
2295
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2296
|
-
expect(m).toTranslate();
|
|
2297
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2298
|
-
location: source.locations[1],
|
|
2299
|
-
type: 'fieldReference',
|
|
2300
|
-
text: 'c',
|
|
2301
|
-
definition: {
|
|
2302
|
-
location: source.locations[0],
|
|
2303
|
-
},
|
|
2304
|
-
});
|
|
2305
|
-
});
|
|
2306
|
-
test('reference to field in measure', () => {
|
|
2307
|
-
const source = (0, test_translator_1.markSource) `
|
|
2308
|
-
source: e is a {
|
|
2309
|
-
measure: ${'c is count()'}
|
|
2310
|
-
measure: c2 is ${'c'}
|
|
2311
|
-
}
|
|
2312
|
-
`;
|
|
2313
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2314
|
-
expect(m).toTranslate();
|
|
2315
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2316
|
-
location: source.locations[1],
|
|
2317
|
-
type: 'fieldReference',
|
|
2318
|
-
text: 'c',
|
|
2319
|
-
definition: {
|
|
2320
|
-
location: source.locations[0],
|
|
2321
|
-
},
|
|
2322
|
-
});
|
|
2323
|
-
});
|
|
2324
|
-
test.skip('reference to field in top', () => {
|
|
2325
|
-
const source = (0, test_translator_1.markSource) `
|
|
2326
|
-
query: ${"table('aTable')"} -> {
|
|
2327
|
-
group_by: abool
|
|
2328
|
-
top: 10 by ${'abool'}
|
|
2329
|
-
}
|
|
2330
|
-
`;
|
|
2331
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2332
|
-
expect(m).toTranslate();
|
|
2333
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2334
|
-
location: source.locations[1],
|
|
2335
|
-
type: 'fieldReference',
|
|
2336
|
-
text: 'abool',
|
|
2337
|
-
definition: {
|
|
2338
|
-
location: source.locations[0],
|
|
2339
|
-
},
|
|
2340
|
-
});
|
|
2341
|
-
});
|
|
2342
|
-
test.skip('reference to field in top (output space)', () => {
|
|
2343
|
-
const source = (0, test_translator_1.markSource) `
|
|
2344
|
-
query: a -> {
|
|
2345
|
-
group_by: ${'one is 1'}
|
|
2346
|
-
top: 10 by ${'one'}
|
|
2347
|
-
}
|
|
2348
|
-
`;
|
|
2349
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2350
|
-
expect(m).toTranslate();
|
|
2351
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2352
|
-
location: source.locations[1],
|
|
2353
|
-
type: 'fieldReference',
|
|
2354
|
-
text: 'abool',
|
|
2355
|
-
definition: {
|
|
2356
|
-
location: source.locations[0],
|
|
2357
|
-
},
|
|
2358
|
-
});
|
|
2359
|
-
});
|
|
2360
|
-
test('reference to field in filter', () => {
|
|
2361
|
-
const source = (0, test_translator_1.markSource) `
|
|
2362
|
-
query: ${"table('aTable')"} -> {
|
|
2363
|
-
group_by: abool
|
|
2364
|
-
where: ${'abool'}
|
|
2365
|
-
}
|
|
2366
|
-
`;
|
|
2367
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2368
|
-
expect(m).toTranslate();
|
|
2369
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2370
|
-
location: source.locations[1],
|
|
2371
|
-
type: 'fieldReference',
|
|
2372
|
-
text: 'abool',
|
|
2373
|
-
definition: {
|
|
2374
|
-
location: source.locations[0],
|
|
2375
|
-
},
|
|
2376
|
-
});
|
|
2377
|
-
});
|
|
2378
|
-
test('reference to field in aggregate source', () => {
|
|
2379
|
-
const source = (0, test_translator_1.markSource) `
|
|
2380
|
-
source: na is ${"table('aTable')"}
|
|
2381
|
-
query: na -> { aggregate: ai_sum is ${'ai'}.sum() }
|
|
2382
|
-
`;
|
|
2383
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2384
|
-
expect(m).toTranslate();
|
|
2385
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2386
|
-
location: source.locations[1],
|
|
2387
|
-
type: 'fieldReference',
|
|
2388
|
-
text: 'ai',
|
|
2389
|
-
definition: {
|
|
2390
|
-
location: source.locations[0],
|
|
2391
|
-
},
|
|
2392
|
-
});
|
|
2393
|
-
});
|
|
2394
|
-
function pos(location) {
|
|
2395
|
-
return location.range.start;
|
|
2396
|
-
}
|
|
2397
|
-
test('reference to join in aggregate source', () => {
|
|
2398
|
-
const source = (0, test_translator_1.markSource) `
|
|
2399
|
-
source: na is a {
|
|
2400
|
-
join_one: ${'self is a on astr = self.astr'}
|
|
2401
|
-
}
|
|
2402
|
-
query: na -> { aggregate: ai_sum is ${'self'}.sum(self.ai) }
|
|
2403
|
-
`;
|
|
2404
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2405
|
-
expect(m).toTranslate();
|
|
2406
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2407
|
-
location: source.locations[1],
|
|
2408
|
-
type: 'joinReference',
|
|
2409
|
-
text: 'self',
|
|
2410
|
-
definition: {
|
|
2411
|
-
location: source.locations[0],
|
|
2412
|
-
},
|
|
2413
|
-
});
|
|
2414
|
-
});
|
|
2415
|
-
test('reference to join in aggregate in expr', () => {
|
|
2416
|
-
const source = (0, test_translator_1.markSource) `
|
|
2417
|
-
source: na is a {
|
|
2418
|
-
join_one: ${'self is a on astr = self.astr'}
|
|
2419
|
-
}
|
|
2420
|
-
query: na -> { aggregate: ai_sum is self.sum(${'self'}.ai) }
|
|
2421
|
-
`;
|
|
2422
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2423
|
-
expect(m).toTranslate();
|
|
2424
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2425
|
-
location: source.locations[1],
|
|
2426
|
-
type: 'joinReference',
|
|
2427
|
-
text: 'self',
|
|
2428
|
-
definition: {
|
|
2429
|
-
location: source.locations[0],
|
|
2430
|
-
},
|
|
2431
|
-
});
|
|
2432
|
-
});
|
|
2433
|
-
test('reference to sourcein join', () => {
|
|
2434
|
-
const source = (0, test_translator_1.markSource) `
|
|
2435
|
-
source: ${'exp1 is a'}
|
|
2436
|
-
source: exp2 is a {
|
|
2437
|
-
join_one: ${'exp1'} on astr = exp1.astr
|
|
2438
|
-
}
|
|
2439
|
-
`;
|
|
2440
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2441
|
-
expect(m).toTranslate();
|
|
2442
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2443
|
-
location: source.locations[1],
|
|
2444
|
-
type: 'exploreReference',
|
|
2445
|
-
text: 'exp1',
|
|
2446
|
-
definition: {
|
|
2447
|
-
location: source.locations[0],
|
|
2448
|
-
},
|
|
2449
|
-
});
|
|
2450
|
-
});
|
|
2451
|
-
test('reference to field in aggregate (in expr)', () => {
|
|
2452
|
-
const source = (0, test_translator_1.markSource) `
|
|
2453
|
-
source: na is ${"table('aTable')"}
|
|
2454
|
-
query: na -> { aggregate: ai_sum is sum(${'ai'}) }
|
|
2455
|
-
`;
|
|
2456
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2457
|
-
expect(m).toTranslate();
|
|
2458
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2459
|
-
location: source.locations[1],
|
|
2460
|
-
type: 'fieldReference',
|
|
2461
|
-
text: 'ai',
|
|
2462
|
-
definition: {
|
|
2463
|
-
location: source.locations[0],
|
|
2464
|
-
},
|
|
2465
|
-
});
|
|
2466
|
-
});
|
|
2467
|
-
test('reference to field in rename', () => {
|
|
2468
|
-
const source = (0, test_translator_1.markSource) `
|
|
2469
|
-
source: na is ${"table('aTable')"} {
|
|
2470
|
-
rename: bbool is ${'abool'}
|
|
2471
|
-
}
|
|
2472
|
-
`;
|
|
2473
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2474
|
-
expect(m).toTranslate();
|
|
2475
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2476
|
-
location: source.locations[1],
|
|
2477
|
-
type: 'fieldReference',
|
|
2478
|
-
text: 'abool',
|
|
2479
|
-
definition: {
|
|
2480
|
-
location: source.locations[0],
|
|
2481
|
-
},
|
|
2482
|
-
});
|
|
2483
|
-
});
|
|
2484
|
-
test('reference to field in join with', () => {
|
|
2485
|
-
const source = (0, test_translator_1.markSource) `
|
|
2486
|
-
source: exp1 is a { primary_key: astr }
|
|
2487
|
-
source: exp2 is ${"table('aTable')"} {
|
|
2488
|
-
join_one: exp1 with ${'astr'}
|
|
2489
|
-
}
|
|
2490
|
-
`;
|
|
2491
|
-
const m = new test_translator_1.TestTranslator(source.code);
|
|
2492
|
-
expect(m).toTranslate();
|
|
2493
|
-
expect(m.referenceAt(pos(source.locations[1]))).toMatchObject({
|
|
2494
|
-
location: source.locations[1],
|
|
2495
|
-
type: 'fieldReference',
|
|
2496
|
-
text: 'astr',
|
|
2497
|
-
definition: {
|
|
2498
|
-
location: source.locations[0],
|
|
2499
|
-
},
|
|
2500
|
-
});
|
|
176
|
+
expect(badTrans).translationToFailWith('Invalid SQL, ZZZZ');
|
|
2501
177
|
});
|
|
2502
178
|
});
|
|
2503
179
|
describe('translate imports', () => {
|
|
@@ -2895,7 +571,7 @@ describe('sql expressions', () => {
|
|
|
2895
571
|
expect(compileSql).toBeDefined();
|
|
2896
572
|
if (compileSql) {
|
|
2897
573
|
m.translator.update({
|
|
2898
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
574
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2899
575
|
});
|
|
2900
576
|
expect(m).toTranslate();
|
|
2901
577
|
}
|
|
@@ -2909,7 +585,7 @@ describe('sql expressions', () => {
|
|
|
2909
585
|
expect(compileSql).toBeDefined();
|
|
2910
586
|
if (compileSql) {
|
|
2911
587
|
m.translator.update({
|
|
2912
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
588
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2913
589
|
});
|
|
2914
590
|
expect(m).toTranslate();
|
|
2915
591
|
}
|
|
@@ -2923,7 +599,7 @@ describe('sql expressions', () => {
|
|
|
2923
599
|
expect(compileSql).toBeDefined();
|
|
2924
600
|
if (compileSql) {
|
|
2925
601
|
m.translator.update({
|
|
2926
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
602
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2927
603
|
});
|
|
2928
604
|
expect(m).toTranslate();
|
|
2929
605
|
}
|
|
@@ -2938,7 +614,7 @@ describe('sql expressions', () => {
|
|
|
2938
614
|
expect(compileSql).toBeDefined();
|
|
2939
615
|
if (compileSql) {
|
|
2940
616
|
m.translator.update({
|
|
2941
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
617
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2942
618
|
});
|
|
2943
619
|
expect(m).toTranslate();
|
|
2944
620
|
}
|
|
@@ -2952,7 +628,7 @@ describe('sql expressions', () => {
|
|
|
2952
628
|
expect(compileSql).toBeDefined();
|
|
2953
629
|
if (compileSql) {
|
|
2954
630
|
m.update({
|
|
2955
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
631
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2956
632
|
});
|
|
2957
633
|
expect(m).toTranslate();
|
|
2958
634
|
}
|
|
@@ -2966,7 +642,7 @@ describe('sql expressions', () => {
|
|
|
2966
642
|
expect(compileSql).toBeDefined();
|
|
2967
643
|
if (compileSql) {
|
|
2968
644
|
m.update({
|
|
2969
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
645
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2970
646
|
});
|
|
2971
647
|
expect(m).toTranslate();
|
|
2972
648
|
}
|
|
@@ -2983,7 +659,7 @@ describe('sql expressions', () => {
|
|
|
2983
659
|
expect(compileSql).toBeDefined();
|
|
2984
660
|
if (compileSql) {
|
|
2985
661
|
m.translator.update({
|
|
2986
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
662
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
2987
663
|
});
|
|
2988
664
|
expect(m).toTranslate();
|
|
2989
665
|
}
|
|
@@ -2994,7 +670,7 @@ describe('sql expressions', () => {
|
|
|
2994
670
|
const req = m.translate().compileSQL;
|
|
2995
671
|
if (req) {
|
|
2996
672
|
m.update({
|
|
2997
|
-
compileSQL: { [req.name]: getSelectOneStruct(req) },
|
|
673
|
+
compileSQL: { [req.name]: (0, test_translator_1.getSelectOneStruct)(req) },
|
|
2998
674
|
});
|
|
2999
675
|
}
|
|
3000
676
|
expect(m).toTranslateWithWarnings('`sql:` statement is deprecated, use `connection_name.sql(...)` instead');
|
|
@@ -3006,7 +682,7 @@ describe('sql expressions', () => {
|
|
|
3006
682
|
const req = m.translate().compileSQL;
|
|
3007
683
|
if (req) {
|
|
3008
684
|
m.update({
|
|
3009
|
-
compileSQL: { [req.name]: getSelectOneStruct(req) },
|
|
685
|
+
compileSQL: { [req.name]: (0, test_translator_1.getSelectOneStruct)(req) },
|
|
3010
686
|
});
|
|
3011
687
|
}
|
|
3012
688
|
expect(m).toTranslateWithWarnings('`sql:` statement is deprecated, use `connection_name.sql(...)` instead', '`from_sql` is deprecated; use `connection_name.sql(...)` as a source directly');
|
|
@@ -3020,7 +696,7 @@ describe('sql expressions', () => {
|
|
|
3020
696
|
expect(compileSql).toBeDefined();
|
|
3021
697
|
if (compileSql) {
|
|
3022
698
|
m.update({
|
|
3023
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
699
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
3024
700
|
});
|
|
3025
701
|
expect(m).toTranslate();
|
|
3026
702
|
}
|
|
@@ -3034,7 +710,7 @@ describe('sql expressions', () => {
|
|
|
3034
710
|
expect(compileSql).toBeDefined();
|
|
3035
711
|
if (compileSql) {
|
|
3036
712
|
m.update({
|
|
3037
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
713
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
3038
714
|
});
|
|
3039
715
|
expect(m).toTranslate();
|
|
3040
716
|
}
|
|
@@ -3048,7 +724,7 @@ describe('sql expressions', () => {
|
|
|
3048
724
|
expect(compileSql).toBeDefined();
|
|
3049
725
|
if (compileSql) {
|
|
3050
726
|
m.update({
|
|
3051
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
727
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
3052
728
|
});
|
|
3053
729
|
expect(m).toTranslate();
|
|
3054
730
|
}
|
|
@@ -3066,7 +742,7 @@ describe('sql expressions', () => {
|
|
|
3066
742
|
expect(compileSql).toBeDefined();
|
|
3067
743
|
if (compileSql) {
|
|
3068
744
|
m.update({
|
|
3069
|
-
compileSQL: { [compileSql.name]: getSelectOneStruct(compileSql) },
|
|
745
|
+
compileSQL: { [compileSql.name]: (0, test_translator_1.getSelectOneStruct)(compileSql) },
|
|
3070
746
|
});
|
|
3071
747
|
expect(m).translationToFailWith('Cannot refine a SQL query');
|
|
3072
748
|
}
|