@malloydata/malloy 0.0.435 → 0.0.436

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 (76) hide show
  1. package/dist/api/foundation/build_targets.d.ts +5 -6
  2. package/dist/api/foundation/build_targets.js +74 -6
  3. package/dist/api/foundation/core.d.ts +16 -4
  4. package/dist/api/foundation/core.js +23 -23
  5. package/dist/api/foundation/index.d.ts +1 -1
  6. package/dist/api/foundation/runtime.d.ts +21 -2
  7. package/dist/api/foundation/runtime.js +44 -31
  8. package/dist/api/foundation/types.d.ts +43 -3
  9. package/dist/connection/dbmeta.d.ts +29 -0
  10. package/dist/connection/dbmeta.js +46 -0
  11. package/dist/connection/index.d.ts +1 -0
  12. package/dist/connection/index.js +1 -0
  13. package/dist/dialect/databricks/databricks.d.ts +11 -12
  14. package/dist/dialect/databricks/databricks.js +24 -36
  15. package/dist/dialect/dialect.d.ts +56 -170
  16. package/dist/dialect/dialect.js +89 -90
  17. package/dist/dialect/duckdb/duckdb.d.ts +2 -2
  18. package/dist/dialect/duckdb/duckdb.js +1 -1
  19. package/dist/dialect/index.d.ts +1 -1
  20. package/dist/dialect/index.js +2 -2
  21. package/dist/dialect/mysql/mysql.d.ts +11 -12
  22. package/dist/dialect/mysql/mysql.js +20 -32
  23. package/dist/dialect/pg_impl.d.ts +9 -10
  24. package/dist/dialect/pg_impl.js +27 -73
  25. package/dist/dialect/postgres/postgres.d.ts +4 -4
  26. package/dist/dialect/postgres/postgres.js +4 -12
  27. package/dist/dialect/snowflake/snowflake.d.ts +11 -13
  28. package/dist/dialect/snowflake/snowflake.js +30 -82
  29. package/dist/dialect/sqlserver/sqlserver.d.ts +12 -13
  30. package/dist/dialect/sqlserver/sqlserver.js +46 -47
  31. package/dist/dialect/standardsql/standardsql.d.ts +11 -16
  32. package/dist/dialect/standardsql/standardsql.js +22 -73
  33. package/dist/dialect/trino/trino.d.ts +16 -18
  34. package/dist/dialect/trino/trino.js +127 -181
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +2 -2
  37. package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
  38. package/dist/lang/ast/expressions/boolean-value.js +41 -0
  39. package/dist/lang/ast/expressions/case.js +7 -0
  40. package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
  41. package/dist/lang/ast/expressions/expr-cast.js +5 -0
  42. package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
  43. package/dist/lang/ast/expressions/expr-func.js +13 -2
  44. package/dist/lang/ast/expressions/expr-minus.js +5 -2
  45. package/dist/lang/ast/expressions/expr-null.js +4 -0
  46. package/dist/lang/ast/expressions/pick-when.js +7 -0
  47. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  48. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  49. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  50. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  51. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  52. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  53. package/dist/lang/ast/query-items/field-references.js +23 -0
  54. package/dist/lang/ast/query-properties/filters.js +3 -1
  55. package/dist/lang/ast/statements/import-statement.js +13 -0
  56. package/dist/lang/ast/types/expression-def.js +17 -2
  57. package/dist/lang/ast/types/malloy-element.d.ts +24 -1
  58. package/dist/lang/ast/types/malloy-element.js +63 -26
  59. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  60. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  61. package/dist/lang/composite-source-utils.js +10 -17
  62. package/dist/lang/parse-log.d.ts +12 -0
  63. package/dist/lang/parse-log.js +2 -0
  64. package/dist/lang/test/test-translator.d.ts +2 -1
  65. package/dist/lang/test/test-translator.js +37 -3
  66. package/dist/model/expression_compiler.js +8 -7
  67. package/dist/model/filter_compilers.js +13 -12
  68. package/dist/model/given_binding.d.ts +8 -0
  69. package/dist/model/given_binding.js +13 -0
  70. package/dist/model/index.d.ts +1 -1
  71. package/dist/model/index.js +2 -1
  72. package/dist/model/sql_compiled.d.ts +19 -20
  73. package/dist/model/sql_compiled.js +29 -17
  74. package/dist/version.d.ts +1 -1
  75. package/dist/version.js +1 -1
  76. package/package.json +4 -4
@@ -23,15 +23,6 @@ function timestampMeasureable(units) {
23
23
  'day',
24
24
  ].includes(units);
25
25
  }
