@malloydata/malloy 0.0.434 → 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 (103) 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/compile.d.ts +0 -1
  4. package/dist/api/foundation/compile.js +0 -3
  5. package/dist/api/foundation/core.d.ts +16 -4
  6. package/dist/api/foundation/core.js +23 -23
  7. package/dist/api/foundation/index.d.ts +1 -1
  8. package/dist/api/foundation/runtime.d.ts +21 -3
  9. package/dist/api/foundation/runtime.js +45 -54
  10. package/dist/api/foundation/types.d.ts +43 -4
  11. package/dist/connection/dbmeta.d.ts +29 -0
  12. package/dist/connection/dbmeta.js +46 -0
  13. package/dist/connection/index.d.ts +1 -0
  14. package/dist/connection/index.js +1 -0
  15. package/dist/dialect/databricks/databricks.d.ts +11 -12
  16. package/dist/dialect/databricks/databricks.js +24 -36
  17. package/dist/dialect/dialect.d.ts +70 -171
  18. package/dist/dialect/dialect.js +123 -92
  19. package/dist/dialect/dialect_map.js +2 -0
  20. package/dist/dialect/duckdb/duckdb.d.ts +2 -2
  21. package/dist/dialect/duckdb/duckdb.js +1 -1
  22. package/dist/dialect/index.d.ts +3 -2
  23. package/dist/dialect/index.js +4 -2
  24. package/dist/dialect/mysql/mysql.d.ts +11 -12
  25. package/dist/dialect/mysql/mysql.js +20 -32
  26. package/dist/dialect/pg_impl.d.ts +9 -10
  27. package/dist/dialect/pg_impl.js +27 -73
  28. package/dist/dialect/postgres/postgres.d.ts +4 -4
  29. package/dist/dialect/postgres/postgres.js +4 -12
  30. package/dist/dialect/snowflake/snowflake.d.ts +11 -13
  31. package/dist/dialect/snowflake/snowflake.js +30 -82
  32. package/dist/dialect/sqlserver/dialect_functions.d.ts +2 -0
  33. package/dist/dialect/sqlserver/dialect_functions.js +42 -0
  34. package/dist/dialect/sqlserver/function_overrides.d.ts +2 -0
  35. package/dist/dialect/sqlserver/function_overrides.js +55 -0
  36. package/dist/dialect/sqlserver/index.d.ts +1 -0
  37. package/dist/dialect/sqlserver/index.js +22 -0
  38. package/dist/dialect/sqlserver/sqlserver.d.ts +101 -0
  39. package/dist/dialect/sqlserver/sqlserver.js +537 -0
  40. package/dist/dialect/sqlserver/windows_zones.d.ts +1 -0
  41. package/dist/dialect/sqlserver/windows_zones.js +613 -0
  42. package/dist/dialect/standardsql/standardsql.d.ts +11 -16
  43. package/dist/dialect/standardsql/standardsql.js +22 -73
  44. package/dist/dialect/trino/trino.d.ts +16 -18
  45. package/dist/dialect/trino/trino.js +127 -181
  46. package/dist/index.d.ts +2 -2
  47. package/dist/index.js +6 -4
  48. package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
  49. package/dist/lang/ast/expressions/boolean-value.js +41 -0
  50. package/dist/lang/ast/expressions/case.js +7 -0
  51. package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
  52. package/dist/lang/ast/expressions/expr-cast.js +5 -0
  53. package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
  54. package/dist/lang/ast/expressions/expr-func.js +13 -2
  55. package/dist/lang/ast/expressions/expr-minus.js +5 -2
  56. package/dist/lang/ast/expressions/expr-null.js +4 -0
  57. package/dist/lang/ast/expressions/pick-when.js +7 -0
  58. package/dist/lang/ast/field-space/index-field-space.js +13 -1
  59. package/dist/lang/ast/field-space/query-spaces.js +11 -0
  60. package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
  61. package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
  62. package/dist/lang/ast/query-items/field-declaration.js +22 -16
  63. package/dist/lang/ast/query-items/field-references.d.ts +6 -0
  64. package/dist/lang/ast/query-items/field-references.js +23 -0
  65. package/dist/lang/ast/query-properties/filters.js +3 -1
  66. package/dist/lang/ast/source-elements/named-source.js +0 -2
  67. package/dist/lang/ast/source-elements/sql-source.js +0 -3
  68. package/dist/lang/ast/source-elements/table-source.js +1 -2
  69. package/dist/lang/ast/statements/import-statement.js +17 -0
  70. package/dist/lang/ast/types/expression-def.js +17 -2
  71. package/dist/lang/ast/types/malloy-element.d.ts +24 -8
  72. package/dist/lang/ast/types/malloy-element.js +63 -44
  73. package/dist/lang/ast/view-elements/reference-view.js +4 -0
  74. package/dist/lang/ast/view-elements/refine-utils.js +1 -0
  75. package/dist/lang/composite-source-utils.js +10 -17
  76. package/dist/lang/parse-log.d.ts +12 -0
  77. package/dist/lang/parse-log.js +2 -0
  78. package/dist/lang/parse-malloy.d.ts +17 -2
  79. package/dist/lang/parse-malloy.js +48 -14
  80. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +3 -3
  81. package/dist/lang/parse-tree-walkers/find-external-references.js +17 -16
  82. package/dist/lang/test/test-translator.d.ts +11 -1
  83. package/dist/lang/test/test-translator.js +51 -3
  84. package/dist/model/expression_compiler.js +9 -8
  85. package/dist/model/filter_compilers.js +13 -12
  86. package/dist/model/given_binding.d.ts +8 -0
  87. package/dist/model/given_binding.js +13 -0
  88. package/dist/model/index.d.ts +1 -1
  89. package/dist/model/index.js +2 -1
  90. package/dist/model/query_query.d.ts +3 -1
  91. package/dist/model/query_query.js +157 -139
  92. package/dist/model/source_def_utils.d.ts +7 -1
  93. package/dist/model/source_def_utils.js +20 -0
  94. package/dist/model/sql_compiled.d.ts +19 -20
  95. package/dist/model/sql_compiled.js +29 -17
  96. package/dist/test/index.js +16 -8
  97. package/dist/test/matchers.d.ts +1 -1
  98. package/dist/test/matchers.js +1 -1
  99. package/dist/test/test-models.js +6 -13
  100. package/dist/version.d.ts +1 -1
  101. package/dist/version.js +1 -1
  102. package/femto-config.motly +13 -1
  103. package/package.json +7 -5
