@malloydata/malloy 0.0.177-dev240828173040 → 0.0.177-dev240829030921

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.
Files changed (121) hide show
  1. package/dist/dialect/dialect.d.ts +23 -13
  2. package/dist/dialect/dialect.js +33 -27
  3. package/dist/dialect/duckdb/duckdb.d.ts +7 -11
  4. package/dist/dialect/duckdb/duckdb.js +34 -100
  5. package/dist/dialect/duckdb/functions/greatest_and_least.js +5 -12
  6. package/dist/dialect/functions/concat.js +1 -1
  7. package/dist/dialect/functions/greatest_and_least.js +1 -1
  8. package/dist/dialect/functions/simple_numeric_functions.js +1 -1
  9. package/dist/dialect/functions/sql.js +5 -15
  10. package/dist/dialect/functions/string_agg.js +2 -2
  11. package/dist/dialect/functions/trim_functions.js +2 -2
  12. package/dist/dialect/functions/util.d.ts +6 -9
  13. package/dist/dialect/functions/util.js +29 -38
  14. package/dist/dialect/index.d.ts +1 -1
  15. package/dist/dialect/index.js +1 -2
  16. package/dist/dialect/pg_impl.d.ts +15 -0
  17. package/dist/dialect/pg_impl.js +91 -0
  18. package/dist/dialect/postgres/functions/greatest_and_least.js +1 -3
  19. package/dist/dialect/postgres/functions/string_agg.js +2 -2
  20. package/dist/dialect/postgres/postgres.d.ts +7 -11
  21. package/dist/dialect/postgres/postgres.js +29 -92
  22. package/dist/dialect/snowflake/functions/string_agg.js +2 -2
  23. package/dist/dialect/snowflake/snowflake.d.ts +9 -9
  24. package/dist/dialect/snowflake/snowflake.js +41 -48
  25. package/dist/dialect/standardsql/functions/string_agg.js +4 -4
  26. package/dist/dialect/standardsql/standardsql.d.ts +9 -9
  27. package/dist/dialect/standardsql/standardsql.js +56 -51
  28. package/dist/dialect/trino/functions/concat.js +1 -1
  29. package/dist/dialect/trino/functions/string_agg.js +2 -2
  30. package/dist/dialect/trino/trino.d.ts +11 -12
  31. package/dist/dialect/trino/trino.js +59 -83
  32. package/dist/index.d.ts +3 -3
  33. package/dist/index.js +3 -4
  34. package/dist/lang/ast/ast-utils.d.ts +1 -1
  35. package/dist/lang/ast/ast-utils.js +2 -2
  36. package/dist/lang/ast/expressions/apply.js +1 -2
  37. package/dist/lang/ast/expressions/binary-boolean.d.ts +2 -1
  38. package/dist/lang/ast/expressions/binary-boolean.js +6 -3
  39. package/dist/lang/ast/expressions/binary-numeric.d.ts +2 -1
  40. package/dist/lang/ast/expressions/boolean.js +1 -1
  41. package/dist/lang/ast/expressions/constant-expression.d.ts +3 -3
  42. package/dist/lang/ast/expressions/constant-expression.js +0 -23
  43. package/dist/lang/ast/expressions/expr-aggregate-function.js +24 -34
  44. package/dist/lang/ast/expressions/expr-alternation-tree.d.ts +6 -3
  45. package/dist/lang/ast/expressions/expr-alternation-tree.js +10 -5
  46. package/dist/lang/ast/expressions/expr-cast.js +1 -2
  47. package/dist/lang/ast/expressions/expr-coalesce.js +4 -1
  48. package/dist/lang/ast/expressions/expr-compare.d.ts +3 -3
  49. package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
  50. package/dist/lang/ast/expressions/expr-count.js +3 -3
  51. package/dist/lang/ast/expressions/expr-func.js +15 -18
  52. package/dist/lang/ast/expressions/expr-granular-time.js +8 -10
  53. package/dist/lang/ast/expressions/expr-id-reference.js +2 -2
  54. package/dist/lang/ast/expressions/expr-minus.js +5 -4
  55. package/dist/lang/ast/expressions/expr-not.js +1 -1
  56. package/dist/lang/ast/expressions/expr-now.js +1 -6
  57. package/dist/lang/ast/expressions/expr-null.js +1 -1
  58. package/dist/lang/ast/expressions/expr-number.js +1 -1
  59. package/dist/lang/ast/expressions/expr-parens.js +1 -1
  60. package/dist/lang/ast/expressions/expr-props.js +4 -7
  61. package/dist/lang/ast/expressions/expr-regex.js +1 -3
  62. package/dist/lang/ast/expressions/expr-string.js +1 -7
  63. package/dist/lang/ast/expressions/expr-time-extract.js +12 -16
  64. package/dist/lang/ast/expressions/expr-time.d.ts +2 -2
  65. package/dist/lang/ast/expressions/expr-time.js +4 -6
  66. package/dist/lang/ast/expressions/expr-ungroup.js +2 -2
  67. package/dist/lang/ast/expressions/for-range.d.ts +2 -1
  68. package/dist/lang/ast/expressions/function-ordering.js +5 -5
  69. package/dist/lang/ast/expressions/partial-compare.d.ts +3 -3
  70. package/dist/lang/ast/expressions/pick-when.js +24 -8
  71. package/dist/lang/ast/expressions/range.d.ts +2 -1
  72. package/dist/lang/ast/expressions/range.js +4 -2
  73. package/dist/lang/ast/expressions/time-literal.d.ts +4 -3
  74. package/dist/lang/ast/expressions/time-literal.js +5 -10
  75. package/dist/lang/ast/expressions/top-by.js +1 -2
  76. package/dist/lang/ast/expressions/utils.d.ts +3 -12
  77. package/dist/lang/ast/expressions/utils.js +8 -56
  78. package/dist/lang/ast/field-space/reference-field.js +1 -1
  79. package/dist/lang/ast/index.d.ts +1 -1
  80. package/dist/lang/ast/index.js +1 -1
  81. package/dist/lang/ast/query-builders/index-builder.d.ts +2 -2
  82. package/dist/lang/ast/query-builders/reduce-builder.d.ts +2 -2
  83. package/dist/lang/ast/query-items/field-declaration.js +1 -5
  84. package/dist/lang/ast/query-properties/filters.d.ts +3 -3
  85. package/dist/lang/ast/query-properties/filters.js +6 -5
  86. package/dist/lang/ast/query-properties/top.js +1 -2
  87. package/dist/lang/ast/source-elements/refined-source.js +1 -1
  88. package/dist/lang/ast/source-properties/joins.js +11 -10
  89. package/dist/lang/ast/time-utils.d.ts +5 -7
  90. package/dist/lang/ast/time-utils.js +17 -57
  91. package/dist/lang/ast/types/binary_operators.d.ts +9 -0
  92. package/dist/lang/ast/types/binary_operators.js +28 -0
  93. package/dist/lang/ast/types/expr-result.d.ts +3 -3
  94. package/dist/lang/ast/types/expression-def.d.ts +4 -3
  95. package/dist/lang/ast/types/expression-def.js +43 -50
  96. package/dist/lang/lib/Malloy/MalloyParser.d.ts +0 -11
  97. package/dist/lang/lib/Malloy/MalloyParser.js +803 -858
  98. package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +0 -13
  99. package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +0 -8
  100. package/dist/lang/malloy-to-ast.d.ts +0 -1
  101. package/dist/lang/malloy-to-ast.js +0 -4
  102. package/dist/lang/test/expressions.spec.js +42 -86
  103. package/dist/lang/test/field-symbols.spec.js +8 -6
  104. package/dist/lang/test/literals.spec.js +6 -6
  105. package/dist/lang/test/model_serialization.spec.js +5 -5
  106. package/dist/lang/test/test-translator.js +9 -7
  107. package/dist/malloy.d.ts +3 -3
  108. package/dist/malloy.js +1 -1
  109. package/dist/model/index.d.ts +1 -1
  110. package/dist/model/index.js +2 -1
  111. package/dist/model/malloy_query.d.ts +24 -19
  112. package/dist/model/malloy_query.js +272 -244
  113. package/dist/model/malloy_types.d.ts +224 -205
  114. package/dist/model/malloy_types.js +19 -98
  115. package/dist/model/utils.d.ts +10 -3
  116. package/dist/model/utils.js +84 -219
  117. package/package.json +1 -1
  118. package/dist/lang/ast/types/comparison.d.ts +0 -11
  119. package/dist/lang/ast/types/comparison.js +0 -41
  120. package/dist/lang/ast/types/equality.d.ts +0 -7
  121. package/dist/lang/ast/types/equality.js +0 -37