26
- /**
27
- * Return a non UTC timezone, if one was specificed.
28
- */
29
- function qtz(qi) {
30
- const tz = qi.queryTimezone;
31
- if (tz && tz !== 'UTC') {
32
- return tz;
33
- }
34
- }
35
26
  const trinoToMalloyTypes = {
36
27
  'varchar': { type: 'string' },
37
28
  'integer': { type: 'number', numberType: 'integer' },
@@ -57,6 +48,33 @@ const trinoToMalloyTypes = {
57
48
  // TIME
58
49
  // GEOGRAPHY
59
50
  };
51
+ function trinoTrunc(expr, unit) {
52
+ // Weeks start on Monday here, so truncate the following day
53
+ if (unit === 'week') {
54
+ return `(DATE_TRUNC('${unit}', (${expr} + INTERVAL '1' DAY)) - INTERVAL '1' DAY)`;
55
+ }
56
+ return `DATE_TRUNC('${unit}', ${expr})`;
57
+ }
58
+ function trinoDateAdd(expr, op, magnitude, unit) {
59
+ // DATE_ADD has no quarter or week
60
+ let offsetUnit = unit;
61
+ let offsetMag = magnitude;
62
+ if (unit === 'quarter') {
63
+ offsetUnit = 'month';
64
+ offsetMag = `${magnitude}*3`;
65
+ }
66
+ else if (unit === 'week') {
67
+ offsetUnit = 'day';
68
+ offsetMag = `${magnitude}*7`;
69
+ }
70
+ const n = op === '-' ? `(${offsetMag})*-1` : offsetMag;
71
+ return `DATE_ADD('${offsetUnit}', ${n}, ${expr})`;
72
+ }
73
+ function trinoExtract(expr, units) {
74
+ const extracted = `EXTRACT(${pg_impl_1.timeExtractMap[units] || units} FROM ${expr})`;
75
+ // Trino counts the days of a week from 1 on Monday
76
+ return units === 'day_of_week' ? `mod(${extracted}+1,7)` : extracted;
77
+ }
60
78
  class TrinoDialect extends pg_impl_1.PostgresBase {
61
79
  constructor() {
62
80
  super(...arguments);
@@ -339,87 +357,34 @@ ${(0, utils_1.indent)(sql)}
339
357
  const definitions = this.buildTypeExpression(fieldList);
340
358
  return `CAST(ROW(${fields}) as ROW(${definitions}))`;
341
359
  }
360
+ // Civil time is a TIMESTAMP, which Trino keeps without a zone
342
361
  sqlConvertToCivilTime(expr, timezone, typeDef) {
343
- // Trino civil time = TIMESTAMPTZ with query timezone as stored timezone
344
- // Operations (extract, truncate, etc.) happen in the stored timezone
345
- if (typeDef.type === 'timestamp') {
346
- // TIMESTAMP (UTC wall clock) → TIMESTAMPTZ in query timezone
347
- return {
348
- sql: `at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(timezone)})`,
349
- typeDef: { type: 'timestamptz' },
350
- };
362
+ const tz = this.sqlTimezoneLiteral(timezone);
363
+ if (malloy_types_1.TD.isTimestamptz(typeDef)) {
364
+ return `CAST(at_timezone(${expr}, ${tz}) AS TIMESTAMP)`;
351
365
  }
352
- // TIMESTAMPTZ → TIMESTAMPTZ in query timezone (same instant, different stored tz)
353
- return {
354
- sql: `at_timezone(${expr}, ${this.sqlTimezoneLiteral(timezone)})`,
355
- typeDef: { type: 'timestamptz' },
356
- };
366
+ return timezone === 'UTC'
367
+ ? expr
368
+ : `CAST(at_timezone(with_timezone(${expr}, 'UTC'), ${tz}) AS TIMESTAMP)`;
357
369
  }
358
- sqlConvertFromCivilTime(expr, _timezone, destTypeDef) {
359
- // From civil TIMESTAMPTZ (in query timezone) to destination type
360
- if (destTypeDef.type === 'timestamptz') {
361
- // Already TIMESTAMPTZ, keep as-is
362
- return expr;
363
- }
364
- // To TIMESTAMP: convert to UTC and cast to plain TIMESTAMP
365
- return `CAST(at_timezone(${expr}, 'UTC') AS TIMESTAMP)`;
366
- }
367
- sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
368
- // Trino starts weeks on Monday, Malloy wants Sunday
369
- // Add 1 day before truncating, subtract 1 day after
370
- if (unit === 'week') {
371
- return `(DATE_TRUNC('${unit}', (${expr} + INTERVAL '1' DAY)) - INTERVAL '1' DAY)`;
370
+ sqlConvertFromCivilTime(expr, timezone, destTypeDef) {
371
+ const tz = this.sqlTimezoneLiteral(timezone);
372
+ if (malloy_types_1.TD.isTimestamptz(destTypeDef)) {
373
+ return `with_timezone(${expr}, ${tz})`;
372
374
  }
373
- return `DATE_TRUNC('${unit}', ${expr})`;
374
- }
375
- sqlOffsetTime(expr, op, magnitude, unit, _typeDef, _inCivilTime, _timezone) {
376
- // Convert quarter/week to supported units
377
- let offsetUnit = unit;
378
- let offsetMag = magnitude;
379
- if (unit === 'quarter') {
380
- offsetUnit = 'month';
381
- offsetMag = `${magnitude}*3`;
382
- }
383
- else if (unit === 'week') {
384
- offsetUnit = 'day';
385
- offsetMag = `${magnitude}*7`;
386
- }
387
- // Handle subtraction by negating
388
- const n = op === '-' ? `(${offsetMag})*-1` : offsetMag;
389
- return `DATE_ADD('${offsetUnit}', ${n}, ${expr})`;
375
+ return timezone === 'UTC'
376
+ ? expr
377
+ : `CAST(at_timezone(with_timezone(${expr}, ${tz}), 'UTC') AS TIMESTAMP)`;
378
+ }
379
+ sqlTruncate(expr, unit, _civilType) {
380
+ return trinoTrunc(expr, unit);
390
381
  }
391
- sqlCast(qi, cast) {
382
+ sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
383
+ return trinoDateAdd(expr, op, magnitude, unit);
384
+ }
385
+ sqlCast(_qi, cast) {
392
386
  const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
393
- const tz = qtz(qi);
394
387
  const expr = cast.e.sql || '';
395
- // Timezone-aware casts when query timezone is set
396
- if (tz && srcTypeDef && dstTypeDef) {
397
- // TIMESTAMP → DATE: interpret as UTC, convert to query timezone
398
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
399
- return `CAST(at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
400
- }
401
- // TIMESTAMPTZ → DATE: convert to query timezone
402
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
403
- return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
404
- }
405
- // DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
406
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
407
- return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
408
- }
409
- // DATE → TIMESTAMPTZ: interpret date in query timezone
410
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
411
- return `with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)})`;
412
- }
413
- // TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
414
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
415
- return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS TIMESTAMP)`;
416
- }
417
- // TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as being in query timezone
418
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
419
- return `with_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)})`;
420
- }
421
- }
422
- // No special handling needed, or no query timezone
423
388
  if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
