@malloydata/malloy 0.0.433 → 0.0.435

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 (74) hide show
  1. package/dist/api/foundation/compile.d.ts +2 -2
  2. package/dist/api/foundation/compile.js +8 -9
  3. package/dist/api/foundation/config.js +6 -15
  4. package/dist/api/foundation/runtime.d.ts +2 -8
  5. package/dist/api/foundation/runtime.js +11 -39
  6. package/dist/api/foundation/types.d.ts +1 -1
  7. package/dist/connection/validate_table_path.d.ts +1 -2
  8. package/dist/connection/validate_table_path.js +1 -2
  9. package/dist/dialect/databricks/databricks.js +6 -6
  10. package/dist/dialect/dialect.d.ts +35 -3
  11. package/dist/dialect/dialect.js +67 -5
  12. package/dist/dialect/dialect_map.js +2 -0
  13. package/dist/dialect/duckdb/duckdb.js +7 -21
  14. package/dist/dialect/index.d.ts +2 -1
  15. package/dist/dialect/index.js +3 -1
  16. package/dist/dialect/mysql/mysql.js +8 -8
  17. package/dist/dialect/pg_impl.js +12 -12
  18. package/dist/dialect/postgres/postgres.js +3 -3
  19. package/dist/dialect/snowflake/snowflake.js +12 -12
  20. package/dist/dialect/sqlserver/dialect_functions.d.ts +2 -0
  21. package/dist/dialect/sqlserver/dialect_functions.js +42 -0
  22. package/dist/dialect/sqlserver/function_overrides.d.ts +2 -0
  23. package/dist/dialect/sqlserver/function_overrides.js +55 -0
  24. package/dist/dialect/sqlserver/index.d.ts +1 -0
  25. package/dist/dialect/sqlserver/index.js +22 -0
  26. package/dist/dialect/sqlserver/sqlserver.d.ts +102 -0
  27. package/dist/dialect/sqlserver/sqlserver.js +538 -0
  28. package/dist/dialect/sqlserver/windows_zones.d.ts +1 -0
  29. package/dist/dialect/sqlserver/windows_zones.js +613 -0
  30. package/dist/dialect/standardsql/standardsql.d.ts +1 -1
  31. package/dist/dialect/standardsql/standardsql.js +16 -13
  32. package/dist/dialect/trino/trino.d.ts +3 -3
  33. package/dist/dialect/trino/trino.js +32 -28
  34. package/dist/index.d.ts +1 -1
  35. package/dist/index.js +5 -3
  36. package/dist/lang/ast/source-elements/named-source.js +0 -2
  37. package/dist/lang/ast/source-elements/refined-source.js +1 -1
  38. package/dist/lang/ast/source-elements/sql-source.js +2 -4
  39. package/dist/lang/ast/source-elements/table-source.js +1 -2
  40. package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
  41. package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
  42. package/dist/lang/ast/statements/import-statement.js +4 -0
  43. package/dist/lang/ast/types/malloy-element.d.ts +0 -7
  44. package/dist/lang/ast/types/malloy-element.js +0 -18
  45. package/dist/lang/composite-source-utils.js +10 -3
  46. package/dist/lang/malloy-to-ast.js +2 -8
  47. package/dist/lang/parse-malloy.d.ts +20 -4
  48. package/dist/lang/parse-malloy.js +50 -15
  49. package/dist/lang/parse-tree-walkers/find-external-references.d.ts +3 -3
  50. package/dist/lang/parse-tree-walkers/find-external-references.js +17 -16
  51. package/dist/lang/test/test-translator.d.ts +12 -2
  52. package/dist/lang/test/test-translator.js +16 -2
  53. package/dist/model/expression_compiler.js +1 -1
  54. package/dist/model/join_instance.d.ts +1 -0
  55. package/dist/model/join_instance.js +9 -1
  56. package/dist/model/query_query.d.ts +28 -1
  57. package/dist/model/query_query.js +299 -156
  58. package/dist/model/source_def_utils.d.ts +7 -1
  59. package/dist/model/source_def_utils.js +20 -0
  60. package/dist/model/sql_block.d.ts +2 -2
  61. package/dist/model/sql_block.js +11 -47
  62. package/dist/model/sql_compiled.d.ts +3 -1
  63. package/dist/model/sql_compiled.js +8 -6
  64. package/dist/model/stage_writer.js +3 -3
  65. package/dist/model/utils.d.ts +2 -2
  66. package/dist/model/utils.js +2 -3
  67. package/dist/test/index.js +16 -8
  68. package/dist/test/matchers.d.ts +1 -1
  69. package/dist/test/matchers.js +1 -1
  70. package/dist/test/test-models.js +6 -13
  71. package/dist/version.d.ts +1 -1
  72. package/dist/version.js +1 -1
  73. package/femto-config.motly +15 -1
  74. package/package.json +7 -5