@@ -235,7 +235,7 @@ class QueryField extends QueryNode {
235
235
  // find the structDef and return the path to the field...
236
236
  const field = context.getFieldByName(expr.path);
237
237
  if ((0, malloy_types_1.hasExpression)(field.fieldDef)) {
238
- const ret = this.generateExpressionFromExpr(resultSet, field.parent, field.fieldDef.e, state);
238
+ const ret = this.exprToSQL(resultSet, field.parent, field.fieldDef.e, state);
239
239
  // in order to avoid too many parens, there was some code here ..
240
240
  // if (!ret.match(/^\(.*\)$/)) {
241
241
  // ret = `(${ret})`;
@@ -253,8 +253,19 @@ class QueryField extends QueryNode {
253
253
  generateOutputFieldFragment(resultSet, _context, frag, _state) {
254
254
  return `(${resultSet.getField(frag.name).getAnalyticalSQL(false)})`;
255
255
  }
256
- generateSQLExpression(resultSet, context, frag, state) {
257
- return this.generateExpressionFromExpr(resultSet, context, frag.e, state);
256
+ *stringsFromSQLExpression(resultSet, context, e, state) {
257
+ /*
258
+ * Like template strings, the array of strings is paired with template insertions,
259
+ * each string is followed by at most one expression to be inserted
260
+ */
261
+ const subExprList = [...e.kids.args];
262
+ for (const str of e.src) {
263
+ yield str;
264
+ const expr = subExprList.shift();
265
+ if (expr) {
266
+ yield this.exprToSQL(resultSet, context, expr, state);
267
+ }
268
+ }
258
269
  }
259
270
  getParameterMap(overload, numArgs) {
260
271
  return new Map(overload.params.map((param, paramIndex) => {
@@ -265,53 +276,61 @@ class QueryField extends QueryNode {
265
276
  }));
266
277
  }
267
278
  expandFunctionCall(dialect, overload, args, orderBy, limit) {
279
+ function withCommas(es) {
280
+ const ret = [];
281
+ for (let i = 0; i < es.length;) {
282
+ ret.push(es[i]);
283
+ i += 1;
284
+ if (i < es.length) {
285
+ ret.push(',');
286
+ }
287
+ }
288
+ return ret;
289
+ }
268
290
  const paramMap = this.getParameterMap(overload, args.length);
269
291
  if (overload.dialect[dialect] === undefined) {
270
- throw new Error(`Function is not defined for dialect ${dialect}`);
292
+ throw new Error(`Function is not defined for '${dialect}' dialect`);
271
293
  }
272
- return (0, utils_1.exprMap)(overload.dialect[dialect].e, fragment => {
294
+ const expanded = (0, utils_1.exprMap)(overload.dialect[dialect].e, fragment => {
273
295
  var _a, _b;
274
- if (typeof fragment === 'string') {
275
- return [fragment];
276
- }
277
- else if (fragment.type === 'spread') {
278
- const param = fragment.e[0];
279
- if (fragment.e.length !== 1 ||
280
- typeof param === 'string' ||
281
- param.type !== 'function_parameter') {
296
+ if (fragment.node === 'spread') {
297
+ const param = fragment.e;
298
+ if (param.node !== 'function_parameter') {
282
299
  throw new Error('Invalid function definition. Argument to spread must be a function parameter.');
283
300
  }
284
301
  const entry = paramMap.get(param.name);
285
302
  if (entry === undefined) {
286
- return [fragment];
287
- }
288
- else {
289
- return (0, utils_1.joinWith)(entry.argIndexes.map(argIndex => args[argIndex]), ',');
303
+ return fragment;
290
304
  }
305
+ const spread = entry.argIndexes.map(argIndex => args[argIndex]);
306
+ return (0, utils_1.composeSQLExpr)(withCommas(spread));
291
307
  }
292
- else if (fragment.type === 'function_parameter') {
308
+ else if (fragment.node === 'function_parameter') {
293
309
  const entry = paramMap.get(fragment.name);
294
310
  if (entry === undefined) {
295
- return [fragment];
311
+ return fragment;
296
312
  }
297
313
  else if (entry.param.isVariadic) {
298
- const spread = (0, utils_1.joinWith)(entry.argIndexes.map(argIndex => args[argIndex]), ',');
299
- return ['[', ...spread, ']'];
314
+ const spread = entry.argIndexes.map(argIndex => args[argIndex]);
315
+ return (0, utils_1.composeSQLExpr)(withCommas(spread));
300
316
  }
301
317
  else {
302
318
  return args[entry.argIndexes[0]];
303
319
  }
304
320
  }
305
- else if (fragment.type === 'aggregate_order_by') {
321
+ else if (fragment.node === 'aggregate_order_by') {
306
322
  return orderBy
307
- ? [` ${(_a = fragment.prefix) !== null && _a !== void 0 ? _a : ''}${orderBy}${(_b = fragment.suffix) !== null && _b !== void 0 ? _b : ''}`]
308
- : [];
323
+ ? (0, utils_1.composeSQLExpr)([
324
+ ` ${(_a = fragment.prefix) !== null && _a !== void 0 ? _a : ''}${orderBy}${(_b = fragment.suffix) !== null && _b !== void 0 ? _b : ''}`,
325
+ ])
326
+ : { node: '' };
309
327
  }
310
- else if (fragment.type === 'aggregate_limit') {
311
- return limit ? [` ${limit}`] : [];
328
+ else if (fragment.node === 'aggregate_limit') {
329
+ return limit ? (0, utils_1.composeSQLExpr)([` ${limit}`]) : { node: '' };
312
330
  }
313
- return [fragment];
331
+ return fragment;
314
332
  });
333
+ return expanded;
315
334
  }
316
335
  getFunctionOrderBy(resultSet, context, state, orderBy, args, overload) {
317
336
  if (orderBy.length === 0)
@@ -319,9 +338,9 @@ class QueryField extends QueryNode {
319
338
  return ('ORDER BY ' +
320
339
  orderBy
321
340
  .map(ob => {
322
- var _a, _b;
341
+ var _a;
323
342
  const defaultOrderByArgIndex = (_a = overload.dialect[context.dialect.name].defaultOrderByArgIndex) !== null && _a !== void 0 ? _a : 0;
324
- const expr = (_b = ob.e) !== null && _b !== void 0 ? _b : args[defaultOrderByArgIndex];
343
+ const expr = ob.node === 'functionOrderBy' ? ob.e : args[defaultOrderByArgIndex];
325
344
  const osql = this.generateDimFragment(resultSet, context, expr, state);
326
345
  const dirsql = ob.dir === 'asc' ? ' ASC' : ob.dir === 'desc' ? ' DESC' : '';
327
346
  return `${osql}${dirsql}`;
@@ -345,7 +364,7 @@ class QueryField extends QueryNode {
345
364
  generateFunctionCallExpression(resultSet, context, frag, state) {
346
365
  var _a, _b, _c;
347
366
  const overload = frag.overload;
348
- const args = frag.args;
367
+ const args = frag.kids.args;
349
368
  const isSymmetric = (_a = frag.overload.isSymmetric) !== null && _a !== void 0 ? _a : false;
350
369
  const distinctKey = (0, malloy_types_1.expressionIsAggregate)(overload.returnType.expressionType) &&
351
370
  !isSymmetric &&
@@ -355,7 +374,7 @@ class QueryField extends QueryNode {
355
374
  distinctKey &&
356
375
  !context.dialect.supportsAggDistinct &&
357
376
  context.dialect.name !== 'snowflake') {
358
- return this.generateAsymmetricStringAggExpression(resultSet, context, args[0], args[1], distinctKey, frag.orderBy, context.dialect.name, state);
377
+ return this.generateAsymmetricStringAggExpression(resultSet, context, args[0], args[1], distinctKey, frag.kids.orderBy, context.dialect.name, state);
359
378
  }
360
379
  if (distinctKey) {
361
380
  if (!context.dialect.supportsAggDistinct) {
@@ -364,11 +383,11 @@ class QueryField extends QueryNode {
364
383
  const argsExpressions = args.map(arg => {
365
384
  return this.generateDimFragment(resultSet, context, arg, state);
366
385
  });
367
- const orderBys = (_b = frag.orderBy) !== null && _b !== void 0 ? _b : [];
386
+ const orderBys = (_b = frag.kids.orderBy) !== null && _b !== void 0 ? _b : [];
368
387
  const orderByExpressions = orderBys.map(ob => {
369
- var _a, _b;
388
+ var _a;
370
389
  const defaultOrderByArgIndex = (_a = overload.dialect[context.dialect.name].defaultOrderByArgIndex) !== null && _a !== void 0 ? _a : 0;
371
- const expr = (_b = ob.e) !== null && _b !== void 0 ? _b : args[defaultOrderByArgIndex];
390
+ const expr = ob.node === 'functionOrderBy' ? ob.e : args[defaultOrderByArgIndex];
372
391
  return this.generateDimFragment(resultSet, context, expr, state);
373
392
  });
374
393
  return context.dialect.sqlAggDistinct(distinctKey, [...argsExpressions, ...orderByExpressions], valNames => {
@@ -382,17 +401,17 @@ class QueryField extends QueryNode {
382
401
  return args[i];
383
402
  }
384
403
  }
385
- return [v];
404
+ return (0, utils_1.composeSQLExpr)([v]);
386
405
  });
387
406
  const newArgs = vals.slice(0, argsExpressions.length);
388
407
  const orderBy = vals
389
408
  .slice(argsExpressions.length)
390
409
  .map((e, i) => {
391
- return { e, dir: orderBys[i].dir };
410
+ return { node: 'functionOrderBy', e, dir: orderBys[i].dir };
392
411
  });
393
412
  const orderBySQL = this.getFunctionOrderBy(resultSet, context, state, orderBy, newArgs, overload);
394
413
  const funcCall = this.expandFunctionCall(context.dialect.name, overload, newArgs, orderBySQL, aggregateLimit);
395
- return this.generateExpressionFromExpr(resultSet, context, funcCall, state);
414
+ return this.exprToSQL(resultSet, context, funcCall, state);
396
415
  });
397
416
  }
398
417
  else {
@@ -413,11 +432,13 @@ class QueryField extends QueryNode {
413
432
  // fragment.
414
433
  return param.allowedTypes.every(t => (0, malloy_types_1.isLiteral)(t.evalSpace))
415
434
  ? arg
416
- : [this.generateDimFragment(resultSet, context, arg, state)];
435
+ : (0, utils_1.composeSQLExpr)([
436
+ this.generateDimFragment(resultSet, context, arg, state),
437
+ ]);
417
438
  })
418
439
  : args;
419
- const orderBySql = frag.orderBy
420
- ? this.getFunctionOrderBy(resultSet, context, state, frag.orderBy, args, overload)
440
+ const orderBySql = frag.kids.orderBy
441
+ ? this.getFunctionOrderBy(resultSet, context, state, frag.kids.orderBy, args, overload)
421
442
  : '';
422
443
  const funcCall = this.expandFunctionCall(context.dialect.name, overload, mappedArgs, orderBySql, aggregateLimit);
423
444
  if ((0, malloy_types_1.expressionIsAnalytic)(overload.returnType.expressionType)) {
@@ -426,7 +447,7 @@ class QueryField extends QueryNode {
426
447
  });
427
448
  return this.generateAnalyticFragment(resultSet, context, funcCall, overload, state, args, extraPartitions, orderBySql);
428
449
  }
429
- return this.generateExpressionFromExpr(resultSet, context, funcCall, state);
450
+ return this.exprToSQL(resultSet, context, funcCall, state);
430
451
  }
431
452
  }
432
453
  generateSpread(_resultSet, _context, _frag, _state) {
@@ -436,19 +457,19 @@ class QueryField extends QueryNode {
436
457
  const name = expr.path[0];
437
458
  const argument = context.arguments()[name];
438
459
  if (argument.value) {
439
- return this.generateExpressionFromExpr(resultSet, context, argument.value, state);
460
+ return this.exprToSQL(resultSet, context, argument.value, state);
440
461
  }
441
462
  throw new Error(`Can't generate SQL, no value for ${expr.path}`);
442
463
  }
443
464
  generateFilterFragment(resultSet, context, expr, state) {
444
465
  const allWhere = new utils_1.AndChain(state.whereSQL);
445
- for (const cond of expr.filterList) {
446
- allWhere.add(this.generateExpressionFromExpr(resultSet, context, cond.expression, state.withWhere()));
466
+ for (const cond of expr.kids.filterList) {
467
+ allWhere.add(this.exprToSQL(resultSet, context, cond.e, state.withWhere()));
447
468
  }
448
- return this.generateExpressionFromExpr(resultSet, context, expr.e, state.withWhere(allWhere.sql()));
469
+ return this.exprToSQL(resultSet, context, expr.kids.e, state.withWhere(allWhere.sql()));
449
470
  }
450
471
  generateDimFragment(resultSet, context, expr, state) {
451
- let dim = this.generateExpressionFromExpr(resultSet, context, expr, state);
472
+ let dim = this.exprToSQL(resultSet, context, expr, state);
452
473
  if (state.whereSQL) {
453
474
  dim = `CASE WHEN ${state.whereSQL} THEN ${dim} END`;
454
475
  }
@@ -461,7 +482,7 @@ class QueryField extends QueryNode {
461
482
  let totalGroupSet;
462
483
  let ungroupSet;
463
484
  if (expr.fields && expr.fields.length > 0) {
464
- const key = expr.fields.sort().join('|') + expr.type;
485
+ const key = expr.fields.sort().join('|') + expr.node;
465
486
  ungroupSet = resultSet.ungroupedSets.get(key);
466
487
  if (ungroupSet === undefined) {
467
488
  throw new Error(`Internal Error, cannot find groupset with key ${key}`);
@@ -471,7 +492,7 @@ class QueryField extends QueryNode {
471
492
  else {
472
493
  totalGroupSet = resultSet.parent ? resultSet.parent.groupSet : 0;
473
494
  }
474
- const s = this.generateExpressionFromExpr(resultSet, context, expr.e, state.withTotal(totalGroupSet));
495
+ const s = this.exprToSQL(resultSet, context, expr.e, state.withTotal(totalGroupSet));
475
496
  const fields = resultSet.getUngroupPartitions(ungroupSet);
476
497
  let partitionBy = '';
477
498
  const fieldsString = fields.map(f => f.getAnalyticalSQL(true)).join(', ');
@@ -511,9 +532,7 @@ class QueryField extends QueryNode {
511
532
  }
512
533
  generateSymmetricFragment(resultSet, context, expr, state) {
513
534
  const dimSQL = this.generateDimFragment(resultSet, context, expr.e, state);
514
- const f = expr.function === 'count_distinct'
515
- ? 'count(distinct '
516
- : expr.function + '(';
535
+ const f = expr.function === 'distinct' ? 'count(distinct ' : expr.function + '(';
517
536
  return `${f}${dimSQL})`;
518
537
  }
519
538
  generateAvgFragment(resultSet, context, expr, state) {
@@ -573,16 +592,6 @@ class QueryField extends QueryNode {
573
592
  return `${func}${thing})`;
574
593
  }
575
594
  }
576
- generateDialect(resultSet, context, expr, state) {
577
- return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(resultSet.getQueryInfo(), expr), state);
578
- }
579
- generateSqlString(resultSet, context, expr, state) {
580
- return expr.e
581
- .map(part => typeof part === 'string'
582
- ? part
583
- : this.generateExpressionFromExpr(resultSet, context, [part], state))
584
- .join('');
585
- }
586
595
  generateSourceReference(resultSet, context, expr) {
587
596
  if (expr.path === undefined) {
588
597
  return context.getSQLIdentifier();
@@ -666,44 +675,85 @@ class QueryField extends QueryNode {
666
675
  }
667
676
  const argIndex = overload.params.findIndex(param => param.name === value);
668
677
  const arg = args[argIndex];
669
- if (arg.length !== 1 ||
670
- typeof arg[0] === 'string' ||
671
- arg[0].type !== 'dialect' ||
672
- arg[0].function !== 'numberLiteral') {
678
+ if (arg.node !== 'numberLiteral') {
673
679
  throw new Error('Invalid number of rows for window spec');
674
680
  }
675
681
  // TODO this does not handle float literals correctly
676
- return arg[0].literal;
682
+ return arg.literal;
677
683
  });
678
684
  between = `ROWS BETWEEN ${preceding} PRECEDING AND ${following} FOLLOWING`;
679
685
  }
680
- const funcSQL = this.generateExpressionFromExpr(resultStruct, context, expr, state);
686
+ const funcSQL = this.exprToSQL(resultStruct, context, expr, state);
681
687
  let retExpr = `${funcSQL} OVER(${partitionBy} ${orderBy} ${between})`;
682
688
  if (isComplex) {
683
689
  retExpr = `CASE WHEN group_set=${resultStruct.groupSet} THEN ${retExpr} END`;
684
690
  }
685
691
  return retExpr;
686
692
  }
687
- generateExpressionFromExpr(resultSet, context, e, state = new GenerateState()) {
688
- let s = '';
689
- for (const expr of e) {
690
- if (typeof expr === 'string') {
691
- s += expr;
692
- }
693
- else if ((0, malloy_types_1.isFieldFragment)(expr)) {
694
- s += this.generateFieldFragment(resultSet, context, expr, state);
695
- }
696
- else if ((0, malloy_types_1.isParameterFragment)(expr)) {
697
- s += this.generateParameterFragment(resultSet, context, expr, state);
698
- }
699
- else if ((0, malloy_types_1.isFilterFragment)(expr)) {
700
- s += this.generateFilterFragment(resultSet, context, expr, state);
701
- }
702
- else if ((0, malloy_types_1.isUngroupFragment)(expr)) {
703
- s += this.generateUngroupedFragment(resultSet, context, expr, state);
693
+ generatePickSQL(pf) {
694
+ const caseStmt = ['CASE'];
695
+ for (let i = 0; i < pf.kids.pickWhen.length; i += 1) {
696
+ caseStmt.push(`WHEN ${pf.kids.pickWhen[i].sql} THEN ${pf.kids.pickThen[i].sql}`);
697
+ }
698
+ caseStmt.push(`ELSE ${pf.kids.pickElse.sql}`, 'END');
699
+ return caseStmt.join(' ');
700
+ }
701
+ exprToSQL(resultSet, context, exprToTranslate, state = new GenerateState()) {
702
+ // Wrap non leaf sub expressions in parenthesis
703
+ const subExpr = function (qf, e) {
704
+ const sql = qf.exprToSQL(resultSet, context, e, state);
705
+ if ((0, malloy_types_1.exprHasKids)(e)) {
706
+ return `(${sql})`;
704
707
  }
705
- else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
706
- let agg;
708
+ return sql;
709
+ };
710
+ /*
711
+ * Translate the children first, and stash the translation
712
+ * in the nodes themselves, so that if we call into the dialect
713
+ * it will have access to the translated children.
714
+ */
715
+ let expr = exprToTranslate;
716
+ if ((0, malloy_types_1.exprHasE)(exprToTranslate)) {
717
+ expr = { ...exprToTranslate };
718
+ const eSql = subExpr(this, expr.e);
719
+ expr.e = { ...expr.e, sql: eSql };
720
+ }
721
+ else if ((0, malloy_types_1.exprHasKids)(exprToTranslate)) {
722
+ expr = { ...exprToTranslate };
723
+ const oldKids = exprToTranslate.kids;
724
+ for (const [name, kidExpr] of Object.entries(oldKids)) {
725
+ if (Array.isArray(kidExpr)) {
726
+ expr.kids[name] = kidExpr.map(e => {
727
+ return { ...e, sql: subExpr(this, e) };
728
+ });
729
+ }
730
+ else {
731
+ expr.kids[name] = { ...oldKids[name], sql: subExpr(this, kidExpr) };
732
+ }
733
+ }
734
+ }
735
+ /*
736
+ * Give the dialect a chance to translate this node
737
+ */
738
+ const qi = resultSet.getQueryInfo();
739
+ const dialectSQL = this.parent.dialect.exprToSQL(qi, expr);
740
+ if (dialectSQL) {
741
+ return dialectSQL;
742
+ }
743
+ switch (expr.node) {
744
+ case 'field':
745
+ return this.generateFieldFragment(resultSet, context, expr, state);
746
+ case 'parameter':
747
+ return this.generateParameterFragment(resultSet, context, expr, state);
748
+ case 'filteredExpr':
749
+ return this.generateFilterFragment(resultSet, context, expr, state);
750
+ case 'all':
751
+ case 'exclude':
752
+ return this.generateUngroupedFragment(resultSet, context, expr, state);
753
+ case 'genericSQLExpr':
754
+ return Array.from(this.stringsFromSQLExpression(resultSet, context, expr, state)).join('');
755
+ case 'aggregate': {
756
+ let agg = '';
707
757
  if (expr.function === 'sum') {
708
758
  agg = this.generateSumFragment(resultSet, context, expr, state);
709
759
  }
@@ -713,7 +763,9 @@ class QueryField extends QueryNode {
713
763
  else if (expr.function === 'count') {
714
764
  agg = this.generateCountFragment(resultSet, context, expr, state);
715
765
  }
716
- else if (['count_distinct', 'min', 'max'].includes(expr.function)) {
766
+ else if (expr.function === 'min' ||
767
+ expr.function === 'max' ||
768
+ expr.function === 'distinct') {
717
769
  agg = this.generateSymmetricFragment(resultSet, context, expr, state);
718
770
  }
719
771
  else {
@@ -724,68 +776,92 @@ class QueryField extends QueryNode {
724
776
  if (state.totalGroupSet !== -1) {
725
777
  groupSet = state.totalGroupSet;
726
778
  }
727
- s += this.caseGroup([groupSet], agg);
728
- }
729
- else {
730
- s += agg;
731
- }
732
- }
733
- else if ((0, malloy_types_1.isApplyFragment)(expr)) {
734
- const applyVal = this.generateExpressionFromExpr(resultSet, context, expr.value, state);
735
- s += this.generateExpressionFromExpr(resultSet, context, expr.to, state.withApply(applyVal));
736
- }
737
- else if ((0, malloy_types_1.isApplyValue)(expr)) {
738
- if (state.applyValue) {
739
- s += state.applyValue;
740
- }
741
- else {
742
- throw new Error('Internal Error: Partial application value referenced but not provided');
779
+ return this.caseGroup([groupSet], agg);
743
780
  }
781
+ return agg;
744
782
  }
745
- else if ((0, malloy_types_1.isFunctionParameterFragment)(expr)) {
783
+ case 'function_parameter':
746
784
  throw new Error('Internal Error: Function parameter fragment remaining during SQL generation');
747
- }
748
- else if ((0, malloy_types_1.isOutputFieldFragment)(expr)) {
749
- s += this.generateOutputFieldFragment(resultSet, context, expr, state);
750
- }
751
- else if ((0, malloy_types_1.isSQLExpressionFragment)(expr)) {
752
- s += this.generateSQLExpression(resultSet, context, expr, state);
753
- }
754
- else if ((0, malloy_types_1.isFunctionCallFragment)(expr)) {
755
- s += this.generateFunctionCallExpression(resultSet, context, expr, state);
756
- }
757
- else if ((0, malloy_types_1.isSpreadFragment)(expr)) {
758
- s += this.generateSpread(resultSet, context, expr, state);
759
- }
760
- else if (expr.type === 'dialect') {
761
- s += this.generateDialect(resultSet, context, expr, state);
762
- }
763
- else if (expr.type === 'sql-string') {
764
- s += this.generateSqlString(resultSet, context, expr, state);
765
- }
766
- else if (expr.type === 'source-reference') {
767
- s += this.generateSourceReference(resultSet, context, expr);
768
- }
769
- else {
770
- throw new Error(`Internal Error: Unknown expression fragment ${JSON.stringify(expr, undefined, 2)}`);
771
- }
785
+ case 'outputField':
786
+ return this.generateOutputFieldFragment(resultSet, context, expr, state);
787
+ case 'function_call':
788
+ return this.generateFunctionCallExpression(resultSet, context, expr, state);
789
+ case 'spread':
790
+ return this.generateSpread(resultSet, context, expr, state);
791
+ case 'source-reference':
792
+ return this.generateSourceReference(resultSet, context, expr);
793
+ case '+':
794
+ case '-':
795
+ case '*':
796
+ case '%':
797
+ case '/':
798
+ case '=':
799
+ case '!=':
800
+ case '>':
801
+ case '<':
802
+ case '>=':
803
+ case '<=':
804
+ return `${expr.kids.left.sql}${expr.node}${expr.kids.right.sql}`;
805
+ case 'and':
806
+ case 'or':
807
+ return `${expr.kids.left.sql} ${expr.node} ${expr.kids.right.sql}`;
808
+ case 'coalesce':
809
+ return `COALESCE(${expr.kids.left.sql},${expr.kids.right.sql})`;
810
+ case 'like':
811
+ return `${expr.kids.left.sql} LIKE ${expr.kids.right.sql}`;
812
+ case '!like':
813
+ return `${expr.kids.left.sql} NOT LIKE ${expr.kids.right.sql}`;
814
+ case '()':
815
+ return `(${expr.e.sql})`;
816
+ case 'not':
817
+ return `NOT ${expr.e.sql}`;
818
+ case 'unary-':
819
+ return `-${expr.e.sql}`;
820
+ case 'is-null':
821
+ return `${expr.e.sql} IS NULL`;
822
+ case 'is-not-null':
823
+ return `${expr.e.sql} IS NOT NULL`;
824
+ case 'true':
825
+ case 'false':
826
+ return expr.node;
827
+ case 'null':
828
+ return 'NULL';
829
+ case 'pick':
830
+ return this.generatePickSQL(expr);
831
+ case '':
832
+ return '';
833
+ case 'filterCondition':
834
+ // our child will be translated at the top of this function
835
+ if (expr.e.sql) {
836
+ expr.sql = expr.e.sql;
837
+ return expr.sql;
838
+ }
839
+ return '';
840
+ case 'functionDefaultOrderBy':
841
+ case 'functionOrderBy':
842
+ return '';
843
+ default:
844
+ throw new Error(`Internal Error: Unknown expression node '${expr.node}' ${JSON.stringify(expr, undefined, 2)}`);
772
845
  }
773
- return s;
774
846
  }
775
847
  getExpr() {
776
848
  if ((0, malloy_types_1.hasExpression)(this.fieldDef)) {
777
849
  return this.fieldDef.e;
778
850
  }
779
- return [
780
- this.parent.dialect.sqlFieldReference(this.parent.getSQLIdentifier(), this.fieldDef.name, this.fieldDef.type, this.parent.fieldDef.structSource.type === 'nested' ||
781
- this.parent.fieldDef.structSource.type === 'inline' ||
782
- (this.parent.fieldDef.structSource.type === 'sql' &&
783
- this.parent.fieldDef.structSource.method === 'nested'), this.parent.fieldDef.structRelationship.type === 'nested' &&
784
- this.parent.fieldDef.structRelationship.isArray),
785
- ];
851
+ return {
852
+ node: 'genericSQLExpr',
853
+ kids: { args: [] },
854
+ src: [
855
+ this.parent.dialect.sqlFieldReference(this.parent.getSQLIdentifier(), this.fieldDef.name, this.fieldDef.type, this.parent.fieldDef.structSource.type === 'nested' ||
856
+ this.parent.fieldDef.structSource.type === 'inline' ||
857
+ (this.parent.fieldDef.structSource.type === 'sql' &&
858
+ this.parent.fieldDef.structSource.method === 'nested'), this.parent.fieldDef.structRelationship.type === 'nested' &&
859
+ this.parent.fieldDef.structRelationship.isArray),
860
+ ],
861
+ };
786
862
  }
787
863
  generateExpression(resultSet) {
788
- return this.generateExpressionFromExpr(resultSet, this.parent, this.getExpr());
864
+ return this.exprToSQL(resultSet, this.parent, this.getExpr());
789
865
  }
790
866
  }
791
867
  function isCalculatedField(f) {
@@ -843,14 +919,13 @@ class QueryFieldStruct extends QueryAtomicField {
843
919
  structRelationship: {
844
920
  type: 'one',
845
921
  matrixOperation: 'left',
846
- onExpression: [
847
- {
848
- type: 'field',
849
- path: [this.primaryKey],
922
+ onExpression: {
923
+ node: '=',
924
+ kids: {
925
+ left: { node: 'field', path: [this.primaryKey] },
926
+ right: { node: 'field', path: [foreignKeyName] },
850
927
  },
851
- '=',
852
- { type: 'field', path: [foreignKeyName] },
853
- ],
928
+ },
854
929
  },
855
930
  };
856
931
  }
@@ -862,8 +937,12 @@ class QueryFieldDate extends QueryAtomicField {
862
937
  return super.generateExpression(resultSet);
863
938
  }
864
939
  else {
865
- const truncated = this.parent.dialect.sqlTrunc(resultSet.getQueryInfo(), { value: this.getExpr(), valueType: 'date' }, fd.timeframe);
866
- return this.generateExpressionFromExpr(resultSet, this.parent, truncated);
940
+ const truncated = {
941
+ node: 'trunc',
942
+ e: { ...this.getExpr(), dataType: 'date' },
943
+ units: fd.timeframe,
944
+ };
945
+ return this.exprToSQL(resultSet, this.parent, truncated);
867
946
  }
868
947
  }
869
948
  // clone ourselves on demand as a timeframe.
@@ -973,6 +1052,12 @@ class FieldInstanceResult {
973
1052
  this.resultUsesUngrouped = false;
974
1053
  this.firstSegment = turtleDef.pipeline[0];
975
1054
  }
1055
+ /**
1056
+ * Information about the query containing this result set. Invented
1057
+ * to pass on timezone information, but maybe more things will
1058
+ * eventually go in here.
1059
+ * @returns QueryInfo
1060
+ */
976
1061
  getQueryInfo() {
977
1062
  if (!(0, malloy_types_1.isIndexSegment)(this.firstSegment) &&
978
1063
  !(0, malloy_types_1.isRawSegment)(this.firstSegment)) {
@@ -1371,7 +1456,7 @@ class JoinInstance {
1371
1456
  const qf = new QueryFieldBoolean({
1372
1457
  type: 'boolean',
1373
1458
  name: 'ignoreme',
1374
- e: filter.expression,
1459
+ e: filter.e,
1375
1460
  }, this.queryStruct);
1376
1461
  this.joinFilterConditions.push(qf);
1377
1462
  }
@@ -1528,31 +1613,46 @@ class QueryQuery extends QueryField {
1528
1613
  }
1529
1614
  addDependantExpr(resultStruct, context, e, joinStack) {
1530
1615
  var _a;
1531
- for (const expr of e) {
1532
- if ((0, malloy_types_1.isFunctionCallFragment)(expr) &&
1533
- (0, malloy_types_1.expressionIsAnalytic)(expr.overload.returnType.expressionType) &&
1534
- this.parent.dialect.cantPartitionWindowFunctionsOnExpressions) {
1535
- // force the use of a lateral_join_bag
1536
- resultStruct.root().isComplexQuery = true;
1537
- resultStruct.root().queryUsesPartitioning = true;
1616
+ for (const expr of (0, utils_1.exprWalk)(e)) {
1617
+ if (expr.node === 'function_call') {
1618
+ if ((0, malloy_types_1.expressionIsAnalytic)(expr.overload.returnType.expressionType) &&
1619
+ this.parent.dialect.cantPartitionWindowFunctionsOnExpressions) {
1620
+ // force the use of a lateral_join_bag
1621
+ resultStruct.root().isComplexQuery = true;
1622
+ resultStruct.root().queryUsesPartitioning = true;
1623
+ }
1624
+ const isSymmetric = (_a = expr.overload.isSymmetric) !== null && _a !== void 0 ? _a : false;
1625
+ const isAggregate = (0, malloy_types_1.expressionIsAggregate)(expr.overload.returnType.expressionType);
1626
+ const isAsymmetricAggregate = isAggregate && !isSymmetric;
1627
+ const uniqueKeyPossibleUse = isAsymmetricAggregate
1628
+ ? 'generic_asymmetric_aggregate'
1629
+ : undefined;
1630
+ if (expr.structPath) {
1631
+ this.addDependantPath(resultStruct, context, expr.structPath, uniqueKeyPossibleUse, joinStack);
1632
+ }
1633
+ else if (isAsymmetricAggregate) {
1634
+ resultStruct.addStructToJoin(context, this, uniqueKeyPossibleUse, joinStack);
1635
+ }
1636
+ if ((0, malloy_types_1.expressionIsAnalytic)(expr.overload.returnType.expressionType)) {
1637
+ resultStruct.root().queryUsesPartitioning = true;
1638
+ }
1538
1639
  }
1539
- if ((0, malloy_types_1.isUngroupFragment)(expr)) {
1640
+ else if (expr.node === 'all' || expr.node === 'exclude') {
1540
1641
  resultStruct.resultUsesUngrouped = true;
1541
1642
  resultStruct.root().isComplexQuery = true;
1542
1643
  resultStruct.root().queryUsesPartitioning = true;
1543
1644
  if (expr.fields && expr.fields.length > 0) {
1544
- const key = expr.fields.sort().join('|') + expr.type;
1645
+ const key = expr.fields.sort().join('|') + expr.node;
1545
1646
  if (resultStruct.ungroupedSets.get(key) === undefined) {
1546
1647
  resultStruct.ungroupedSets.set(key, {
1547
- type: expr.type,
1648
+ type: expr.node,
1548
1649
  fields: expr.fields,
1549
1650
  groupSet: -1,
1550
1651
  });
1551
1652
  }
1552
1653
  }
1553
- this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1554
1654
  }
1555
- else if ((0, malloy_types_1.isFieldFragment)(expr)) {
1655
+ if (expr.node === 'field') {
1556
1656
  const field = context.getDimensionOrMeasureByName(expr.path);
1557
1657
  if ((0, malloy_types_1.hasExpression)(field.fieldDef)) {
1558
1658
  this.addDependantExpr(resultStruct, field.parent, field.fieldDef.e, joinStack);
@@ -1561,54 +1661,10 @@ class QueryQuery extends QueryField {
1561
1661
  resultStruct
1562
1662
  .root()
1563
1663
  .addStructToJoin(field.parent.getJoinableParent(), this, undefined, joinStack);
1564
- // this.addDependantPath(resultStruct, field.parent, expr.path, false);
1565
- }
1566
- }
1567
- else if ((0, malloy_types_1.isFilterFragment)(expr)) {
1568
- for (const filterCond of expr.filterList) {
1569
- this.addDependantExpr(resultStruct, context, filterCond.expression, joinStack);
1570
- this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1571
- }
1572
- this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1573
- }
1574
- else if ((0, malloy_types_1.isDialectFragment)(expr)) {
1575
- const expressions = [];
1576
- switch (expr.function) {
1577
- case 'now':
1578
- break;
1579
- case 'div':
1580
- case 'mod':
1581
- expressions.push(expr.denominator);
1582
- expressions.push(expr.numerator);
1583
- break;
1584
- case 'numberLiteral':
1585
- case 'timeLiteral':
1586
- case 'stringLiteral':
1587
- case 'regexpLiteral':
1588
- break;
1589
- case 'timeDiff':
1590
- expressions.push(expr.left.value, expr.right.value);
1591
- break;
1592
- case 'delta':
1593
- expressions.push(expr.base.value, expr.delta);
1594
- break;
1595
- case 'trunc':
1596
- case 'extract':
1597
- expressions.push(expr.expr.value);
1598
- break;
1599
- case 'regexpMatch':
1600
- case 'cast':
1601
- expressions.push(expr.expr);
1602
- break;
1603
- default:
1604
- throw new Error("Unknown dialect Fragment type. Can't generate dependancies");
1605
- }
1606
- for (const e of expressions) {
1607
- this.addDependantExpr(resultStruct, context, e, joinStack);
1608
- }
1609
- }
1610
- else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
1611
- if ((0, malloy_types_1.isAsymmetricFragment)(expr)) {
1664
+ }
1665
+ }
1666
+ else if (expr.node === 'aggregate') {
1667
+ if ((0, malloy_types_1.isAsymmetricExpr)(expr)) {
1612
1668
  if (expr.structPath) {
1613
1669
  this.addDependantPath(resultStruct, context, expr.structPath, expr.function, joinStack);
1614
1670
  }
@@ -1617,34 +1673,6 @@ class QueryQuery extends QueryField {
1617
1673
  resultStruct.addStructToJoin(context, this, expr.function, joinStack);
1618
1674
  }
1619
1675
  }
1620
- this.addDependantExpr(resultStruct, context, expr.e, joinStack);
1621
- }
1622
- else if ((0, malloy_types_1.isFunctionCallFragment)(expr)) {
1623
- const isSymmetric = (_a = expr.overload.isSymmetric) !== null && _a !== void 0 ? _a : false;
1624
- const isAggregate = (0, malloy_types_1.expressionIsAggregate)(expr.overload.returnType.expressionType);
1625
- const isAsymmetricAggregate = isAggregate && !isSymmetric;
1626
- const uniqueKeyPossibleUse = isAsymmetricAggregate
1627
- ? 'generic_asymmetric_aggregate'
1628
- : undefined;
1629
- if (expr.structPath) {
1630
- this.addDependantPath(resultStruct, context, expr.structPath, uniqueKeyPossibleUse, joinStack);
1631
- }
1632
- else if (isAsymmetricAggregate) {
1633
- resultStruct.addStructToJoin(context, this, uniqueKeyPossibleUse, joinStack);
1634
- }
1635
- for (const e of expr.args) {
1636
- this.addDependantExpr(resultStruct, context, e, joinStack);
1637
- }
1638
- if ((0, malloy_types_1.expressionIsAnalytic)(expr.overload.returnType.expressionType)) {
1639
- resultStruct.root().queryUsesPartitioning = true;
1640
- }
1641
- if (expr.orderBy) {
1642
- for (const ob of expr.orderBy) {
1643
- if (ob.e) {
1644
- this.addDependantExpr(resultStruct, context, ob.e, joinStack);
1645
- }
1646
- }
1647
- }
1648
1676
  }
1649
1677
  }
1650
1678
  }
@@ -1716,7 +1744,7 @@ class QueryQuery extends QueryField {
1716
1744
  // in the correct catgory.
1717
1745
  for (const cond of resultStruct.firstSegment.filterList || []) {
1718
1746
  const context = this.parent;
1719
- this.addDependantExpr(resultStruct, context, cond.expression, []);
1747
+ this.addDependantExpr(resultStruct, context, cond.e, []);
1720
1748
  }
1721
1749
  for (const join of resultStruct.root().joins.values() || []) {
1722
1750
  for (const qf of join.joinFilterConditions || []) {
@@ -1726,9 +1754,11 @@ class QueryQuery extends QueryField {
1726
1754
  }
1727
1755
  }
1728
1756
  }
1729
- generateSQLFilters(resultStruct, which, filterList = undefined) {
1757
+ generateSQLFilters(resultStruct, which
1758
+ // filterList: FilterCondition[] | undefined = undefined
1759
+ ) {
1730
1760
  const resultFilters = new utils_1.AndChain();
1731
- const list = filterList || resultStruct.firstSegment.filterList;
1761
+ const list = resultStruct.firstSegment.filterList;
1732
1762
  if (list === undefined) {
1733
1763
  return resultFilters;
1734
1764
  }
@@ -1739,7 +1769,7 @@ class QueryQuery extends QueryField {
1739
1769
  const context = this.parent;
1740
1770
  if ((which === 'having' && (0, malloy_types_1.expressionIsCalculation)(cond.expressionType)) ||
1741
1771
  (which === 'where' && (0, malloy_types_1.expressionIsScalar)(cond.expressionType))) {
1742
- const sqlClause = this.generateExpressionFromExpr(resultStruct, context, cond.expression, undefined);
1772
+ const sqlClause = this.exprToSQL(resultStruct, context, cond.e, undefined);
1743
1773
  resultFilters.add(sqlClause);
1744
1774
  }
1745
1775
  }
@@ -2935,9 +2965,7 @@ class QueryStruct extends QueryNode {
2935
2965
  value: param.value === null
2936
2966
  ? null
2937
2967
  : (0, utils_1.exprMap)(param.value, frag => {
2938
- if (typeof frag === 'string')
2939
- return [frag];
2940
- if (frag.type === 'parameter') {
2968
+ if (frag.node === 'parameter') {
2941
2969
  if (this.parent === undefined) {
2942
2970
  throw new Error('No parent from which to retrieve parameter value');
2943
2971
  }
@@ -2950,7 +2978,7 @@ class QueryStruct extends QueryNode {
2950
2978
  return resolved2.value;
2951
2979
  }
2952
2980
  }
2953
- return [frag];
2981
+ return frag;
2954
2982
  }),
2955
2983
  };
2956
2984
  }