424
389
  const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
425
390
  return `${castFunc}(${expr} AS ${dstSQLType})`;
@@ -567,37 +532,12 @@ ${(0, utils_1.indent)(sql)}
567
532
  sqlDateLiteral(_qi, literal) {
568
533
  return `DATE '${literal}'`;
569
534
  }
570
- sqlTimestampLiteral(qi, literal, timezone) {
571
- const tz = timezone || qtz(qi);
572
- if (tz) {
573
- // Interpret wall clock time in timezone, convert to UTC wall clock, cast to TIMESTAMP
574
- return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
575
- }
576
- return `TIMESTAMP '${literal}'`;
577
- }
578
535
  sqlTimestamptzLiteral(_qi, literal, timezone) {
579
536
  // Use with_timezone to create a TIMESTAMP WITH TIME ZONE
580
537
  return `with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(timezone)})`;
581
538
  }
582
- sqlTimeExtractExpr(qi, from) {
583
- const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
584
- let extractFrom = from.e.sql || '';
585
- if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
586
- const tz = qtz(qi);
587
- if (tz) {
588
- // Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
589
- if (from.e.typeDef.type === 'timestamp') {
590
- // TIMESTAMP: interpret as UTC, convert to query timezone
591
- extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'), ${this.sqlTimezoneLiteral(tz)})`;
592
- }
593
- else {
594
- // TIMESTAMPTZ: convert to query timezone
595
- extractFrom = `at_timezone(${extractFrom}, ${this.sqlTimezoneLiteral(tz)})`;
596
- }
597
- }
598
- }
599
- const extracted = `EXTRACT(${pgUnits} FROM ${extractFrom})`;
600
- return from.units === 'day_of_week' ? `mod(${extracted}+1,7)` : extracted;
539
+ sqlCivilExtract(expr, units, _civilType) {
540
+ return trinoExtract(expr, units);
601
541
  }
