@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
@@ -39,46 +39,13 @@ class PostgresBase extends dialect_1.Dialect {
39
39
  sqlNowExpr() {
40
40
  return 'LOCALTIMESTAMP';
41
41
  }
42
- sqlTimeExtractExpr(qi, from) {
43
- const units = exports.timeExtractMap[from.units] || from.units;
44
- let extractFrom = from.e.sql;
45
- if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
46
- const tz = (0, dialect_1.qtz)(qi);
47
- if (tz) {
48
- extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
49
- }
50
- }
51
- const extracted = `EXTRACT(${units} FROM ${extractFrom})`;
52
- return from.units === 'day_of_week' ? `(${extracted}+1)` : extracted;
42
+ sqlCivilExtract(expr, units, _civilType) {
43
+ const extracted = `EXTRACT(${exports.timeExtractMap[units] || units} FROM ${expr})`;
44
+ return units === 'day_of_week' ? `(${extracted}+1)` : extracted;
53
45
  }
54
- sqlCast(qi, cast) {
46
+ sqlCast(_qi, cast) {
55
47
  const expr = cast.e.sql || '';
56
48
  const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
57
- const tz = (0, dialect_1.qtz)(qi);
58
- // Timezone-aware casts when query timezone is set
59
- if (tz && srcTypeDef && dstTypeDef) {
60
- // TIMESTAMP → DATE: convert via TIMESTAMPTZ to query timezone
61
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
62
- return `CAST((${expr}::TIMESTAMPTZ) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
63
- }
64
- // TIMESTAMPTZ → DATE: convert to query timezone
65
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
66
- return `CAST((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
67
- }
68
- // DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
69
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
70
- return `CAST((${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
71
- }
72
- // DATE → TIMESTAMPTZ: interpret date in query timezone
73
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
74
- return `(${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
75
- }
76
- // TIMESTAMPTZ → TIMESTAMP: convert to query timezone (returns TIMESTAMP)
77
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
78
- return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
79
- }
80
- }
81
- // No special handling needed, or no query timezone
82
49
  if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
83
50
  const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
84
51
  return `${castFunc}(${expr} AS ${dstSQLType})`;
@@ -91,11 +58,7 @@ class PostgresBase extends dialect_1.Dialect {
91
58
  sqlDateLiteral(_qi, literal) {
92
59
  return `DATE '${literal}'`;
93
60
  }
94
- sqlTimestampLiteral(qi, literal, timezone) {
95
- const tz = timezone || (0, dialect_1.qtz)(qi);
96
- if (tz) {
97
- return `TIMESTAMPTZ '${literal} ${this.checkedTimezoneName(tz)}'::TIMESTAMP`;
98
- }
61
+ sqlCivilLiteral(literal) {
99
62
  return `TIMESTAMP '${literal}'`;
100
63
  }
101
64
  sqlTimestamptzLiteral(_qi, literal, timezone) {
@@ -109,44 +72,35 @@ class PostgresBase extends dialect_1.Dialect {
109
72
  return 'ARRAY[' + array.join(',') + ']';
110
73
  }
111
74
  sqlConvertToCivilTime(expr, timezone, typeDef) {
112
- // PostgreSQL/DuckDB: AT TIME ZONE is polymorphic
113
- // For timestamptz (TIMESTAMPTZ): AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
114
- if (typeDef.type === 'timestamptz') {
115
- return {
116
- sql: `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
117
- typeDef: { type: 'timestamp' },
118
- };
75
+ const tz = this.sqlTimezoneLiteral(timezone);
76
+ if (malloy_types_1.TD.isTimestamptz(typeDef)) {
77
+ return `(${expr}) AT TIME ZONE ${tz}`;
119
78
  }
120
- // For plain timestamps: cast to TIMESTAMPTZ (interprets as UTC)
121
- // Then AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
122
- return {
123
- sql: `(${expr})::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
124
- typeDef: { type: 'timestamp' },
125
- };
79
+ return timezone === 'UTC'
80
+ ? expr
81
+ : `(${expr})::TIMESTAMPTZ AT TIME ZONE ${tz}`;
126
82
  }
127
83
  sqlConvertFromCivilTime(expr, timezone, destTypeDef) {
128
- if (destTypeDef.type === 'timestamptz') {
129
- return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`;
84
+ const tz = this.sqlTimezoneLiteral(timezone);
85
+ if (malloy_types_1.TD.isTimestamptz(destTypeDef)) {
86
+ return `(${expr}) AT TIME ZONE ${tz}`;
130
87
  }
131
- return `((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)})::TIMESTAMP`;
88
+ return timezone === 'UTC'
89
+ ? expr
90
+ : `((${expr}) AT TIME ZONE ${tz})::TIMESTAMP`;
132
91
  }
133
- sqlTruncate(expr, unit, _typeDef, inCivilTime, _timezone) {
134
- // PostgreSQL/DuckDB starts weeks on Monday, Malloy wants Sunday
135
- // Add 1 day before truncating, subtract 1 day after
136
- let truncated;
92
+ sqlTruncate(expr, unit, _civilType) {
93
+ // Weeks start on Monday here, so truncate the following day
137
94
  if (unit === 'week') {
138
- truncated = `(DATE_TRUNC('${unit}', (${expr} + INTERVAL '1' DAY)) - INTERVAL '1' DAY)`;
139
- }
140
- else {
141
- truncated = `DATE_TRUNC('${unit}', ${expr})`;
95
+ return `(DATE_TRUNC('${unit}', (${expr} + INTERVAL '1' DAY)) - INTERVAL '1' DAY)`;
142
96
  }
143
- // DATE_TRUNC returns DATE for calendar units (day/week/month/quarter/year)
144
- // When in civil time (plain TIMESTAMP), cast DATE back to TIMESTAMP to continue operations
145
- const calendarUnits = ['day', 'week', 'month', 'quarter', 'year'];
146
- if (inCivilTime && calendarUnits.includes(unit)) {
147
- return `(${truncated})::TIMESTAMP`;
148
- }
149
- return truncated;
97
+ return `DATE_TRUNC('${unit}', ${expr})`;
98
+ }
99
+ sqlCivilToDate(expr) {
100
+ return `CAST(${expr} AS DATE)`;
101
+ }
102
+ sqlDateToCivil(expr) {
103
+ return `CAST(${expr} AS TIMESTAMP)`;
150
104
  }
151
105
  }
152
106
  exports.PostgresBase = PostgresBase;
@@ -1,6 +1,6 @@
1
- import type { Sampling, AtomicTypeDef, TypecastExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, ArrayLiteralNode, TimeExtractExpr, TimestampUnit } from '../../model/malloy_types';
1
+ import type { Sampling, AtomicTypeDef, TypecastExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, ArrayLiteralNode, TimestampUnit, ExtractUnit } from '../../model/malloy_types';
2
2
  import type { DialectFunctionOverloadDef } from '../functions';
3
- import { type CompiledOrderBy, type DialectFieldList, type FieldReferenceType, type QueryInfo } from '../dialect';
3
+ import { type CompiledOrderBy, type DialectFieldList, type FieldReferenceType, type QueryInfo, type CivilTime } from '../dialect';
4
4
  import { PostgresBase } from '../pg_impl';
5
5
  export declare class PostgresDialect extends PostgresBase {
6
6
  name: string;
@@ -46,7 +46,7 @@ export declare class PostgresDialect extends PostgresBase {
46
46
  sqlFinalStage(lastStageName: string, _fields: string[]): string;
47
47
  sqlSelectAliasAsStruct(alias: string): string;
48
48
  sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
49
- sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
49
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
50
50
  sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
51
51
  sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
52
52
  sqlSumDistinct(key: string, value: string, funcName: string): string;
@@ -66,5 +66,5 @@ export declare class PostgresDialect extends PostgresBase {
66
66
  validateTypeName(sqlType: string): boolean;
67
67
  sqlLiteralRecord(lit: RecordLiteralNode): string;
68
68
  sqlLiteralArray(lit: ArrayLiteralNode): string;
69
- sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
69
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
70
70
  }
@@ -200,7 +200,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
200
200
  sqlCreateTableAsSelect(_tableName, _sql) {
201
201
  throw new Error('Not implemented Yet');
202
202
  }
203
- sqlOffsetTime(expr, op, magnitude, unit, _typeDef, _inCivilTime, _timezone) {
203
+ sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
204
204
  // Convert quarter/week to supported units
205
205
  let offsetUnit = unit;
206
206
  let offsetMag = magnitude;
@@ -387,19 +387,11 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
387
387
  const array = lit.kids.values.map(val => val.sql);
388
388
  return 'JSONB_BUILD_ARRAY(' + array.join(',') + ')';
389
389
  }
390
- sqlTimeExtractExpr(qi, from) {
391
- const units = pg_impl_1.timeExtractMap[from.units] || from.units;
392
- let extractFrom = from.e.sql;
393
- if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
394
- const tz = (0, dialect_1.qtz)(qi);
395
- if (tz) {
396
- extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
397
- }
398
- }
390
+ sqlCivilExtract(expr, units, _civilType) {
399
391
  // PostgreSQL before 14 returns a double precision for EXTRACT, cast to integer
400
392
  // since it is common to pass an extraction to mod ( like in fitler expressions )
401
- const extracted = `EXTRACT(${units} FROM ${extractFrom})::integer`;
402
- return from.units === 'day_of_week' ? `(${extracted}+1)` : `(${extracted})`;
393
+ const extracted = `EXTRACT(${pg_impl_1.timeExtractMap[units] || units} FROM ${expr})::integer`;
394
+ return units === 'day_of_week' ? `(${extracted}+1)` : `(${extracted})`;
403
395
  }
404
396
  }
405
397
  exports.PostgresDialect = PostgresDialect;
@@ -1,7 +1,7 @@
1
- import type { Sampling, AtomicTypeDef, TimeExtractExpr, TypecastExpr, MeasureTimeExpr, RegexMatchExpr, BasicAtomicTypeDef, TimestampTypeDef, ArrayLiteralNode, RecordLiteralNode } from '../../model/malloy_types';
1
+ import type { Sampling, AtomicTypeDef, TypecastExpr, MeasureTimeExpr, RegexMatchExpr, BasicAtomicTypeDef, ArrayLiteralNode, RecordLiteralNode, ATimestampTypeDef, TimestampUnit, ExtractUnit } from '../../model/malloy_types';
2
2
  import type { DialectFunctionOverloadDef } from '../functions';
3
3
  import type { CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping, QueryInfo } from '../dialect';
4
- import { Dialect } from '../dialect';
4
+ import { CivilTime, Dialect } from '../dialect';
5
5
  export declare class SnowflakeDialect extends Dialect {
6
6
  name: string;
7
7
  experimental: boolean;
@@ -50,19 +50,17 @@ export declare class SnowflakeDialect extends Dialect {
50
50
  sqlCreateFunctionCombineLastStage(_lastStageName: string): string;
51
51
  sqlSelectAliasAsStruct(alias: string): string;
52
52
  sqlCreateTableAsSelect(tableName: string, sql: string): string;
53
- sqlConvertToCivilTime(expr: string, timezone: string, typeDef: AtomicTypeDef): {
54
- sql: string;
55
- typeDef: AtomicTypeDef;
56
- };
57
- sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: TimestampTypeDef): string;
58
- sqlTruncate(expr: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
59
- sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: string, typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
60
- sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
61
- private atTz;
53
+ sqlConvertToCivilTime(expr: string, timezone: string, typeDef: ATimestampTypeDef): string;
54
+ sqlConvertFromCivilTime(expr: string, timezone: string, destTypeDef: ATimestampTypeDef): string;
55
+ sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
56
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, civilType: CivilTime): string;
57
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
58
+ sqlCivilToDate(expr: string): string;
59
+ sqlDateToCivil(expr: string): string;
62
60
  sqlNowExpr(): string;
63
- sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
61
+ sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
64
62
  sqlDateLiteral(_qi: QueryInfo, literal: string): string;
65
- sqlTimestampLiteral(qi: QueryInfo, literal: string, timezone: string | undefined): string;
63
+ sqlCivilLiteral(literal: string): string;
66
64
  sqlTimestamptzLiteral(_qi: QueryInfo, literal: string, timezone: string): string;
67
65
  sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
68
66
  sqlRegexpMatch(compare: RegexMatchExpr): string;
@@ -249,61 +249,45 @@ ${(0, utils_1.indent)(sql)}
249
249
  );
250
250
  `;
251
251
  }
252
+ // Civil time is a TIMESTAMP_NTZ, which has no zone
252
253
  sqlConvertToCivilTime(expr, timezone, typeDef) {
253
- // For timestamptz (TIMESTAMP_TZ): use 2-arg form
254
- // Returns TIMESTAMP_TZ with timezone preserved
255
- if (typeDef.type === 'timestamptz') {
256
- return {
257
- sql: `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, ${expr})`,
258
- typeDef: { type: 'timestamptz' },
259
- };
254
+ const tz = this.sqlTimezoneLiteral(timezone);
255
+ if (malloy_types_1.TD.isTimestamptz(typeDef)) {
256
+ return `CONVERT_TIMEZONE(${tz}, ${expr})::TIMESTAMP_NTZ`;
260
257
  }
261
- // For plain timestamps (TIMESTAMP_NTZ): use 3-arg form
262
- // Must cast to TIMESTAMP_NTZ first, returns TIMESTAMP_NTZ
263
- return {
264
- sql: `CONVERT_TIMEZONE('UTC', ${this.sqlTimezoneLiteral(timezone)}, (${expr})::TIMESTAMP_NTZ)`,
265
- typeDef: { type: 'timestamp' },
266
- };
267
- }
268
- sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
269
- // After civil time operations, we have a TIMESTAMP_NTZ in the target timezone
270
- // Convert from timezone to UTC, returning TIMESTAMP_NTZ
271
- return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, 'UTC', (${expr})::TIMESTAMP_NTZ)`;
272
- }
273
- sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
274
- // Snowflake session is configured with WEEK_START=7 (Sunday)
275
- // so DATE_TRUNC already truncates to Sunday - no adjustment needed
276
- // Unlike PostgreSQL/DuckDB, Snowflake's DATE_TRUNC preserves the input type
277
- // (TIMESTAMP_NTZ → TIMESTAMP_NTZ, TIMESTAMP_TZ → TIMESTAMP_TZ)
258
+ return timezone === 'UTC'
259
+ ? expr
260
+ : `CONVERT_TIMEZONE('UTC', ${tz}, (${expr})::TIMESTAMP_NTZ)`;
261
+ }
262
+ sqlConvertFromCivilTime(expr, timezone, destTypeDef) {
263
+ const utc = timezone === 'UTC'
264
+ ? expr
265
+ : `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, 'UTC', (${expr})::TIMESTAMP_NTZ)`;
266
+ // The session time zone is UTC
267
+ return malloy_types_1.TD.isTimestamptz(destTypeDef) ? `(${utc})::TIMESTAMP_TZ` : utc;
268
+ }
269
+ sqlTruncate(expr, unit, _civilType) {
270
+ // The session sets WEEK_START to Sunday
278
271
  return `DATE_TRUNC('${unit}', ${expr})`;
279
272
  }
280
- sqlOffsetTime(expr, op, magnitude, unit, typeDef, _inCivilTime, _timezone) {
281
- const funcName = typeDef.type === 'date' ? 'DATEADD' : 'TIMESTAMPADD';
273
+ sqlOffsetTime(expr, op, magnitude, unit, civilType) {
274
+ const funcName = civilType === dialect_1.CivilTime.Date ? 'DATEADD' : 'TIMESTAMPADD';
282
275
  const n = op === '+' ? magnitude : `-(${magnitude})`;
283
276
  return `${funcName}(${unit}, ${n}, ${expr})`;
284
277
  }
285
- sqlTimeExtractExpr(qi, from) {
286
- const extractUnits = extractionMap[from.units] || from.units;
287
- let extractFrom = from.e.sql;
288
- const tz = (0, dialect_1.qtz)(qi);
289
- if (tz && malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
290
- extractFrom = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${extractFrom})`;
291
- }
292
- return `EXTRACT(${extractUnits} FROM ${extractFrom})`;
293
- }
294
- atTz(sqlExpr, tz) {
295
- if (tz !== undefined) {
296
- return `(
297
- TO_CHAR(${sqlExpr}::TIMESTAMP_NTZ, 'YYYY-MM-DD HH24:MI:SS.FF9') ||
298
- TO_CHAR(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, '1970-01-01 00:00:00'), 'TZHTZM')
299
- )::TIMESTAMP_TZ`;
300
- }
301
- return `${sqlExpr}::TIMESTAMP_NTZ`;
278
+ sqlCivilExtract(expr, units, _civilType) {
279
+ return `EXTRACT(${extractionMap[units] || units} FROM ${expr})`;
280
+ }
281
+ sqlCivilToDate(expr) {
282
+ return `TO_DATE(${expr})`;
283
+ }
284
+ sqlDateToCivil(expr) {
285
+ return `(${expr})::TIMESTAMP_NTZ`;
302
286
  }
303
287
  sqlNowExpr() {
304
288
  return 'CURRENT_TIMESTAMP()';
305
289
  }
306
- sqlCast(qi, cast) {
290
+ sqlCast(_qi, cast) {
307
291
  const src = cast.e.sql || '';
308
292
  const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
309
293
  if (malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
@@ -316,50 +300,14 @@ ${(0, utils_1.indent)(sql)}
316
300
  throw new Error(`Snowflake dialect doesn't support safe cast for a few types:
317
301
  refer to: https://docs.snowflake.com/en/sql-reference/functions/try_cast`);
318
302
  }
319
- const tz = (0, dialect_1.qtz)(qi);
320
- // Timezone-aware casts when query timezone is set
321
- if (tz && srcTypeDef && dstTypeDef) {
322
- // TIMESTAMP → DATE: convert to query timezone, then to date
323
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
324
- return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
325
- }
326
- // TIMESTAMPTZ → DATE: convert to query timezone, then to date
327
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
328
- return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
329
- }
330
- // DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
331
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
332
- const retExpr = `TO_TIMESTAMP(${src})`;
333
- return this.atTz(retExpr, tz);
334
- }
335
- // DATE → TIMESTAMPTZ: interpret date in query timezone
336
- if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
337
- const retExpr = `TO_TIMESTAMP(${src})`;
338
- return this.atTz(retExpr, tz);
339
- }
340
- // TIMESTAMPTZ → TIMESTAMP: convert to query timezone, get UTC wall clock
341
- if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
342
- return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src})::TIMESTAMP_NTZ`;
343
- }
344
- // TIMESTAMP → TIMESTAMPTZ: interpret as UTC, convert to TIMESTAMPTZ
345
- if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
346
- return this.atTz(src, tz);
347
- }
348
- }
349
303
  const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
350
304
  return `${castFunc}(${src} AS ${dstSQLType})`;
351
305
  }
352
306
  sqlDateLiteral(_qi, literal) {
353
307
  return `TO_DATE('${literal}')`;
354
308
  }
355
- sqlTimestampLiteral(qi, literal, timezone) {
356
- const tz = timezone || (0, dialect_1.qtz)(qi);
357
- let ret = `'${literal}'::TIMESTAMP_NTZ`;
358
- if (tz) {
359
- // Interpret the literal as being in query timezone, convert to UTC
360
- ret = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, 'UTC', ${ret})`;
361
- }
362
- return ret;
309
+ sqlCivilLiteral(literal) {
310
+ return `'${literal}'::TIMESTAMP_NTZ`;
363
311
  }
364
312
  sqlTimestamptzLiteral(_qi, literal, timezone) {
365
313
  // Use TIMESTAMP_TZ_FROM_PARTS to create timestamptz
@@ -0,0 +1,2 @@
1
+ import type { DefinitionBlueprintMap } from '../functions/util';
2
+ export declare const SQLSERVER_DIALECT_FUNCTIONS: DefinitionBlueprintMap;
@@ -0,0 +1,42 @@
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.SQLSERVER_DIALECT_FUNCTIONS = void 0;
8
+ const util_1 = require("../functions/util");
9
+ const order_by = {
10
+ node: 'aggregate_order_by',
11
+ prefix: ' WITHIN GROUP (',
12
+ suffix: ')',
13
+ };
14
+ // STRING_AGG's result is limited to 8000 bytes unless its input is a MAX type.
15
+ const string_agg = {
16
+ default_separator: {
17
+ takes: { 'value': { dimension: 'string' } },
18
+ returns: { measure: 'string' },
19
+ supportsOrderBy: true,
20
+ impl: {
21
+ expr: (0, util_1.sql) `STRING_AGG(CAST(${(0, util_1.arg)('value')} AS NVARCHAR(MAX)), ',')${order_by}`,
22
+ },
23
+ },
24
+ with_separator: {
25
+ takes: {
26
+ 'value': { dimension: 'string' },
27
+ 'separator': { literal: 'string' },
28
+ },
29
+ returns: { measure: 'string' },
30
+ supportsOrderBy: true,
31
+ impl: {
32
+ expr: (0, util_1.sql) `STRING_AGG(CAST(${(0, util_1.arg)('value')} AS NVARCHAR(MAX)), ${(0, util_1.arg)('separator')})${order_by}`,
33
+ },
34
+ },
35
+ };
36
+ // No string_agg_distinct: STRING_AGG accepts no DISTINCT, so the translator
37
+ // reports the function as unknown for this dialect.
38
+ exports.SQLSERVER_DIALECT_FUNCTIONS = {
39
+ string_agg,
40
+ ...(0, util_1.def)('reverse', { 'str': 'string' }, 'string'),
41
+ };
42
+ //# sourceMappingURL=dialect_functions.js.map
@@ -0,0 +1,2 @@
1
+ import type { MalloyStandardFunctionImplementations as OverrideMap } from '../functions/malloy_standard_functions';
2
+ export declare const SQLSERVER_MALLOY_STANDARD_OVERLOADS: OverrideMap;
@@ -0,0 +1,55 @@
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.SQLSERVER_MALLOY_STANDARD_OVERLOADS = void 0;
8
+ // LEN ignores trailing spaces: append one character, count, subtract one
9
+ const len = (arg) => `(LEN(${arg} + N'x') - 1)`;
10
+ exports.SQLSERVER_MALLOY_STANDARD_OVERLOADS = {
11
+ ceil: { function: 'CEILING' },
12
+ chr: { sql: "CASE WHEN ${value} = 0 THEN '' ELSE NCHAR(${value}) END" },
13
+ div: { sql: 'FLOOR(${dividend} / ${divisor})' },
14
+ // starts_with and ends_with are written as comparisons because SQL Server
15
+ // has no boolean values: legal as a condition (where:, having:), a server
16
+ // error as a value. A NULL input matches nothing, where the standard's
17
+ // COALESCE(..., false) returns false.
18
+ ends_with: {
19
+ sql: `RIGHT(\${value}, ${len('${suffix}')}) = \${suffix}`,
20
+ },
21
+ ifnull: { function: 'ISNULL' },
22
+ // A FLOAT holds neither infinity nor NaN, so neither condition is ever true
23
+ is_inf: { sql: '(1=0)' },
24
+ is_nan: { sql: '(1=0)' },
25
+ length: { sql: len('${value}') },
26
+ ln: { sql: 'LOG(${value})' },
27
+ log: { sql: 'LOG(${value}, ${base})' },
28
+ // POWER answers in the type of its base, so an integer base truncates
29
+ pow: { sql: 'POWER(CAST(${base} AS FLOAT(53)), ${exponent})' },
30
+ round: {
31
+ to_integer: { sql: 'ROUND(${value}, 0)' },
32
+ to_precision: { sql: 'ROUND(${value}, ${precision})' },
33
+ },
34
+ atan2: { function: 'ATN2' },
35
+ trim: { characters: { sql: 'TRIM(${trim_characters} FROM ${value})' } },
36
+ starts_with: {
37
+ sql: `LEFT(\${value}, ${len('${prefix}')}) = \${prefix}`,
38
+ },
39
+ stddev: { function: 'STDEV' },
40
+ string_repeat: { function: 'REPLICATE' },
41
+ strpos: { sql: 'CHARINDEX(${search_string}, ${test_string})' },
42
+ substr: {
43
+ position_only: {
44
+ sql: `SUBSTRING(\${value}, CASE WHEN \${position} < 0 THEN ${len('${value}')} + \${position} + 1 ELSE \${position} END, ${len('${value}')})`,
45
+ },
46
+ with_length: {
47
+ sql: `SUBSTRING(\${value}, CASE WHEN \${position} < 0 THEN ${len('${value}')} + \${position} + 1 ELSE \${position} END, \${length})`,
48
+ },
49
+ },
50
+ trunc: {
51
+ to_integer: { sql: 'ROUND(${value}, 0, 1)' },
52
+ to_precision: { sql: 'ROUND(${value}, ${precision}, 1)' },
53
+ },
54
+ };
55
+ //# sourceMappingURL=function_overrides.js.map
@@ -0,0 +1 @@
1
+ export * from './sqlserver';
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./sqlserver"), exports);
22
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,101 @@
1
+ import type { Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, MeasureTimeExpr, BasicAtomicTypeDef, RegexMatchExpr, TimestampUnit, OrderBy, ExtractUnit } from '../../model/malloy_types';
2
+ import type { DialectFunctionOverloadDef } from '../functions';
3
+ import type { BooleanTypeSupport, DialectFieldList, FieldReferenceType, GroupByClauseType, QueryInfo } from '../dialect';
4
+ import { CivilTime, Dialect } from '../dialect';
5
+ import type { ValidateTablePathResult } from '../table-path';
6
+ /** T-SQL for SQL Server 2017 and later, and Azure SQL Database. */
7
+ export declare class SQLServerDialect extends Dialect {
8
+ name: string;
9
+ experimental: boolean;
10
+ stringLiteralStyle: "doubled";
11
+ identifierEscapeStyle: "doubled";
12
+ identifierQuoteChar: string;
13
+ defaultNumberType: string;
14
+ defaultDecimalType: string;
15
+ udfPrefix: string;
16
+ hasFinalStage: boolean;
17
+ divisionIsInteger: boolean;
18
+ supportsSumDistinctFunction: boolean;
19
+ unnestWithNumbers: boolean;
20
+ defaultSampling: {
21
+ rows: number;
22
+ };
23
+ supportUnnestArrayAgg: boolean;
24
+ supportsAggDistinct: boolean;
25
+ supportsCTEinCoorelatedSubQueries: boolean;
26
+ dontUnionIndex: boolean;
27
+ supportsQualify: boolean;
28
+ supportsSafeCast: boolean;
29
+ supportsNesting: boolean;
30
+ supportsPipelinesInViews: boolean;
31
+ supportsFullJoin: boolean;
32
+ readsNestedData: boolean;
33
+ supportsComplexFilteredSources: boolean;
34
+ supportsArraysInData: boolean;
35
+ compoundObjectInSchema: boolean;
36
+ supportsSelectReplace: boolean;
37
+ supportsTempTables: boolean;
38
+ booleanType: BooleanTypeSupport;
39
+ hasTimestamptz: boolean;
40
+ supportsBigIntPrecision: boolean;
41
+ maxIdentifierLength: number;
42
+ likeEscape: boolean;
43
+ likeExtraWildcards: string[];
44
+ groupByClause: GroupByClauseType;
45
+ tablePathBareIdentRegex: RegExp;
46
+ /**
47
+ * `[schema].[table]` is read as the ANSI-quoted path it stands for, and the
48
+ * canonical form is the ANSI spelling the base validates.
49
+ */
50
+ sqlValidateTableName(input: string): ValidateTablePathResult;
51
+ malloyTypeToSQLType(malloyType: AtomicTypeDef): string;
52
+ sqlTypeToMalloyType(sqlType: string): BasicAtomicTypeDef;
53
+ validateTypeName(sqlType: string): boolean;
54
+ sqlGroupSetTable(groupSetCount: number): string;
55
+ sqlAnyValue(_groupSet: number, fieldName: string): string;
56
+ sqlAggregateTurtle(): string;
57
+ sqlAnyValueTurtle(): string;
58
+ sqlAnyValueLastTurtle(): string;
59
+ sqlCoaleseMeasuresInline(): string;
60
+ sqlUnnestAlias(): string;
61
+ sqlSelectAliasAsStruct(): string;
62
+ sqlLiteralRecord(): string;
63
+ sqlLiteralArray(): string;
64
+ sqlFieldReference(parentAlias: string, _parentType: FieldReferenceType, childName: string, _childType: string): string;
65
+ private unsupported;
66
+ sqlUnnestPipelineHead(_isSingleton: boolean, _sourceSQLExpression: string, _fieldList?: DialectFieldList): string;
67
+ sqlCreateFunction(_id: string, _funcText: string): string;
68
+ sqlCreateFunctionCombineLastStage(_lastStageName: string, _fieldList: DialectFieldList, _orderBy: OrderBy[] | undefined): string;
69
+ sqlCreateTableAsSelect(tableName: string, sql: string): string;
70
+ sqlGenerateUUID(): string;
71
+ sqlLiteralString(literal: string): string;
72
+ sqlSumDistinctHashedKey(sqlDistinctKey: string): string;
73
+ sqlSumDistinct(key: string, value: string, funcName: string): string;
74
+ sqlAggDistinct(_key: string, _values: string[], _func: (valNames: string[]) => string): string;
75
+ sqlOrderByLimit(orderTerms: string[], limit: number | undefined): string;
76
+ sqlSampleTable(tableSQL: string, sample: Sampling | undefined, onBaseTable?: boolean): string;
77
+ castToString(expression: string): string;
78
+ concat(...values: string[]): string;
79
+ sqlDateToString(sqlDateExp: string): string;
80
+ sqlNowExpr(): string;
81
+ sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
82
+ sqlCivilToDate(expr: string): string;
83
+ sqlDateToCivil(expr: string): string;
84
+ sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
85
+ sqlRegexpMatch(_df: RegexMatchExpr): string;
86
+ sqlDateLiteral(_qi: QueryInfo, literal: string): string;
87
+ sqlCivilLiteral(literal: string): string;
88
+ sqlTimestamptzLiteral(_qi: QueryInfo, literal: string, timezone: string): string;
89
+ sqlTimezoneLiteral(timezone: string): string;
90
+ sqlConvertToCivilTime(expr: string, timezone: string, typeDef: ATimestampTypeDef): string;
91
+ sqlConvertFromCivilTime(expr: string, timezone: string, destTypeDef: ATimestampTypeDef): string;
92
+ sqlTruncate(expr: string, unit: TimestampUnit, civilType: CivilTime): string;
93
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
94
+ sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
95
+ getDialectFunctionOverrides(): {
96
+ [name: string]: DialectFunctionOverloadDef[];
97
+ };
98
+ getDialectFunctions(): {
99
+ [name: string]: DialectFunctionOverloadDef[];
100
+ };
101
+ }