@@ -4,9 +4,9 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.Dialect = exports.dayIndex = exports.EscapeStyle = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
7
+ exports.Dialect = exports.dayIndex = exports.CivilTime = exports.EscapeStyle = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
8
8
  exports.inDays = inDays;
9
- exports.qtz = qtz;
9
+ exports.queryZone = queryZone;
10
10
  exports.turtleGroupSetCondition = turtleGroupSetCondition;
11
11
  const malloy_types_1 = require("../model/malloy_types");
12
12
  const table_path_1 = require("./table-path");
@@ -37,6 +37,14 @@ exports.EscapeStyle = {
37
37
  Backslash: 'backslash',
38
38
  Unset: 'unset',
39
39
  };
40
+ /**
41
+ * The two kinds of civil time: a calendar date, and a date with a time of
42
+ * day. Neither has a zone.
43
+ */
44
+ exports.CivilTime = {
45
+ Date: 'date',
46
+ DateAndTime: 'date_and_time',
47
+ };
40
48
  const allUnits = [
41
49
  'microsecond',
42
50
  'millisecond',
@@ -53,14 +61,10 @@ exports.dayIndex = allUnits.indexOf('day');
53
61
  function inDays(units) {
54
62
  return allUnits.indexOf(units) >= exports.dayIndex;
55
63
  }
56
- // Return the active query timezone, if it different than the
57
- // "native" timezone for timestamps.
58
- function qtz(qi) {
59
- const tz = qi.queryTimezone;
60
- if (tz === undefined || tz === qi.systemTimezone) {
61
- return undefined;
62
- }
63
- return tz;
64
+ /** The query time zone, or UTC when the query has none. */
65
+ function queryZone(qi) {
66
+ var _a;
67
+ return (_a = qi.queryTimezone) !== null && _a !== void 0 ? _a : 'UTC';
64
68
  }
65
69
  /**
66
70
  * The per-element condition for an aggregate turtle (sqlAggregateTurtle). On
@@ -81,6 +85,12 @@ function turtleGroupSetCondition(groupSet, filterSQL) {
81
85
  }
82
86
  class Dialect {
83
87
  constructor() {
88
+ /**
89
+ * What the compiler consults, not `experimental` itself. The test harness
90
+ * calls `acceptExperimental()` on the dialect under test so the shared
91
+ * suites can run on an experimental dialect without every test enabling it.
92
+ */
93
+ this.experimentalAccepted = false;
84
94
  // -- we should add flags with default values from now on so as to not break
85
95
  // dialects outside our repository
86
96
  //
@@ -106,6 +116,9 @@ class Dialect {
106
116
  this.readsNestedData = true;
107
117
  // ORDER BY 1 DESC
108
118
  this.orderByClause = 'ordinal';
119
+ // GROUP BY 1, or GROUP BY the dimension expressions for an engine whose
120
+ // GROUP BY takes no ordinals
121
+ this.groupByClause = 'ordinal';
109
122
  // null will match in a function signature
110
123
  this.nullMatchesFunctionSignature = true;
111
124
  // support select * replace(...)
@@ -147,6 +160,8 @@ class Dialect {
147
160
  this.booleanType = 'supported';
148
161
  // Like characters are escaped with ESCAPE clause
149
162
  this.likeEscape = true;
163
+ // Characters LIKE reads as wildcards beyond % and _, escaped like them
164
+ this.likeExtraWildcards = [];
150
165
  /**
151
166
  * Mappings from integer value ranges to Malloy number types.
152
167
  *
@@ -209,6 +224,12 @@ class Dialect {
209
224
  */
210
225
  this.identifierEscapeStyle = exports.EscapeStyle.Unset;
211
226
  }
227
+ isExperimental() {
228
+ return this.experimental && !this.experimentalAccepted;
229
+ }
230
+ acceptExperimental() {
231
+ this.experimentalAccepted = true;
232
+ }
212
233
  // Generate the lateral join bag clause for window function partitioning.
213
234
  // The expressions are dimension fields that need to be referenced by name
214
235
  // in PARTITION BY clauses. Must be overridden by any dialect that sets
@@ -219,6 +240,14 @@ class Dialect {
219
240
  }
220
241
  throw new Error('Internal error: sqlLateralJoinBag called but cantPartitionWindowFunctionsOnExpressions is false');
221
242
  }
243
+ /** The ordering and row limit which end a SELECT stage */
244
+ sqlOrderByLimit(orderTerms, limit) {
245
+ let s = orderTerms.length > 0 ? this.sqlOrderBy(orderTerms, 'query') + '\n' : '';
246
+ if (limit !== undefined) {
247
+ s += `LIMIT ${limit}\n`;
248
+ }
249
+ return s;
250
+ }
222
251
  /**
223
252
  * Determine the Malloy number type for a numeric literal.
224
253
  */
@@ -406,96 +435,70 @@ class Dialect {
406
435
  ignoreInProject(_fieldName) {
407
436
  return false;
408
437
  }
409
- /**
410
- * Determines whether a truncation and/or offset operation needs to be performed in
411
- * civil (local) time, and if so, which timezone to use.
412
- *
413
- * Calendar-based operations (day, week, month, quarter, year) typically need civil time
414
- * computation because these units can cross DST boundaries, changing the UTC offset.
415
- *
416
- * Default implementation:
417
- * - Returns true for timestamps with truncation or calendar-unit offsets
418
- * - Uses the query timezone from QueryInfo, or 'UTC' if none specified
419
- * - Returns false for dates or sub-day offsets (hour, minute, second)
420
- *
421
- * Dialects can override this if they have different rules about which operations
422
- * require civil time computation.
423
- *
424
- * @param typeDef The Malloy type of the base expression
425
- * @param truncateTo The truncation unit, if any
426
- * @param offsetUnit The offset unit, if any
427
- * @param qi Query information including timezone settings
428
- * @returns Object with `needed` boolean and optional `tz` string
429
- */
430
- needsCivilTimeComputation(typeDef, truncateTo, offsetUnit, qi) {
431
- // Calendar units that can cross DST boundaries
432
- const calendarUnits = ['day', 'week', 'month', 'quarter', 'year'];
433
- const isCalendarTruncate = truncateTo !== undefined && calendarUnits.includes(truncateTo);
434
- const isCalendarOffset = offsetUnit !== undefined && calendarUnits.includes(offsetUnit);
435
- const tz = qtz(qi);
436
- // Timestamps with calendar truncation/offset need civil time computation
437
- // BUT only if there's actually a timezone to convert to/from
438
- const needed = malloy_types_1.TD.isAnyTimestamp(typeDef) &&
439
- (isCalendarTruncate || isCalendarOffset) &&
440
- tz !== undefined;
441
- return { needed, tz };
438
+ sqlTimeExtractExpr(qi, from) {
439
+ const time = from.e;
440
+ if (malloy_types_1.TD.isAnyTimestamp(time.typeDef)) {
441
+ const civil = this.sqlConvertToCivilTime(time.sql, queryZone(qi), time.typeDef);
442
+ return this.sqlCivilExtract(civil, from.units, exports.CivilTime.DateAndTime);
443
+ }
444
+ return this.sqlCivilExtract(time.sql, from.units, exports.CivilTime.Date);
442
445
  }
443
446
  /**
444
- * Unified function for truncation and/or offset operations. This turns out to
445
- * be a very common operation and one which can be optimized by doing it together.
446
- *
447
- * Much of the complexity has to do with getting timezone values set up so that
448
- * they will truncate/offset in the query timezone instead of UTC.
449
- *
450
- * The intention is that this implementation will work for all dialects, and all
451
- * the dialect peculiarities are handled with the new primitives introduced to
452
- * support this function:
453
- * - needsCivilTimeComputation: Determines if operation needs civil time
454
- * - sqlConvertToCivilTime/sqlConvertFromCivilTime: Timezone conversion
455
- * - sqlTruncate: Truncation operation
456
- * - sqlOffsetTime: Interval arithmetic
457
- *
458
- * OFFSET TIMESTAMP BEHAVIOR:
459
- * - Plain timestamps (offset=false): Always return plain TIMESTAMP in UTC
460
- * - Offset timestamps (offset=true):
461
- * - Simple path: Operate in native embedded timezone, return TIMESTAMPTZ
462
- * - Civil path: Convert to query timezone (preserving instant), operate there,
463
- * return TIMESTAMPTZ in query timezone via sqlConvertFromCivilTime
464
- * - BigQuery/MySQL: No offset timestamp support, this logic never runs
465
- *
466
- * @param baseExpr The time expression to operate on (already compiled, with .sql populated)
467
- * @param qi Query information including timezone
468
- * @param truncateTo Optional truncation unit (year, month, day, etc.)
469
- * @param offset Optional offset to apply (after truncation if both present)
447
+ * Truncation and/or offset, the offset applied after the truncation.
448
+ * Doing both in one pass converts to and from civil time only once.
470
449
  */
471
450
  sqlTruncAndOffset(baseExpr, qi, truncateTo, offset) {
472
- // Determine if we need to work in civil (local) time
473
- if (malloy_types_1.TD.isAnyTimestamp(baseExpr.typeDef)) {
474
- const { needed: needsCivil, tz } = this.needsCivilTimeComputation(baseExpr.typeDef, truncateTo, offset === null || offset === void 0 ? void 0 : offset.unit, qi);
475
- if (needsCivil && tz) {
476
- // Civil time path: convert to local time, operate, convert back to UTC
477
- const civilResult = this.sqlConvertToCivilTime(baseExpr.sql, tz, baseExpr.typeDef);
478
- let expr = civilResult.sql;
479
- const civilTypeDef = civilResult.typeDef;
480
- if (truncateTo) {
481
- expr = this.sqlTruncate(expr, truncateTo, civilTypeDef, true, tz);
482
- }
483
- if (offset) {
484
- expr = this.sqlOffsetTime(expr, offset.op, offset.magnitude, offset.unit, civilTypeDef, true, tz);
485
- }
486
- return this.sqlConvertFromCivilTime(expr, tz, baseExpr.typeDef);
487
- }
451
+ const timeType = baseExpr.typeDef;
452
+ if (!malloy_types_1.TD.isAnyTimestamp(timeType)) {
453
+ return this.civilTruncAndOffset(baseExpr.sql, exports.CivilTime.Date, truncateTo, offset);
488
454
  }
489
- // Simple path: no civil time conversion needed
490
- let sql = baseExpr.sql;
455
+ const usesQueryZone = truncateTo !== undefined ||
456
+ (offset !== undefined && (0, malloy_types_1.isDateUnit)(offset.unit));
457
+ const zone = usesQueryZone ? queryZone(qi) : 'UTC';
458
+ const civil = this.sqlConvertToCivilTime(baseExpr.sql, zone, timeType);
459
+ return this.sqlConvertFromCivilTime(this.civilTruncAndOffset(civil, exports.CivilTime.DateAndTime, truncateTo, offset), zone, timeType);
460
+ }
461
+ civilTruncAndOffset(expr, civilType, truncateTo, offset) {
462
+ let sql = expr;
491
463
  if (truncateTo) {
492
- sql = this.sqlTruncate(sql, truncateTo, baseExpr.typeDef, false, qtz(qi));
464
+ sql = this.sqlTruncate(sql, truncateTo, civilType);
493
465
  }
494
466
  if (offset) {
495
- sql = this.sqlOffsetTime(sql, offset.op, offset.magnitude, offset.unit, baseExpr.typeDef, false, qtz(qi));
467
+ sql = this.sqlOffsetTime(sql, offset.op, offset.magnitude, offset.unit, civilType);
496
468
  }
497
469
  return sql;
498
470
  }
471
+ /**
472
+ * A cast between date, timestamp and timestamptz, or undefined for any
473
+ * other cast.
474
+ */
475
+ sqlTimeCast(qi, cast) {
476
+ var _a;
477
+ const { srcTypeDef, dstTypeDef } = this.sqlCastPrep(cast);
478
+ const expr = (_a = cast.e.sql) !== null && _a !== void 0 ? _a : '';
479
+ if (malloy_types_1.TD.isAnyTimestamp(srcTypeDef)) {
480
+ if (malloy_types_1.TD.isDate(dstTypeDef)) {
481
+ return this.sqlCivilToDate(this.sqlConvertToCivilTime(expr, queryZone(qi), srcTypeDef));
482
+ }
483
+ if (malloy_types_1.TD.isAnyTimestamp(dstTypeDef) &&
484
+ dstTypeDef.type !== srcTypeDef.type) {
485
+ // The same instant in another SQL type. A round trip through civil
486
+ // time in any zone keeps the instant; UTC costs the least.
487
+ return this.sqlConvertFromCivilTime(this.sqlConvertToCivilTime(expr, 'UTC', srcTypeDef), 'UTC', dstTypeDef);
488
+ }
489
+ }
490
+ if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isAnyTimestamp(dstTypeDef)) {
491
+ return this.sqlConvertFromCivilTime(this.sqlDateToCivil(expr), queryZone(qi), dstTypeDef);
492
+ }
493
+ return undefined;
494
+ }
495
+ /**
496
+ * The timestamp at which `timezone`, or the query time zone when there is
497
+ * none, shows the civil time `literal`.
498
+ */
499
+ sqlTimestampLiteral(qi, literal, timezone) {
500
+ return this.sqlConvertFromCivilTime(this.sqlCivilLiteral(literal), timezone !== null && timezone !== void 0 ? timezone : queryZone(qi), { type: 'timestamp' });
501
+ }
499
502
  /**
500
503
  * Reject characters that can break SQL literals or
501
504
  * be changed by SQL formatting. Timezone-name validity belongs to the
@@ -549,6 +552,7 @@ class Dialect {
549
552
  * @returns The SQL translation of the expression, or undefined
550
553
  */
551
554
  exprToSQL(qi, df) {
555
+ var _a;
552
556
  switch (df.node) {
553
557
  case 'now':
554
558
  return this.sqlNowExpr();
@@ -578,7 +582,7 @@ class Dialect {
578
582
  case 'extract':
579
583
  return this.sqlTimeExtractExpr(qi, df);
580
584
  case 'cast':
581
- return this.sqlCast(qi, df);
585
+ return (_a = this.sqlTimeCast(qi, df)) !== null && _a !== void 0 ? _a : this.sqlCast(qi, df);
582
586
  case 'regexpMatch':
583
587
  return this.sqlRegexpMatch(df);
584
588
  case '/': {
@@ -623,7 +627,9 @@ class Dialect {
623
627
  _func) {
624
628
  return 'sqlAggDistinct called but not implemented';
625
629
  }
626
- sqlSampleTable(tableSQL, sample) {
630
+ // onBaseTable: the source is a table the engine can sample in place; a
631
+ // dialect whose sampling reads the table itself ignores a sample elsewhere
632
+ sqlSampleTable(tableSQL, sample, _onBaseTable = true) {
627
633
  if (sample !== undefined) {
628
634
  throw new Error(`Sampling is not supported on dialect ${this.name}.`);
629
635
  }
@@ -694,6 +700,11 @@ class Dialect {
694
700
  escapeActive = false;
695
701
  escapeClause = true;
696
702
  }
703
+ else if (this.likeEscape && this.likeExtraWildcards.includes(c)) {
704
+ escaped += '^' + c;
705
+ escapeActive = false;
706
+ escapeClause = true;
707
+ }
697
708
  else {
698
709
  if (escapeActive) {
699
710
  if (this.likeEscape) {
@@ -720,10 +731,30 @@ class Dialect {
720
731
  */
721
732
  sqlBoolean(bv) {
722
733
  if (this.booleanType === 'none') {
723
- return bv ? '(1=1)' : '(1-0)';
734
+ return bv ? '(1=1)' : '(1=0)';
724
735
  }
725
736
  return bv ? 'true' : 'false';
726
737
  }
738
+ /**
739
+ * Malloy's `not`: of NULL, true. A dialect with booleanType 'none' has no
740
+ * boolean value for COALESCE to return, so the test is written as a
741
+ * condition.
742
+ */
743
+ sqlNot(condition) {
744
+ if (this.booleanType === 'none') {
745
+ return `(CASE WHEN ${condition} THEN 1 ELSE 0 END = 0)`;
746
+ }
747
+ return `COALESCE(NOT ${condition},TRUE)`;
748
+ }
749
+ /**
750
+ * A condition Malloy defines as true when SQL says NULL: `!=` and `!~`
751
+ */
752
+ sqlNullIsTrue(condition) {
753
+ if (this.booleanType === 'none') {
754
+ return `(CASE WHEN NOT (${condition}) THEN 1 ELSE 0 END = 0)`;
755
+ }
756
+ return `COALESCE(${condition},true)`;
757
+ }
727
758
  /**
728
759
  * What a boolean value looks like in a query result
729
760
  */
@@ -14,6 +14,7 @@ const standardsql_1 = require("./standardsql");
14
14
  const trino_1 = require("./trino");
15
15
  const mysql_1 = require("./mysql");
16
16
  const databricks_1 = require("./databricks");
17
+ const sqlserver_1 = require("./sqlserver");
17
18
  const dialectMap = new Map();
18
19
  function getDialect(name) {
19
20
  const d = dialectMap.get(name);
@@ -36,4 +37,5 @@ registerDialect(new trino_1.TrinoDialect());
36
37
  registerDialect(new trino_1.PrestoDialect());
37
38
  registerDialect(new mysql_1.MySQLDialect());
38
39
  registerDialect(new databricks_1.DatabricksDialect());
40
+ registerDialect(new sqlserver_1.SQLServerDialect());
39
41
  //# sourceMappingURL=dialect_map.js.map
@@ -1,6 +1,6 @@
1
1
  import type { Sampling, AtomicTypeDef, RegexMatchExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, OrderBy, TimestampUnit } from '../../model/malloy_types';
2
2
  import type { DialectFunctionOverloadDef } from '../functions';
3
- import type { CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
3
+ import type { CivilTime, CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
4
4
  import { PostgresBase } from '../pg_impl';
5
5
  import type { ValidateTablePathResult } from '../table-path';
6
6
  export declare class DuckDBDialect extends PostgresBase {
@@ -62,7 +62,7 @@ export declare class DuckDBDialect extends PostgresBase {
62
62
  castToString(expression: string): string;
63
63
  concat(...values: string[]): string;
64
64
  validateTypeName(sqlType: string): boolean;
65
- sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
65
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
66
66
  sqlRegexpMatch(df: RegexMatchExpr): string;
67
67
  sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
68
68
  sqlLiteralRecord(lit: RecordLiteralNode): string;
@@ -332,7 +332,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
332
332
  // Brackets: INT[ ]
333
333
  return sqlType.match(/^[A-Za-z\s(),[\]0-9]*$/) !== null;
334
334
  }
335
- sqlOffsetTime(expr, op, magnitude, unit, _typeDef, _inCivilTime, _timezone) {
335
+ sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
336
336
  // DuckDB doesn't support INTERVAL '1' WEEK, convert to days
337
337
  let offsetUnit = unit;
338
338
  let offsetMag = magnitude;
@@ -1,7 +1,7 @@
1
1
  export type { DialectFunctionOverloadDef, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, } from './functions/util';
2
2
  export { arg, anyExprType, makeParam, overload, minScalar, minAggregate, maxScalar, spread, param, variadicParam, literal, sql, } from './functions/util';
3
- export { Dialect, qtz } from './dialect';
4
- export type { DialectFieldList, CompiledOrderBy, LateralJoinExpression, QueryInfo, FieldReferenceType, } from './dialect';
3
+ export { Dialect, queryZone } from './dialect';
4
+ export type { DialectFieldList, CompiledOrderBy, LateralJoinExpression, QueryInfo, FieldReferenceType, GroupByClauseType, OrderByClauseType, } from './dialect';
5
5
  export { StandardSQLDialect } from './standardsql';
6
6
  export { PostgresDialect } from './postgres';
7
7
  export { DuckDBDialect } from './duckdb';
@@ -9,6 +9,7 @@ export { SnowflakeDialect } from './snowflake';
9
9
  export { TrinoDialect } from './trino';
10
10
  export { MySQLDialect } from './mysql';
11
11
  export { DatabricksDialect } from './databricks';
12
+ export { SQLServerDialect } from './sqlserver';
12
13
  export { getDialect, registerDialect } from './dialect_map';
13
14
  export { getMalloyStandardFunctions } from './functions';
14
15
  export type { MalloyStandardFunctionImplementations } from './functions';
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.qtz = exports.Dialect = exports.sql = exports.literal = exports.variadicParam = exports.param = exports.spread = exports.maxScalar = exports.minAggregate = exports.minScalar = exports.overload = exports.makeParam = exports.anyExprType = exports.arg = void 0;
7
+ exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.SQLServerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.queryZone = exports.Dialect = exports.sql = exports.literal = exports.variadicParam = exports.param = exports.spread = exports.maxScalar = exports.minAggregate = exports.minScalar = exports.overload = exports.makeParam = exports.anyExprType = exports.arg = void 0;
8
8
  var util_1 = require("./functions/util");
9
9
  Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return util_1.arg; } });
10
10
  Object.defineProperty(exports, "anyExprType", { enumerable: true, get: function () { return util_1.anyExprType; } });
@@ -20,7 +20,7 @@ Object.defineProperty(exports, "literal", { enumerable: true, get: function () {
20
20
  Object.defineProperty(exports, "sql", { enumerable: true, get: function () { return util_1.sql; } });
21
21
  var dialect_1 = require("./dialect");
22
22
  Object.defineProperty(exports, "Dialect", { enumerable: true, get: function () { return dialect_1.Dialect; } });
23
- Object.defineProperty(exports, "qtz", { enumerable: true, get: function () { return dialect_1.qtz; } });
23
+ Object.defineProperty(exports, "queryZone", { enumerable: true, get: function () { return dialect_1.queryZone; } });
24
24
  var standardsql_1 = require("./standardsql");
25
25
  Object.defineProperty(exports, "StandardSQLDialect", { enumerable: true, get: function () { return standardsql_1.StandardSQLDialect; } });
26
26
  var postgres_1 = require("./postgres");
@@ -35,6 +35,8 @@ var mysql_1 = require("./mysql");
35
35
  Object.defineProperty(exports, "MySQLDialect", { enumerable: true, get: function () { return mysql_1.MySQLDialect; } });
36
36
  var databricks_1 = require("./databricks");
37
37
  Object.defineProperty(exports, "DatabricksDialect", { enumerable: true, get: function () { return databricks_1.DatabricksDialect; } });
38
+ var sqlserver_1 = require("./sqlserver");
39
+ Object.defineProperty(exports, "SQLServerDialect", { enumerable: true, get: function () { return sqlserver_1.SQLServerDialect; } });
38
40
  var dialect_map_1 = require("./dialect_map");
39
41
  Object.defineProperty(exports, "getDialect", { enumerable: true, get: function () { return dialect_map_1.getDialect; } });
40
42
  Object.defineProperty(exports, "registerDialect", { enumerable: true, get: function () { return dialect_map_1.registerDialect; } });
@@ -1,5 +1,5 @@
1
- import type { Sampling, MeasureTimeExpr, RegexMatchExpr, TimeExtractExpr, TypecastExpr, BasicAtomicTypeDef, AtomicTypeDef, TimestampTypeDef, ArrayLiteralNode, RecordLiteralNode } from '../../model/malloy_types';
2
- import type { BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, OrderByClauseType, QueryInfo } from '../dialect';
1
+ import type { Sampling, MeasureTimeExpr, RegexMatchExpr, TypecastExpr, BasicAtomicTypeDef, AtomicTypeDef, ArrayLiteralNode, RecordLiteralNode, ATimestampTypeDef, TimestampUnit, ExtractUnit } from '../../model/malloy_types';
2
+ import type { CivilTime, BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, OrderByClauseType, QueryInfo } from '../dialect';
3
3
  import { Dialect } from '../dialect';
4
4
  import type { DialectFunctionOverloadDef } from '../functions';
5
5
  export declare class MySQLDialect extends Dialect {
@@ -60,18 +60,17 @@ export declare class MySQLDialect extends Dialect {
60
60
  sqlSelectAliasAsStruct(_alias: string, _fieldList: DialectFieldList): string;
61
61
  sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
62
62
  sqlNowExpr(): string;
63
- sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
64
- sql: string;
65
- typeDef: AtomicTypeDef;
66
- };
67
- sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: TimestampTypeDef): string;
68
- sqlTruncate(expr: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
69
- sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
70
- sqlTimeExtractExpr(qi: QueryInfo, te: TimeExtractExpr): string;
71
- sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
63
+ sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: ATimestampTypeDef): string;
64
+ sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: ATimestampTypeDef): string;
65
+ sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
66
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
67
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
68
+ sqlCivilToDate(expr: string): string;
69
+ sqlDateToCivil(expr: string): string;
70
+ sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
72
71
  sqlRegexpMatch(df: RegexMatchExpr): string;
73
72
  sqlDateLiteral(_qi: QueryInfo, literal: string): string;
74
- sqlTimestampLiteral(qi: QueryInfo, literal: string, timezone: string | undefined): string;
73
+ sqlCivilLiteral(literal: string): string;
75
74
  sqlTimestamptzLiteral(_qi: QueryInfo, _literal: string, _timezone: string): string;
76
75
  sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
77
76
  sqlAggDistinct(_key: string, _values: string[], _func: (valNames: string[]) => string): string;
@@ -351,17 +351,18 @@ class MySQLDialect extends dialect_1.Dialect {
351
351
  sqlNowExpr() {
352
352
  return 'LOCALTIMESTAMP';
353
353
  }
354
+ // Civil time is a DATETIME, which has no zone
354
355
  sqlConvertToCivilTime(expr, timezone, _typeDef) {
355
- // MySQL has no timestamptz type, so typeDef.timestamptz will never be true
356
- return {
357
- sql: `CONVERT_TZ(${expr}, 'UTC', ${this.sqlTimezoneLiteral(timezone)})`,
358
- typeDef: { type: 'timestamp' },
359
- };
356
+ return timezone === 'UTC'
357
+ ? expr
358
+ : `CONVERT_TZ(${expr}, 'UTC', ${this.sqlTimezoneLiteral(timezone)})`;
360
359
  }
361
360
  sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
362
- return `CONVERT_TZ(${expr}, ${this.sqlTimezoneLiteral(timezone)}, 'UTC')`;
361
+ return timezone === 'UTC'
362
+ ? expr
363
+ : `CONVERT_TZ(${expr}, ${this.sqlTimezoneLiteral(timezone)}, 'UTC')`;
363
364
  }
364
- sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
365
+ sqlTruncate(expr, unit, _civilType) {
365
366
  // For week truncation, adjust to Sunday first
366
367
  // DAYOFWEEK returns 1=Sunday, 2=Monday, etc., so subtract (DAYOFWEEK-1) days
367
368
  const adjustedExpr = unit === 'week'
@@ -392,7 +393,7 @@ class MySQLDialect extends dialect_1.Dialect {
392
393
  }
393
394
  return `TIMESTAMP(DATE_FORMAT(${adjustedExpr}, ${format}))`;
394
395
  }
395
- sqlOffsetTime(expr, op, magnitude, unit, _typeDef, _inCivilTime, _timezone) {
396
+ sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
396
397
  // Convert quarter/week to supported units
397
398
  let offsetUnit = unit;
398
399
  let offsetMag = magnitude;
@@ -407,27 +408,18 @@ class MySQLDialect extends dialect_1.Dialect {
407
408
  const interval = `INTERVAL ${offsetMag} ${offsetUnit}`;
408
409
  return `(${expr} ${op} ${interval})`;
409
410
  }
410
- sqlTimeExtractExpr(qi, te) {
411
- const msUnits = msExtractionMap[te.units] || te.units;
412
- let extractFrom = te.e.sql;
413
- if (malloy_types_1.TD.isTimestamp(te.e.typeDef)) {
414
- const tz = (0, dialect_1.qtz)(qi);
415
- if (tz) {
416
- extractFrom = `CONVERT_TZ(${extractFrom}, 'UTC', ${this.sqlTimezoneLiteral(tz)})`;
417
- }
418
- }
419
- return `${msUnits}(${extractFrom})`;
411
+ sqlCivilExtract(expr, units, _civilType) {
412
+ return `${msExtractionMap[units] || units}(${expr})`;
413
+ }
414
+ sqlCivilToDate(expr) {
415
+ return `CAST(${expr} AS DATE)`;
416
+ }
417
+ sqlDateToCivil(expr) {
418
+ return `CAST(${expr} AS DATETIME)`;
420
419
  }
421
- sqlCast(qi, cast) {
420
+ sqlCast(_qi, cast) {
422
421
  const srcSQL = cast.e.sql || 'internal-error-in-sql-generation';
423
- const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
424
- const tz = (0, dialect_1.qtz)(qi);
425
- if (op === 'timestamp::date' && tz) {
426
- return `CAST(CONVERT_TZ(${srcSQL}, 'UTC', ${this.sqlTimezoneLiteral(tz)}) AS DATE) `;
427
- }
428
- else if (op === 'date::timestamp' && tz) {
429
- return ` CONVERT_TZ(${srcSQL}, ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
430
- }
422
+ const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
431
423
  if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
432
424
  if (cast.safe) {
433
425
  throw new Error("Mysql dialect doesn't support Safe Cast");
@@ -445,11 +437,7 @@ class MySQLDialect extends dialect_1.Dialect {
445
437
  sqlDateLiteral(_qi, literal) {
446
438
  return `DATE '${literal}'`;
447
439
  }
448
- sqlTimestampLiteral(qi, literal, timezone) {
449
- const tz = timezone || (0, dialect_1.qtz)(qi);
450
- if (tz) {
451
- return `CONVERT_TZ('${literal}', ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
452
- }
440
+ sqlCivilLiteral(literal) {
453
441
  return `TIMESTAMP '${literal}'`;
454
442
  }
455
443
  sqlTimestamptzLiteral(_qi, _literal, _timezone) {
@@ -1,5 +1,5 @@
1
- import type { ArrayLiteralNode, AtomicTypeDef, ATimestampTypeDef, RecordLiteralNode, RegexMatchExpr, TimeExtractExpr, TimestampUnit, TypecastExpr } from '../model/malloy_types';
2
- import type { QueryInfo } from './dialect';
1
+ import type { ArrayLiteralNode, ATimestampTypeDef, RecordLiteralNode, ExtractUnit, RegexMatchExpr, TimestampUnit, TypecastExpr } from '../model/malloy_types';
2
+ import type { CivilTime, QueryInfo } from './dialect';
3
3
  import { Dialect } from './dialect';
4
4
  export declare const timeExtractMap: Record<string, string>;
5
5
  /**
@@ -14,18 +14,17 @@ export declare abstract class PostgresBase extends Dialect {
14
14
  identifierQuoteChar: string;
15
15
  sqlLiteralString(literal: string): string;
16
16
  sqlNowExpr(): string;
17
- sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
18
- sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
17
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
18
+ sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
19
19
  sqlRegexpMatch(df: RegexMatchExpr): string;
20
20
  sqlDateLiteral(_qi: QueryInfo, literal: string): string;
21
- sqlTimestampLiteral(qi: QueryInfo, literal: string, timezone: string | undefined): string;
21
+ sqlCivilLiteral(literal: string): string;
22
22
  sqlTimestamptzLiteral(_qi: QueryInfo, literal: string, timezone: string): string;
23
23
  sqlLiteralRecord(_lit: RecordLiteralNode): string;
24
24
  sqlLiteralArray(lit: ArrayLiteralNode): string;
25
- sqlConvertToCivilTime(expr: string, timezone: string, typeDef: AtomicTypeDef): {
26
- sql: string;
27
- typeDef: AtomicTypeDef;
28
- };
25
+ sqlConvertToCivilTime(expr: string, timezone: string, typeDef: ATimestampTypeDef): string;
29
26
  sqlConvertFromCivilTime(expr: string, timezone: string, destTypeDef: ATimestampTypeDef): string;
30
- sqlTruncate(expr: string, unit: TimestampUnit, _typeDef: AtomicTypeDef, inCivilTime: boolean, _timezone?: string): string;
27
+ sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
28
+ sqlCivilToDate(expr: string): string;
29
+ sqlDateToCivil(expr: string): string;
31
30
  }