602
542
  sqlLiteralRecord(lit) {
603
543
  var _a, _b;
@@ -630,91 +570,97 @@ class PrestoDialect extends TrinoDialect {
630
570
  return `DATE '${literal}'`;
631
571
  }
632
572
  sqlTimestampLiteral(qi, literal, timezone) {
633
- const tz = timezone || qtz(qi);
634
- if (tz) {
635
- return `CAST(TIMESTAMP '${literal} ${this.checkedTimezoneName(tz)}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
573
+ const tz = timezone !== null && timezone !== void 0 ? timezone : (0, dialect_1.queryZone)(qi);
574
+ if (tz === 'UTC') {
575
+ return `TIMESTAMP '${literal}'`;
636
576
  }
637
- return `TIMESTAMP '${literal}'`;
577
+ return `CAST(TIMESTAMP '${literal} ${this.checkedTimezoneName(tz)}' AS TIMESTAMP)`;
638
578
  }
639
579
  sqlTimestamptzLiteral(_qi, literal, timezone) {
640
580
  return `TIMESTAMP '${literal} ${this.checkedTimezoneName(timezone)}'`;
641
581
  }
642
- sqlConvertToCivilTime(expr, timezone, _typeDef) {
643
- // Presto's AT TIME ZONE operator (not function) produces TIMESTAMPTZ
644
- // Reinterprets the instant in the target timezone
645
- return {
646
- sql: `${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
647
- typeDef: { type: 'timestamptz' },
648
- };
649
- }
650
- sqlConvertFromCivilTime(expr, _timezone, destTypeDef) {
651
- if (destTypeDef.type === 'timestamptz') {
582
+ /*
583
+ * Presto cannot meet the civil contract: its only way to read a date and
584
+ * time of day as an instant in a zone is to parse a string, which fails
585
+ * on a time the zone skips. So Presto never converts to civil time. It
586
+ * places an instant in the zone as a TIMESTAMP WITH TIME ZONE, whose
587
+ * functions read the clock in that zone and resolve any skipped or
588
+ * repeated time themselves. A Presto TIMESTAMP is an instant shown in the
589
+ * session zone, which is UTC.
590
+ */
591
+ sqlConvertToCivilTime() {
592
+ throw new Error('Presto has no civil time');
593
+ }
594
+ sqlConvertFromCivilTime() {
595
+ throw new Error('Presto has no civil time');
596
+ }
597
+ /** The instant `expr`, showing the clock of `timezone` */
598
+ inZone(expr, typeDef, timezone) {
599
+ if (malloy_types_1.TD.isTimestamp(typeDef) && timezone === 'UTC') {
652
600
  return expr;
653
601
  }
654
- return `CAST(${expr} AT TIME ZONE 'UTC' AS TIMESTAMP)`;
602
+ return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`;
655
603
  }
656
- sqlCast(qi, cast) {
657
- const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
658
- const tz = qtz(qi);
659
- const expr = cast.e.sql || '';
660
- // Timezone-aware casts when query timezone is set
661
- // Presto uses AT TIME ZONE operator instead of Trino's with_timezone/at_timezone functions
662
- if (tz && srcTypeDef && dstTypeDef) {
663
- // TIMESTAMP → DATE: interpret as UTC, convert to query timezone
664
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
665
- return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
666
- }
667
- // TIMESTAMPTZ → DATE: convert to query timezone
668
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
669
- return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
670
- }
671
- // DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
672
- // Presto doesn't have a way to interpret TIMESTAMP in a non-UTC timezone,
673
- // so we build a TIMESTAMPTZ literal string and cast it
674
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
675
- const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
676
- return `CAST(${tstzLiteral} AS TIMESTAMP)`;
677
- }
678
- // DATE → TIMESTAMPTZ: interpret date in query timezone
679
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
680
- return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
681
- }
682
- // TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
683
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
684
- return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
685
- }
686
- // TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as UTC
687
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
688
- return `${expr} AT TIME ZONE 'UTC'`;
689
- }
690
- }
691
- // No special handling needed, or no query timezone
692
- if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
693
- const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
694
- return `${castFunc}(${expr} AS ${dstSQLType})`;
604
+ /** An instant from inZone, as a value of `typeDef` */
605
+ fromZone(expr, typeDef, timezone) {
606
+ if (malloy_types_1.TD.isTimestamp(typeDef) && timezone !== 'UTC') {
607
+ return `CAST(${expr} AS TIMESTAMP)`;
695
608
  }
696
609
  return expr;
697
610
  }
698
611
  sqlTimeExtractExpr(qi, from) {
699
- const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
700
- let extractFrom = from.e.sql || '';
701
- if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
702
- const tz = qtz(qi);
703
- if (tz) {
704
- // Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
705
- if (from.e.typeDef.type === 'timestamp') {
706
- // TIMESTAMP: interpret as UTC, convert to query timezone
707
- // Presto uses AT TIME ZONE operator
708
- extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
709
- }
710
- else {
711
- // TIMESTAMPTZ: convert to query timezone
712
- extractFrom = `${extractFrom} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
713
- }
612
+ const time = from.e;
613
+ const expr = malloy_types_1.TD.isAnyTimestamp(time.typeDef)
614
+ ? this.inZone(time.sql, time.typeDef, (0, dialect_1.queryZone)(qi))
615
+ : time.sql;
616
+ return trinoExtract(expr, from.units);
617
+ }
618
+ sqlTruncAndOffset(baseExpr, qi, truncateTo, offset) {
619
+ const timeType = baseExpr.typeDef;
620
+ // Adding hours, minutes or seconds to an instant needs no zone
621
+ const usesQueryZone = malloy_types_1.TD.isAnyTimestamp(timeType) &&
622
+ (truncateTo !== undefined ||
623
+ (offset !== undefined && (0, malloy_types_1.isDateUnit)(offset.unit)));
624
+ const zone = (0, dialect_1.queryZone)(qi);
625
+ let sql = usesQueryZone
626
+ ? this.inZone(baseExpr.sql, timeType, zone)
627
+ : baseExpr.sql;
628
+ if (truncateTo) {
629
+ sql = trinoTrunc(sql, truncateTo);
630
+ }
631
+ if (offset) {
632
+ sql = trinoDateAdd(sql, offset.op, offset.magnitude, offset.unit);
633
+ }
634
+ return usesQueryZone ? this.fromZone(sql, timeType, zone) : sql;
635
+ }
636
+ sqlTimeCast(qi, cast) {
637
+ var _a;
638
+ const { srcTypeDef, dstTypeDef } = this.sqlCastPrep(cast);
639
+ const expr = (_a = cast.e.sql) !== null && _a !== void 0 ? _a : '';
640
+ const zone = (0, dialect_1.queryZone)(qi);
641
+ if (malloy_types_1.TD.isAnyTimestamp(srcTypeDef)) {
642
+ if (malloy_types_1.TD.isDate(dstTypeDef)) {
643
+ return `CAST(${this.inZone(expr, srcTypeDef, zone)} AS DATE)`;
644
+ }
645
+ if (malloy_types_1.TD.isTimestamp(dstTypeDef) && malloy_types_1.TD.isTimestamptz(srcTypeDef)) {
646
+ return `CAST(${expr} AS TIMESTAMP)`;
647
+ }
648
+ if (malloy_types_1.TD.isTimestamptz(dstTypeDef) && malloy_types_1.TD.isTimestamp(srcTypeDef)) {
649
+ return `(${expr}) AT TIME ZONE 'UTC'`;
650
+ }
651
+ }
652
+ if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isAnyTimestamp(dstTypeDef)) {
653
+ if (zone === 'UTC') {
654
+ const midnight = `CAST(${expr} AS TIMESTAMP)`;
655
+ return malloy_types_1.TD.isTimestamp(dstTypeDef)
656
+ ? midnight
657
+ : `(${midnight}) AT TIME ZONE 'UTC'`;
714
658
  }
659
+ // Fails in a zone which skips the midnight starting the date
660
+ const midnight = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(zone)}' AS TIMESTAMP WITH TIME ZONE)`;
661
+ return this.fromZone(midnight, dstTypeDef, zone);
715
662
  }
716
- const extracted = `EXTRACT(${pgUnits} FROM ${extractFrom})`;
717
- return from.units === 'day_of_week' ? `mod(${extracted}+1,7)` : extracted;
663
+ return undefined;
718
664
  }
719
665
  sqlUnnestAlias(source, alias, _fieldList, needDistinctKey, isArray, _isInNestedPipeline) {
720
666
  if (isArray) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, SQLServerDialect, registerDialect, getDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, decodeDottedTablePath, validateDottedTablePath, } from './dialect';
1
+ export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, SQLServerDialect, registerDialect, getDialect, arg, queryZone, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, decodeDottedTablePath, validateDottedTablePath, } from './dialect';
2
2
  export type { DialectFieldList, DialectFunctionOverloadDef, QueryInfo, MalloyStandardFunctionImplementations, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, DecodeDottedTablePathResult, DottedTablePathOptions, TablePathEscapeStyle, TablePathSegment, ValidateTablePathResult, } from './dialect';