@@ -94,7 +94,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
94
94
  }
95
95
  sqlAggregateTurtle(groupSet, fieldList, orderBy, limit, filterSQL) {
96
96
  const fields = fieldList
97
- .map(f => `\n ${f.sqlOutputName}: ${f.sqlExpression}`)
97
+ .map(f => `\n ${this.sqlQuoteIdentifier(f.rawName)}: ${f.sqlExpression}`)
98
98
  .join(', ');
99
99
  const orderByClause = orderBy ? this.sqlTurtleOrderByClause(orderBy) : '';
100
100
  const cond = (0, dialect_1.turtleGroupSetCondition)(groupSet, filterSQL);
@@ -107,7 +107,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
107
107
  }
108
108
  sqlAnyValueTurtle(groupSet, fieldList) {
109
109
  const fields = fieldList
110
- .map(f => `${f.sqlOutputName}:=${f.sqlExpression}`)
110
+ .map(f => `${this.sqlQuoteIdentifier(f.rawName)}:=${f.sqlExpression}`)
111
111
  .join(', ');
112
112
  return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT_PACK(${fields}) END)`;
113
113
  }
@@ -116,10 +116,10 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
116
116
  }
117
117
  sqlCoaleseMeasuresInline(groupSet, fieldList) {
118
118
  const fields = fieldList
119
- .map(f => `${f.sqlOutputName}: ${f.sqlExpression} `)
119
+ .map(f => `${this.sqlQuoteIdentifier(f.rawName)}: ${f.sqlExpression} `)
120
120
  .join(', ');
121
121
  const nullValues = fieldList
122
- .map(f => `${f.sqlOutputName}: NULL`)
122
+ .map(f => `${this.sqlQuoteIdentifier(f.rawName)}: NULL`)
123
123
  .join(', ');
124
124
  return `COALESCE(FIRST({${fields}}) FILTER(WHERE group_set=${groupSet}), {${nullValues}})`;
125
125
  }
@@ -181,28 +181,14 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
181
181
  return `DROP MACRO IF EXISTS ${id}; \n${hackSplitComment}\n CREATE MACRO ${id}(_param) AS (\n${(0, utils_1.indent)(funcText)}\n);\n${hackSplitComment}\n`;
182
182
  }
183
183
  sqlCreateFunctionCombineLastStage(lastStageName, dialectFieldList, orderBy) {
184
- let o = '';
185
- if (orderBy) {
186
- const clauses = [];
187
- for (const c of orderBy) {
188
- if (typeof c.field === 'string') {
189
- clauses.push(`${c.field} ${c.dir || 'asc'}`);
190
- }
191
- else {
192
- clauses.push(`${dialectFieldList[c.field].sqlOutputName} ${c.dir || 'asc'}`);
193
- }
194
- }
195
- if (clauses.length > 0) {
196
- o = ` ORDER BY ${clauses.join(', ')}`;
197
- }
198
- }
184
+ const o = this.sqlCombineLastStageOrderBy(orderBy, dialectFieldList);
199
185
  return `SELECT LIST(STRUCT_PACK(${dialectFieldList
200
- .map(d => this.sqlQuoteIdentifier(d.sqlOutputName))
186
+ .map(d => this.sqlQuoteIdentifier(d.rawName))
201
187
  .join(',')})${o}) FROM ${lastStageName}\n`;
202
188
  }
203
189
  sqlSelectAliasAsStruct(alias, dialectFieldList) {
204
190
  return `STRUCT_PACK(${dialectFieldList
205
- .map(d => `${alias}.${d.sqlOutputName}`)
191
+ .map(d => `${alias}.${this.sqlQuoteIdentifier(d.rawName)}`)
206
192
  .join(', ')})`;
207
193
  }
208
194
  // The simple way to do this is to add a comment on the table
@@ -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
3
  export { Dialect, qtz } from './dialect';
4
- export type { DialectFieldList, CompiledOrderBy, LateralJoinExpression, QueryInfo, FieldReferenceType, } 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.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;
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; } });
@@ -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; } });
@@ -261,7 +261,7 @@ class MySQLDialect extends dialect_1.Dialect {
261
261
  // quote in the user-controlled field name cannot close the SQL
262
262
  // string literal.
263
263
  const jsonPathLit = this.sqlLiteralString('$.' + f.rawName);
264
- fields.push(`${this.sqlQuoteIdentifier(f.sqlOutputName)} ${fType} PATH ${jsonPathLit}`);
264
+ fields.push(`${this.sqlQuoteIdentifier(f.rawName)} ${fType} PATH ${jsonPathLit}`);
265
265
  }
266
266
  return fields.join(',\n');
267
267
  }
@@ -354,12 +354,12 @@ class MySQLDialect extends dialect_1.Dialect {
354
354
  sqlConvertToCivilTime(expr, timezone, _typeDef) {
355
355
  // MySQL has no timestamptz type, so typeDef.timestamptz will never be true
356
356
  return {
357
- sql: `CONVERT_TZ(${expr}, 'UTC', '${timezone}')`,
357
+ sql: `CONVERT_TZ(${expr}, 'UTC', ${this.sqlTimezoneLiteral(timezone)})`,
358
358
  typeDef: { type: 'timestamp' },
359
359
  };
360
360
  }
361
361
  sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
362
- return `CONVERT_TZ(${expr}, '${timezone}', 'UTC')`;
362
+ return `CONVERT_TZ(${expr}, ${this.sqlTimezoneLiteral(timezone)}, 'UTC')`;
363
363
  }
364
364
  sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
365
365
  // For week truncation, adjust to Sunday first
@@ -413,7 +413,7 @@ class MySQLDialect extends dialect_1.Dialect {
413
413
  if (malloy_types_1.TD.isTimestamp(te.e.typeDef)) {
414
414
  const tz = (0, dialect_1.qtz)(qi);
415
415
  if (tz) {
416
- extractFrom = `CONVERT_TZ(${extractFrom}, 'UTC', '${tz}')`;
416
+ extractFrom = `CONVERT_TZ(${extractFrom}, 'UTC', ${this.sqlTimezoneLiteral(tz)})`;
417
417
  }
418
418
  }
419
419
  return `${msUnits}(${extractFrom})`;
@@ -423,10 +423,10 @@ class MySQLDialect extends dialect_1.Dialect {
423
423
  const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
424
424
  const tz = (0, dialect_1.qtz)(qi);
425
425
  if (op === 'timestamp::date' && tz) {
426
- return `CAST(CONVERT_TZ(${srcSQL}, 'UTC', '${tz}') AS DATE) `;
426
+ return `CAST(CONVERT_TZ(${srcSQL}, 'UTC', ${this.sqlTimezoneLiteral(tz)}) AS DATE) `;
427
427
  }
428
428
  else if (op === 'date::timestamp' && tz) {
429
- return ` CONVERT_TZ(${srcSQL}, '${tz}', 'UTC')`;
429
+ return ` CONVERT_TZ(${srcSQL}, ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
430
430
  }