3
3
  export type { QueryRecord, StructDef, TableSourceDef, SQLSourceDef, SourceDef, JoinFieldDef, NamedSourceDefs, MalloyQueryData, DateUnit, ExtractUnit, TimestampUnit, TemporalFieldType, QueryData, QueryValue, Expr, FilterCondition, Argument, Parameter, FieldDef, PipeSegment, QueryFieldDef, IndexFieldDef, TurtleDef, SearchValueMapResult, SearchIndexResult, ModelDef, Query, QueryResult, QueryResultDef, QueryRunStats, QueryScalar, NamedQueryDef, NamedModelObject, ExpressionType, FunctionDef, FunctionOverloadDef, FunctionParameterDef, ExpressionValueType, TypeDesc, FunctionParamTypeDesc, DocumentLocation, DocumentRange, DocumentPosition, Sampling, BasicAtomicTypeDef, BasicAtomicDef, AtomicTypeDef, AtomicFieldDef, ArrayDef, ArrayTypeDef, RecordTypeDef, RepeatedRecordTypeDef, RecordDef, RepeatedRecordDef, RecordLiteralNode, StringLiteralNode, ArrayLiteralNode, SourceComponentInfo, DateLiteralNode, TimestampLiteralNode, TimestamptzLiteralNode, TimeLiteralExpr, TypecastExpr, BuildID, BuildManifest, BuildManifestEntry, GivenValue, VirtualMap, } from './model';
4
4
  export { isSourceDef, isAtomic, isBasicAtomic, isCompoundArrayData, isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, isRepeatedRecord, isBasicArray, mkArrayDef, mkFieldDef, expressionIsAggregate, expressionIsAnalytic, expressionIsCalculation, expressionIsScalar, expressionIsUngroupedAggregate, indent, composeSQLExpr, isTimestampUnit, isDateUnit, constantExprToSQL, } from './model';
@@ -25,7 +25,7 @@ export { routeOf, payloadOf, annotationsForRoute, tagFromAnnotations, } from './
25
25
  /** @deprecated — use the `.annotations` view on a Foundation entity
26
26
  * (`entity.annotations.parseAsTag(route)` / `.texts(route)`). */
27
27
  export { annotationToTag, annotationToTaglines, } from './api/foundation/annotation';
28
- export type { BuildGraph, BuildNode, BuildPlan, BuildTarget, BuildTargets, ConnectionBuild, } from './api/foundation';
28
+ export type { BuildGraph, BuildNode, BuildPlan, BuildTarget, BuildTargetOptions, BuildTargets, ConnectionBuild, UnbuildableSource, } from './api/foundation';
29
29
  export { PersistSource, EMPTY_BUILD_MANIFEST } from './api/foundation';
30
30
  export { Reference } from './api/foundation';
31
31
  export type { ReferenceKind } from './api/foundation';
package/dist/index.js CHANGED
@@ -37,7 +37,7 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  };
38
38
  })();
39
39
  Object.defineProperty(exports, "__esModule", { value: true });
40
- exports.malloyToQuery = exports.constantExprToSQL = exports.isDateUnit = exports.isTimestampUnit = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isBasicArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isCompoundArrayData = exports.isBasicAtomic = exports.isAtomic = exports.isSourceDef = exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.qtz = exports.arg = exports.getDialect = exports.registerDialect = exports.SQLServerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
40
+ exports.malloyToQuery = exports.constantExprToSQL = exports.isDateUnit = exports.isTimestampUnit = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isBasicArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isCompoundArrayData = exports.isBasicAtomic = exports.isAtomic = exports.isSourceDef = exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.queryZone = exports.arg = exports.getDialect = exports.registerDialect = exports.SQLServerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
41
41
  exports.PersistSource = exports.annotationToTaglines = exports.annotationToTag = exports.tagFromAnnotations = exports.annotationsForRoute = exports.payloadOf = exports.routeOf = exports.RoutedNote = exports.Annotations = exports.sqlKey = exports.API = exports.sourceDefToSourceInfo = exports.modelDefToModelInfo = exports.toAsyncGenerator = exports.createConnectionsFromConfig = exports.getRegisteredConnectionTypes = exports.getConnectionTypeDisplayName = exports.getConnectionProperties = exports.registerConnectionType = exports.discoverConfig = exports.defaultConfigOverlays = exports.contextOverlay = exports.envOverlay = exports.MalloyConfig = exports.Manifest = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.AtomicFieldType = exports.Runtime = exports.Malloy = exports.Model = exports.MalloyTranslator = void 0;
42
42
  exports.makeDigest = exports.Reference = exports.EMPTY_BUILD_MANIFEST = void 0;
43
43
  var dialect_1 = require("./dialect");
@@ -52,7 +52,7 @@ Object.defineProperty(exports, "SQLServerDialect", { enumerable: true, get: func
52
52
  Object.defineProperty(exports, "registerDialect", { enumerable: true, get: function () { return dialect_1.registerDialect; } });
53
53
  Object.defineProperty(exports, "getDialect", { enumerable: true, get: function () { return dialect_1.getDialect; } });
54
54
  Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return dialect_1.arg; } });
55
- Object.defineProperty(exports, "qtz", { enumerable: true, get: function () { return dialect_1.qtz; } });
55
+ Object.defineProperty(exports, "queryZone", { enumerable: true, get: function () { return dialect_1.queryZone; } });
56
56
  Object.defineProperty(exports, "overload", { enumerable: true, get: function () { return dialect_1.overload; } });
57
57
  Object.defineProperty(exports, "minScalar", { enumerable: true, get: function () { return dialect_1.minScalar; } });
58
58
  Object.defineProperty(exports, "anyExprType", { enumerable: true, get: function () { return dialect_1.anyExprType; } });
@@ -0,0 +1,24 @@
1
+ import type { ExprValue } from '../types/expr-value';
2
+ import type { FieldSpace } from '../types/field-space';
3
+ import type { MalloyElement } from '../types/malloy-element';
4
+ /**
5
+ * On a dialect with booleanType 'none' a boolean is a condition and never a
6
+ * value. When `type` is a boolean about to be used as a value, logs an error
7
+ * at `logTo` and returns an error-typed value for the caller to return in
8
+ * place of what it was building, so the enclosing expressions see an error
9
+ * and stay quiet. Otherwise returns undefined. `where` says how the boolean
10
+ * was used, `hint` what to write instead.
11
+ */
12
+ export declare function booleanValueError(fs: FieldSpace, type: {
13
+ type: string;
14
+ } | undefined, where: string, logTo: MalloyElement, hint: string): ExprValue | undefined;
15
+ /** The hint where a boolean's unknown state is examined: `is null`, `??` */
16
+ export declare const CONDITION_HINT = "test the condition itself; unknown already tests as false";
17
+ /** The hint for a boolean compared with anything */
18
+ export declare const COMPARE_HINT = "test the condition itself, or its not";
19
+ /** The hint where a number would do: a function argument, a cast */
20
+ export declare const NUMBER_HINT = "write the value, pick 1 when \u2026 else 0";
21
+ /** The hint for a boolean as a result column, from the source text it came from */
22
+ export declare function resultColumnHint(src: string): string;
23
+ /** The hint for a `pick` or `case` whose result is a boolean */
24
+ export declare const CHOICE_HINT = "write values, pick 'yes' when \u2026 else 'no', or the condition itself with and, or and not";
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CHOICE_HINT = exports.NUMBER_HINT = exports.COMPARE_HINT = exports.CONDITION_HINT = void 0;
8
+ exports.booleanValueError = booleanValueError;
9
+ exports.resultColumnHint = resultColumnHint;
10
+ /**
11
+ * On a dialect with booleanType 'none' a boolean is a condition and never a
12
+ * value. When `type` is a boolean about to be used as a value, logs an error
13
+ * at `logTo` and returns an error-typed value for the caller to return in
14
+ * place of what it was building, so the enclosing expressions see an error
15
+ * and stay quiet. Otherwise returns undefined. `where` says how the boolean
16
+ * was used, `hint` what to write instead.
17
+ */
18
+ function booleanValueError(fs, type, where, logTo, hint) {
19
+ const dialect = fs.dialectObj();
20
+ if ((type === null || type === void 0 ? void 0 : type.type) !== 'boolean' || (dialect === null || dialect === void 0 ? void 0 : dialect.booleanType) !== 'none') {
21
+ return undefined;
22
+ }
23
+ return logTo.loggedErrorExpr('boolean-value-unsupported', {
24
+ dialect: dialect.name,
25
+ where,
26
+ hint,
27
+ });
28
+ }
29
+ /** The hint where a boolean's unknown state is examined: `is null`, `??` */
30
+ exports.CONDITION_HINT = 'test the condition itself; unknown already tests as false';
31
+ /** The hint for a boolean compared with anything */
32
+ exports.COMPARE_HINT = 'test the condition itself, or its not';
33
+ /** The hint where a number would do: a function argument, a cast */
34
+ exports.NUMBER_HINT = 'write the value, pick 1 when … else 0';
35
+ /** The hint for a boolean as a result column, from the source text it came from */
36
+ function resultColumnHint(src) {
37
+ return `write the value, pick 'yes' when ${src} else 'no'`;
38
+ }
39
+ /** The hint for a `pick` or `case` whose result is a boolean */
40
+ exports.CHOICE_HINT = "write values, pick 'yes' when … else 'no', or the condition itself with and, or and not";
41
+ //# sourceMappingURL=boolean-value.js.map
@@ -42,6 +42,7 @@ const expr_value_1 = require("../types/expr-value");
42
42
  const expression_def_1 = require("../types/expression-def");