431
431
  if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
432
432
  if (cast.safe) {
@@ -448,7 +448,7 @@ class MySQLDialect extends dialect_1.Dialect {
448
448
  sqlTimestampLiteral(qi, literal, timezone) {
449
449
  const tz = timezone || (0, dialect_1.qtz)(qi);
450
450
  if (tz) {
451
- return `CONVERT_TZ('${literal}', '${tz}', 'UTC')`;
451
+ return `CONVERT_TZ('${literal}', ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
452
452
  }
453
453
  return `TIMESTAMP '${literal}'`;
454
454
  }
@@ -500,7 +500,7 @@ class MySQLDialect extends dialect_1.Dialect {
500
500
  }
501
501
  mapFieldsForJsonObject(fieldList, nullValues) {
502
502
  return fieldList
503
- .map(f => `${f.sqlOutputName.replace(/`/g, "'")}, ${nullValues ? 'NULL' : f.sqlExpression}\n`)
503
+ .map(f => `${this.sqlLiteralString(f.rawName)}, ${nullValues ? 'NULL' : f.sqlExpression}\n`)
504
504
  .join(', ');
505
505
  }
506
506
  castToString(expression) {
@@ -45,7 +45,7 @@ class PostgresBase extends dialect_1.Dialect {
45
45
  if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
46
46
  const tz = (0, dialect_1.qtz)(qi);
47
47
  if (tz) {
48
- extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
48
+ extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
49
49
  }
50
50
  }
51
51
  const extracted = `EXTRACT(${units} FROM ${extractFrom})`;
@@ -59,23 +59,23 @@ class PostgresBase extends dialect_1.Dialect {
59
59
  if (tz && srcTypeDef && dstTypeDef) {
60
60
  // TIMESTAMP → DATE: convert via TIMESTAMPTZ to query timezone
61
61
  if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
62
- return `CAST((${expr}::TIMESTAMPTZ) AT TIME ZONE '${tz}' AS DATE)`;
62
+ return `CAST((${expr}::TIMESTAMPTZ) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
63
63
  }
64
64
  // TIMESTAMPTZ → DATE: convert to query timezone
65
65
  if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
66
- return `CAST((${expr}) AT TIME ZONE '${tz}' AS DATE)`;
66
+ return `CAST((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
67
67
  }
68
68
  // DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
69
69
  if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
70
- return `CAST((${expr})::TIMESTAMP AT TIME ZONE '${tz}' AS TIMESTAMP)`;
70
+ return `CAST((${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
71
71
  }
72
72
  // DATE → TIMESTAMPTZ: interpret date in query timezone
73
73
  if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
74
- return `(${expr})::TIMESTAMP AT TIME ZONE '${tz}'`;
74
+ return `(${expr})::TIMESTAMP AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
75
75
  }
76
76
  // TIMESTAMPTZ → TIMESTAMP: convert to query timezone (returns TIMESTAMP)
77
77
  if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
78
- return `(${expr}) AT TIME ZONE '${tz}'`;
78
+ return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
79
79
  }
80
80
  }
81
81
  // No special handling needed, or no query timezone
@@ -94,12 +94,12 @@ class PostgresBase extends dialect_1.Dialect {
94
94
  sqlTimestampLiteral(qi, literal, timezone) {
95
95
  const tz = timezone || (0, dialect_1.qtz)(qi);
96
96
  if (tz) {
97
- return `TIMESTAMPTZ '${literal} ${tz}'::TIMESTAMP`;
97
+ return `TIMESTAMPTZ '${literal} ${this.checkedTimezoneName(tz)}'::TIMESTAMP`;
98
98
  }
99
99
  return `TIMESTAMP '${literal}'`;
100
100
  }
101
101
  sqlTimestamptzLiteral(_qi, literal, timezone) {
102
- return `TIMESTAMPTZ '${literal} ${timezone}'`;
102
+ return `TIMESTAMPTZ '${literal} ${this.checkedTimezoneName(timezone)}'`;
103
103
  }
104
104
  sqlLiteralRecord(_lit) {
105
105
  throw new Error('Cannot create a record literal for postgres base dialect');
@@ -113,22 +113,22 @@ class PostgresBase extends dialect_1.Dialect {
113
113
  // For timestamptz (TIMESTAMPTZ): AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
114
114
  if (typeDef.type === 'timestamptz') {
115
115
  return {
116
- sql: `(${expr}) AT TIME ZONE '${timezone}'`,
116
+ sql: `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
117
117
  typeDef: { type: 'timestamp' },
118
118
  };
119
119
  }
120
120
  // For plain timestamps: cast to TIMESTAMPTZ (interprets as UTC)
121
121
  // Then AT TIME ZONE converts to plain TIMESTAMP (civil in timezone)
122
122
  return {
123
- sql: `(${expr})::TIMESTAMPTZ AT TIME ZONE '${timezone}'`,
123
+ sql: `(${expr})::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
124
124
  typeDef: { type: 'timestamp' },
125
125
  };
126
126
  }
127
127
  sqlConvertFromCivilTime(expr, timezone, destTypeDef) {
128
128
  if (destTypeDef.type === 'timestamptz') {
129
- return `(${expr}) AT TIME ZONE '${timezone}'`;
129
+ return `(${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`;
130
130
  }
131
- return `((${expr}) AT TIME ZONE '${timezone}')::TIMESTAMP`;
131
+ return `((${expr}) AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)})::TIMESTAMP`;
132
132
  }
133
133
  sqlTruncate(expr, unit, _typeDef, inCivilTime, _timezone) {
134
134
  // PostgreSQL/DuckDB starts weeks on Monday, Malloy wants Sunday
@@ -94,7 +94,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
94
94
  }
95
95
  mapFields(fieldList) {
96
96
  return fieldList
97
- .map(f => `\n ${f.sqlExpression}${f.typeDef.type === 'number' ? `::${this.defaultNumberType}` : ''} as ${f.sqlOutputName}`)
97
+ .map(f => `\n ${f.sqlExpression}${f.typeDef.type === 'number' ? `::${this.defaultNumberType}` : ''} as ${this.sqlQuoteIdentifier(f.rawName)}`)
98
98
  .join(', ');
99
99
  }
100
100
  sqlAggregateTurtle(groupSet, fieldList, orderBy, limit, filterSQL) {
@@ -110,7 +110,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
110
110
  }
111
111
  sqlAnyValueTurtle(groupSet, fieldList) {
112
112
  const fields = fieldList
113
- .map(f => `${f.sqlExpression} as ${f.sqlOutputName}`)
113
+ .map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
114
114
  .join(', ');
115
115
  return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}))`;
116
116
  }
@@ -393,7 +393,7 @@ class PostgresDialect extends pg_impl_1.PostgresBase {
393
393
  if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
394
394
  const tz = (0, dialect_1.qtz)(qi);
395
395
  if (tz) {
396
- extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
396
+ extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE ${this.sqlTimezoneLiteral(tz)})`;
397
397
  }
398
398
  }
399
399
  // PostgreSQL before 14 returns a double precision for EXTRACT, cast to integer
@@ -105,7 +105,7 @@ class SnowflakeDialect extends dialect_1.Dialect {
105
105
  }
106
106
  mapFields(fieldList) {
107
107
  return fieldList
108
- .map(f => `\n ${f.sqlExpression} as ${f.sqlOutputName}`)
108
+ .map(f => `\n ${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
109
109
  .join(', ');
110
110
  }
111
111
  mapFieldsForObjectConstruct(fieldList) {
@@ -138,7 +138,7 @@ class SnowflakeDialect extends dialect_1.Dialect {
138
138
  sqlCoaleseMeasuresInline(groupSet, fieldList) {
139
139
  const fields = this.mapFieldsForObjectConstruct(fieldList);
140
140
  const nullValues = fieldList
141
- .map(f => `'${f.sqlOutputName}', NULL`)
141
+ .map(f => `${this.sqlLiteralString(f.rawName)}, NULL`)
142
142
  .join(', ');
143
143
  return `COALESCE(ARRAY_AGG(CASE WHEN group_set=${groupSet} THEN OBJECT_CONSTRUCT_KEEP_NULL(${fields}) END)[0], OBJECT_CONSTRUCT_KEEP_NULL(${nullValues}))`;
144
144
  }
@@ -254,21 +254,21 @@ ${(0, utils_1.indent)(sql)}
254
254
  // Returns TIMESTAMP_TZ with timezone preserved
255
255
  if (typeDef.type === 'timestamptz') {
256
256
  return {
257
- sql: `CONVERT_TIMEZONE('${timezone}', ${expr})`,
257
+ sql: `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, ${expr})`,
258
258
  typeDef: { type: 'timestamptz' },
259
259
  };
260
260
  }
261
261
  // For plain timestamps (TIMESTAMP_NTZ): use 3-arg form
262
262
  // Must cast to TIMESTAMP_NTZ first, returns TIMESTAMP_NTZ
263
263
  return {
264
- sql: `CONVERT_TIMEZONE('UTC', '${timezone}', (${expr})::TIMESTAMP_NTZ)`,
264
+ sql: `CONVERT_TIMEZONE('UTC', ${this.sqlTimezoneLiteral(timezone)}, (${expr})::TIMESTAMP_NTZ)`,
265
265
  typeDef: { type: 'timestamp' },
266
266
  };
267
267
  }
268
268
  sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
269
269
  // After civil time operations, we have a TIMESTAMP_NTZ in the target timezone
270
270
  // Convert from timezone to UTC, returning TIMESTAMP_NTZ
271
- return `CONVERT_TIMEZONE('${timezone}', 'UTC', (${expr})::TIMESTAMP_NTZ)`;
271
+ return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(timezone)}, 'UTC', (${expr})::TIMESTAMP_NTZ)`;
272
272
  }
273
273
  sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
274
274
  // Snowflake session is configured with WEEK_START=7 (Sunday)
@@ -287,7 +287,7 @@ ${(0, utils_1.indent)(sql)}
287
287
  let extractFrom = from.e.sql;
288
288
  const tz = (0, dialect_1.qtz)(qi);
289
289
  if (tz && malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
290
- extractFrom = `CONVERT_TIMEZONE('${tz}', ${extractFrom})`;
290
+ extractFrom = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${extractFrom})`;
291
291
  }
292
292
  return `EXTRACT(${extractUnits} FROM ${extractFrom})`;
293
293
  }
@@ -295,7 +295,7 @@ ${(0, utils_1.indent)(sql)}
295
295
  if (tz !== undefined) {
296
296
  return `(
297
297
  TO_CHAR(${sqlExpr}::TIMESTAMP_NTZ, 'YYYY-MM-DD HH24:MI:SS.FF9') ||
298
- TO_CHAR(CONVERT_TIMEZONE('${tz}', '1970-01-01 00:00:00'), 'TZHTZM')
298
+ TO_CHAR(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, '1970-01-01 00:00:00'), 'TZHTZM')
299
299
  )::TIMESTAMP_TZ`;
300
300
  }
301
301
  return `${sqlExpr}::TIMESTAMP_NTZ`;
@@ -321,11 +321,11 @@ ${(0, utils_1.indent)(sql)}
321
321
  if (tz && srcTypeDef && dstTypeDef) {
322
322
  // TIMESTAMP → DATE: convert to query timezone, then to date
323
323
  if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
324
- return `TO_DATE(CONVERT_TIMEZONE('${tz}', ${src}))`;
324
+ return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
325
325
  }
326
326
  // TIMESTAMPTZ → DATE: convert to query timezone, then to date
327
327
  if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
328
- return `TO_DATE(CONVERT_TIMEZONE('${tz}', ${src}))`;
328
+ return `TO_DATE(CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src}))`;
329
329
  }
330
330
  // DATE → TIMESTAMP: interpret date in query timezone, return UTC timestamp
331
331
  if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
@@ -339,7 +339,7 @@ ${(0, utils_1.indent)(sql)}
339
339
  }
340
340
  // TIMESTAMPTZ → TIMESTAMP: convert to query timezone, get UTC wall clock
341
341
  if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
342
- return `CONVERT_TIMEZONE('${tz}', ${src})::TIMESTAMP_NTZ`;
342
+ return `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, ${src})::TIMESTAMP_NTZ`;
343
343
  }
344
344
  // TIMESTAMP → TIMESTAMPTZ: interpret as UTC, convert to TIMESTAMPTZ
345
345
  if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
@@ -357,7 +357,7 @@ ${(0, utils_1.indent)(sql)}
357
357
  let ret = `'${literal}'::TIMESTAMP_NTZ`;
358
358
  if (tz) {
359
359
  // Interpret the literal as being in query timezone, convert to UTC
360
- ret = `CONVERT_TIMEZONE('${tz}', 'UTC', ${ret})`;
360
+ ret = `CONVERT_TIMEZONE(${this.sqlTimezoneLiteral(tz)}, 'UTC', ${ret})`;
361
361
  }
362
362
  return ret;
363
363
  }
@@ -374,7 +374,7 @@ ${(0, utils_1.indent)(sql)}
374
374
  const minute = dt.minute;
375
375
  const second = dt.second;
376
376
  const nanosecond = dt.millisecond * 1000000;
377
- return `TIMESTAMP_TZ_FROM_PARTS(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second}, ${nanosecond}, '${timezone}')`;
377
+ return `TIMESTAMP_TZ_FROM_PARTS(${year}, ${month}, ${day}, ${hour}, ${minute}, ${second}, ${nanosecond}, ${this.sqlTimezoneLiteral(timezone)})`;
378
378
  }
379
379
  sqlMeasureTimeExpr(df) {
380
380
  const from = df.kids.left;
@@ -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,102 @@
1
+ import type { Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, MeasureTimeExpr, BasicAtomicTypeDef, RegexMatchExpr, TimeExtractExpr, TimestampUnit, OrderBy } from '../../model/malloy_types';
2
+ import type { DialectFunctionOverloadDef } from '../functions';
3
+ import type { BooleanTypeSupport, DialectFieldList, FieldReferenceType, GroupByClauseType, QueryInfo } from '../dialect';
4
+ import { 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
+ sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
82
+ sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
83
+ sqlRegexpMatch(_df: RegexMatchExpr): string;
84
+ sqlDateLiteral(_qi: QueryInfo, literal: string): string;
85
+ sqlTimestampLiteral(qi: QueryInfo, literal: string, timezone: string | undefined): string;
86
+ sqlTimestamptzLiteral(_qi: QueryInfo, _literal: string, _timezone: string): string;
87
+ sqlTimezoneLiteral(timezone: string): string;
88
+ sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: AtomicTypeDef): {
89
+ sql: string;
90
+ typeDef: AtomicTypeDef;
91
+ };
92
+ sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: ATimestampTypeDef): string;
93
+ sqlTruncate(expr: string, unit: TimestampUnit, typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
94
+ sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
95
+ sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
96
+ getDialectFunctionOverrides(): {
97
+ [name: string]: DialectFunctionOverloadDef[];
98
+ };
99
+ getDialectFunctions(): {
100
+ [name: string]: DialectFunctionOverloadDef[];
101
+ };
102
+ }