43
43
  const malloy_element_1 = require("../types/malloy-element");
44
44
  const TDU = __importStar(require("../typedesc-utils"));
45
+ const boolean_value_1 = require("./boolean-value");
45
46
  function typeCoalesce(ev1, ev2) {
46
47
  return ev1 === undefined || ev1.type === 'null' || ev1.type === 'error'
47
48
  ? ev2
@@ -68,6 +69,9 @@ class Case extends expression_def_1.ExpressionDef {
68
69
  let value = undefined;
69
70
  if (this.value) {
70
71
  const v = this.value.getExpression(fs);
72
+ const noSubject = (0, boolean_value_1.booleanValueError)(fs, v, 'as the subject of a case', this.value, 'write the conditions, case when … then …');
73
+ if (noSubject)
74
+ return noSubject;
71
75
  dependents.push(v);
72
76
  resultExpr.kids.caseValue = v.value;
73
77
  value = v;
@@ -118,6 +122,9 @@ class Case extends expression_def_1.ExpressionDef {
118
122
  dependents.push(elseValue);
119
123
  resultExpr.kids.caseElse = elseValue.value;
120
124
  }
125
+ const noCase = (0, boolean_value_1.booleanValueError)(fs, returnType, 'as the result of a case', this, boolean_value_1.CHOICE_HINT);
126
+ if (noCase)
127
+ return noCase;
121
128
  return (0, expr_value_1.computedExprValue)({
122
129
  value: resultExpr,
123
130
  dataType: returnType ? TDU.atomicDef(returnType) : { type: 'null' },
@@ -8,6 +8,7 @@ exports.ExprAlternationTree = void 0;
8
8
  const expr_value_1 = require("../types/expr-value");
9
9
  const expression_def_1 = require("../types/expression-def");
10
10
  const binary_operators_1 = require("../types/binary_operators");
11
+ const boolean_value_1 = require("./boolean-value");
11
12
  /**
12
13
  * Return a flattened version of an alternation tree, if the tree is
13
14
  * composed entirely values and "or"s.
@@ -58,6 +59,16 @@ class ExprAlternationTree extends expression_def_1.ExpressionDef {
58
59
  if (inList.length > 0 && (applyOp === '=' || applyOp === '!=')) {
59
60
  const isIn = expr.getExpression(fs);
60
61
  const values = inList.map(v => v.getExpression(fs));
62
+ // A boolean on either side of the comparison, the tested value or
63
+ // any of the choices, is a boolean compared with something.
64
+ for (const [el, val] of [
65
+ [expr, isIn],
66
+ ...inList.map((el, i) => [el, values[i]]),
67
+ ]) {
68
+ const noIn = (0, boolean_value_1.booleanValueError)(fs, val, `compared with ${applyOp}`, el, boolean_value_1.COMPARE_HINT);
69
+ if (noIn)
70
+ return noIn;
71
+ }
61
72
  return (0, expr_value_1.computedExprValue)({
62
73
  dataType: { type: 'boolean' },
63
74
  value: {
@@ -8,6 +8,7 @@ exports.ExprCast = void 0;
8
8
  const time_utils_1 = require("../time-utils");
9
9
  const expr_value_1 = require("../types/expr-value");
10
10
  const expression_def_1 = require("../types/expression-def");
11
+ const boolean_value_1 = require("./boolean-value");
11
12
  class ExprCast extends expression_def_1.ExpressionDef {
12
13
  constructor(expr, castType, safe = false) {
13
14
  super({ expr: expr });
@@ -38,6 +39,10 @@ class ExprCast extends expression_def_1.ExpressionDef {
38
39
  }
39
40
  }
40
41
  }
42
+ const noCast = (0, boolean_value_1.booleanValueError)(fs, expr, 'in a cast', this, boolean_value_1.NUMBER_HINT) ||
43
+ (0, boolean_value_1.booleanValueError)(fs, dataType, 'as the type of a cast', this, 'write a comparison');
44
+ if (noCast)
45
+ return noCast;
41
46
  return (0, expr_value_1.computedExprValue)({
42
47
  dataType,
43
48
  value: (0, time_utils_1.castTo)(this.castType, expr.value, expr.type, this.safe),
@@ -42,6 +42,7 @@ const model_1 = require("../../../model");
42
42
  const composite_source_utils_1 = require("../../composite-source-utils");
43
43
  const TDU = __importStar(require("../typedesc-utils"));
44
44
  const expression_def_1 = require("../types/expression-def");
45
+ const boolean_value_1 = require("./boolean-value");
45
46
  class ExprCoalesce extends expression_def_1.ExpressionDef {
46
47
  constructor(expr, altExpr) {
47
48
  super({ expr, altExpr });
@@ -53,6 +54,9 @@ class ExprCoalesce extends expression_def_1.ExpressionDef {
53
54
  computeExpression(fs) {
54
55
  const maybeNull = this.expr.getExpression(fs);
55
56
  const whenNull = this.altExpr.getExpression(fs);
57
+ const noCoalesce = (0, boolean_value_1.booleanValueError)(fs, maybeNull, 'in ??', this, boolean_value_1.CONDITION_HINT);
58
+ if (noCoalesce)
59
+ return noCoalesce;
56
60
  if (maybeNull.type === 'null') {
57
61
  return whenNull;
58
62
  }
@@ -48,6 +48,7 @@ const field_space_1 = require("../types/field-space");
48
48
  const utils_2 = require("../../../model/utils");
49
49
  const TDU = __importStar(require("../typedesc-utils"));
50
50
  const composite_source_utils_1 = require("../../composite-source-utils");
51
+ const boolean_value_1 = require("./boolean-value");
51
52
  // Built-in functions that take a string literal of user-supplied SQL
52
53
  // and emit it directly. Gated by experimental.sql_functions in plain
53
54
  // Malloy; rejected unconditionally in restricted queries because they
@@ -111,9 +112,14 @@ class ExprFunc extends expression_def_1.ExpressionDef {
111
112
  return { found: func, error: undefined };
112
113
  }
113
114
  getPropsExpression(fs, props) {
114
- var _a, _b, _c, _d, _e;
115
+ var _a, _b, _c, _d, _e, _f;
115
116
  const argExprsWithoutImplicit = this.args.map(arg => arg.getExpression(fs));
116
117
  if (this.isRaw) {
118
+ for (const [i, argExpr] of argExprsWithoutImplicit.entries()) {
119
+ const noArg = (0, boolean_value_1.booleanValueError)(fs, argExpr, `as an argument of ${this.name}`, this.args[i], boolean_value_1.NUMBER_HINT);
120
+ if (noArg)
121
+ return noArg;
122
+ }
117
123
  const funcCall = [`${this.name}(`];
118
124
  argExprsWithoutImplicit.forEach((expr, i) => {
119
125
  if (i !== 0) {
@@ -182,6 +188,11 @@ class ExprFunc extends expression_def_1.ExpressionDef {
182
188
  .join(', ')})`);
183
189
  }
184
190
  const { overload, expressionTypeErrors, evalSpaceErrors, nullabilityErrors, returnType, } = result;
191
+ for (const [i, argExpr] of argExprs.entries()) {
192
+ const noArg = (0, boolean_value_1.booleanValueError)(fs, argExpr, `as an argument of ${this.name}`, (_e = this.args[i - (implicitExpr ? 1 : 0)]) !== null && _e !== void 0 ? _e : this, boolean_value_1.NUMBER_HINT);
193
+ if (noArg)
194
+ return noArg;
195
+ }
185
196
  // Report errors for expression type mismatch
186
197
  for (const error of expressionTypeErrors) {
187
198
  const adjustedIndex = error.argIndex - (implicitExpr ? 1 : 0);
@@ -214,7 +225,7 @@ class ExprFunc extends expression_def_1.ExpressionDef {
214
225
  }
215
226
  const type = overload.returnType;
216
227
  const expressionType = (0, malloy_types_1.maxOfExpressionTypes)([
217
- (_e = type.expressionType) !== null && _e !== void 0 ? _e : 'scalar',
228
+ (_f = type.expressionType) !== null && _f !== void 0 ? _f : 'scalar',
218
229
  ...argExprs.map(e => e.expressionType),
219
230
  ]);
220
231
  if (!(0, malloy_types_1.expressionIsAggregate)(overload.returnType.expressionType) &&