@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.
- package/dist/api/foundation/compile.d.ts +2 -2
- package/dist/api/foundation/compile.js +8 -9
- package/dist/api/foundation/config.js +6 -15
- package/dist/api/foundation/runtime.d.ts +2 -8
- package/dist/api/foundation/runtime.js +11 -39
- package/dist/api/foundation/types.d.ts +1 -1
- package/dist/connection/validate_table_path.d.ts +1 -2
- package/dist/connection/validate_table_path.js +1 -2
- package/dist/dialect/databricks/databricks.js +6 -6
- package/dist/dialect/dialect.d.ts +35 -3
- package/dist/dialect/dialect.js +67 -5
- package/dist/dialect/dialect_map.js +2 -0
- package/dist/dialect/duckdb/duckdb.js +7 -21
- package/dist/dialect/index.d.ts +2 -1
- package/dist/dialect/index.js +3 -1
- package/dist/dialect/mysql/mysql.js +8 -8
- package/dist/dialect/pg_impl.js +12 -12
- package/dist/dialect/postgres/postgres.js +3 -3
- package/dist/dialect/snowflake/snowflake.js +12 -12
- package/dist/dialect/sqlserver/dialect_functions.d.ts +2 -0
- package/dist/dialect/sqlserver/dialect_functions.js +42 -0
- package/dist/dialect/sqlserver/function_overrides.d.ts +2 -0
- package/dist/dialect/sqlserver/function_overrides.js +55 -0
- package/dist/dialect/sqlserver/index.d.ts +1 -0
- package/dist/dialect/sqlserver/index.js +22 -0
- package/dist/dialect/sqlserver/sqlserver.d.ts +102 -0
- package/dist/dialect/sqlserver/sqlserver.js +538 -0
- package/dist/dialect/sqlserver/windows_zones.d.ts +1 -0
- package/dist/dialect/sqlserver/windows_zones.js +613 -0
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +16 -13
- package/dist/dialect/trino/trino.d.ts +3 -3
- package/dist/dialect/trino/trino.js +32 -28
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/lang/ast/source-elements/named-source.js +0 -2
- package/dist/lang/ast/source-elements/refined-source.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +2 -4
- package/dist/lang/ast/source-elements/table-source.js +1 -2
- package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
- package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
- package/dist/lang/ast/statements/import-statement.js +4 -0
- package/dist/lang/ast/types/malloy-element.d.ts +0 -7
- package/dist/lang/ast/types/malloy-element.js +0 -18
- package/dist/lang/composite-source-utils.js +10 -3
- package/dist/lang/malloy-to-ast.js +2 -8
- package/dist/lang/parse-malloy.d.ts +20 -4
- package/dist/lang/parse-malloy.js +50 -15
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +3 -3
- package/dist/lang/parse-tree-walkers/find-external-references.js +17 -16
- package/dist/lang/test/test-translator.d.ts +12 -2
- package/dist/lang/test/test-translator.js +16 -2
- package/dist/model/expression_compiler.js +1 -1
- package/dist/model/join_instance.d.ts +1 -0
- package/dist/model/join_instance.js +9 -1
- package/dist/model/query_query.d.ts +28 -1
- package/dist/model/query_query.js +299 -156
- package/dist/model/source_def_utils.d.ts +7 -1
- package/dist/model/source_def_utils.js +20 -0
- package/dist/model/sql_block.d.ts +2 -2
- package/dist/model/sql_block.js +11 -47
- package/dist/model/sql_compiled.d.ts +3 -1
- package/dist/model/sql_compiled.js +8 -6
- package/dist/model/stage_writer.js +3 -3
- package/dist/model/utils.d.ts +2 -2
- package/dist/model/utils.js +2 -3
- package/dist/test/index.js +16 -8
- package/dist/test/matchers.d.ts +1 -1
- package/dist/test/matchers.js +1 -1
- package/dist/test/test-models.js +6 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/femto-config.motly +15 -1
- package/package.json +7 -5
|
@@ -150,7 +150,7 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
150
150
|
// can array agg or any_value a struct...
|
|
151
151
|
sqlAggregateTurtle(groupSet, fieldList, orderBy, limit, filterSQL) {
|
|
152
152
|
const fields = fieldList
|
|
153
|
-
.map(f => `\n ${f.sqlExpression} as ${f.
|
|
153
|
+
.map(f => `\n ${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
154
154
|
.join(', ');
|
|
155
155
|
const orderByClause = orderBy ? this.sqlTurtleOrderByClause(orderBy) : '';
|
|
156
156
|
// BigQuery ARRAY_AGG takes LIMIT as its final sub-clause, after ORDER BY.
|
|
@@ -162,7 +162,7 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
162
162
|
}
|
|
163
163
|
sqlAnyValueTurtle(groupSet, fieldList) {
|
|
164
164
|
const fields = fieldList
|
|
165
|
-
.map(f => `${f.sqlExpression} as ${f.
|
|
165
|
+
.map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
166
166
|
.join(', ');
|
|
167
167
|
return `ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}) END)`;
|
|
168
168
|
}
|
|
@@ -171,10 +171,10 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
171
171
|
}
|
|
172
172
|
sqlCoaleseMeasuresInline(groupSet, fieldList) {
|
|
173
173
|
const fields = fieldList
|
|
174
|
-
.map(f => `${f.sqlExpression} as ${f.
|
|
174
|
+
.map(f => `${f.sqlExpression} as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
175
175
|
.join(', ');
|
|
176
176
|
const nullValues = fieldList
|
|
177
|
-
.map(f => `NULL as ${f.
|
|
177
|
+
.map(f => `NULL as ${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
178
178
|
.join(', ');
|
|
179
179
|
return `COALESCE(ANY_VALUE(CASE WHEN group_set=${groupSet} THEN STRUCT(${fields}) END), STRUCT(${nullValues}))`;
|
|
180
180
|
}
|
|
@@ -240,8 +240,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
240
240
|
sqlCreateFunctionCombineLastStage(lastStageName) {
|
|
241
241
|
return `SELECT ARRAY((SELECT AS STRUCT * FROM ${lastStageName}))\n`;
|
|
242
242
|
}
|
|
243
|
-
sqlSelectAliasAsStruct(alias) {
|
|
244
|
-
|
|
243
|
+
sqlSelectAliasAsStruct(alias, dialectFieldList) {
|
|
244
|
+
const fields = dialectFieldList
|
|
245
|
+
.map(d => `${alias}.${this.sqlQuoteIdentifier(d.rawName)}`)
|
|
246
|
+
.join(', ');
|
|
247
|
+
return `(SELECT AS STRUCT ${fields})`;
|
|
245
248
|
}
|
|
246
249
|
sqlNowExpr() {
|
|
247
250
|
return 'CURRENT_TIMESTAMP()';
|
|
@@ -249,18 +252,18 @@ ${(0, utils_1.indent)(sql)}
|
|
|
249
252
|
sqlTimeExtractExpr(qi, te) {
|
|
250
253
|
const extractTo = extractMap[te.units] || te.units;
|
|
251
254
|
const tz = malloy_types_1.TD.isAnyTimestamp(te.e.typeDef) && qtz(qi);
|
|
252
|
-
const tzAdd = tz ? ` AT TIME ZONE
|
|
255
|
+
const tzAdd = tz ? ` AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}` : '';
|
|
253
256
|
return `EXTRACT(${extractTo} FROM ${te.e.sql}${tzAdd})`;
|
|
254
257
|
}
|
|
255
258
|
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
256
259
|
// BigQuery has no timestamptz type, so typeDef.timestamptz will never be true
|
|
257
260
|
return {
|
|
258
|
-
sql: `DATETIME(${expr},
|
|
261
|
+
sql: `DATETIME(${expr}, ${this.sqlTimezoneLiteral(timezone)})`,
|
|
259
262
|
typeDef: { type: 'timestamp' },
|
|
260
263
|
};
|
|
261
264
|
}
|
|
262
265
|
sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
|
|
263
|
-
return `TIMESTAMP(${expr},
|
|
266
|
+
return `TIMESTAMP(${expr}, ${this.sqlTimezoneLiteral(timezone)})`;
|
|
264
267
|
}
|
|
265
268
|
sqlTruncate(expr, unit, typeDef, inCivilTime, timezone) {
|
|
266
269
|
if (inCivilTime) {
|
|
@@ -272,7 +275,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
272
275
|
return `DATE_TRUNC(${expr}, ${unit})`;
|
|
273
276
|
}
|
|
274
277
|
// TIMESTAMP truncation with optional timezone
|
|
275
|
-
const tzParam = timezone ? `,
|
|
278
|
+
const tzParam = timezone ? `, ${this.sqlTimezoneLiteral(timezone)}` : '';
|
|
276
279
|
return `TIMESTAMP_TRUNC(${expr}, ${unit}${tzParam})`;
|
|
277
280
|
}
|
|
278
281
|
sqlOffsetTime(expr, op, magnitude, unit, typeDef, inCivilTime, _timezone) {
|
|
@@ -299,10 +302,10 @@ ${(0, utils_1.indent)(sql)}
|
|
|
299
302
|
const tz = qtz(qi);
|
|
300
303
|
const src = cast.e.sql || '';
|
|
301
304
|
if (op === 'timestamp::date' && tz) {
|
|
302
|
-
return `DATE(${src}
|
|
305
|
+
return `DATE(${src},${this.sqlTimezoneLiteral(tz)})`;
|
|
303
306
|
}
|
|
304
307
|
if (op === 'date::timestamp' && tz) {
|
|
305
|
-
return `TIMESTAMP(${src},
|
|
308
|
+
return `TIMESTAMP(${src}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
306
309
|
}
|
|
307
310
|
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
308
311
|
const castFunc = cast.safe ? 'SAFE_CAST' : 'CAST';
|
|
@@ -320,7 +323,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
320
323
|
let timestampArgs = `'${literal}'`;
|
|
321
324
|
const tz = timezone || qtz(qi);
|
|
322
325
|
if (tz && tz !== 'UTC') {
|
|
323
|
-
timestampArgs +=
|
|
326
|
+
timestampArgs += `,${this.sqlTimezoneLiteral(tz)}`;
|
|
324
327
|
}
|
|
325
328
|
return `TIMESTAMP(${timestampArgs})`;
|
|
326
329
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Expr, Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode } from '../../model/malloy_types';
|
|
1
|
+
import type { Expr, Sampling, AtomicTypeDef, ATimestampTypeDef, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode, OrderBy } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { CompiledOrderBy, DialectFieldList, OrderByClauseType, QueryInfo } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
@@ -49,8 +49,8 @@ export declare class TrinoDialect extends PostgresBase {
|
|
|
49
49
|
sqlUnnestPipelineHead(isSingleton: boolean, sourceSQLExpression: string): string;
|
|
50
50
|
sqlCreateFunction(id: string, funcText: string): string;
|
|
51
51
|
sqlCreateTableAsSelect(tableName: string, sql: string): string;
|
|
52
|
-
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList): string;
|
|
53
|
-
sqlSelectAliasAsStruct(alias: string, fieldList:
|
|
52
|
+
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList, orderBy: OrderBy[] | undefined): string;
|
|
53
|
+
sqlSelectAliasAsStruct(alias: string, fieldList: DialectFieldList): string;
|
|
54
54
|
keywords: string[];
|
|
55
55
|
sqlConvertToCivilTime(expr: string, timezone: string, typeDef: AtomicTypeDef): {
|
|
56
56
|
sql: string;
|
|
@@ -82,7 +82,7 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
|
|
|
82
82
|
this.orderByClause = 'output_name';
|
|
83
83
|
this.nullMatchesFunctionSignature = false;
|
|
84
84
|
this.supportsSelectReplace = false;
|
|
85
|
-
this.supportsComplexFilteredSources =
|
|
85
|
+
this.supportsComplexFilteredSources = true;
|
|
86
86
|
this.supportsTempTables = false;
|
|
87
87
|
this.supportsCountApprox = true;
|
|
88
88
|
this.supportsHyperLogLog = true;
|
|
@@ -223,7 +223,8 @@ class TrinoDialect extends pg_impl_1.PostgresBase {
|
|
|
223
223
|
}
|
|
224
224
|
buildTypeExpression(fieldList) {
|
|
225
225
|
return fieldList
|
|
226
|
-
.map(dlf => `${
|
|
226
|
+
.map(dlf => `${this.sqlQuoteIdentifier(dlf.rawName)} ` +
|
|
227
|
+
this.malloyTypeToSQLType(dlf.typeDef))
|
|
227
228
|
.join(', \n');
|
|
228
229
|
}
|
|
229
230
|
// can array agg or any_value a struct...
|
|
@@ -325,15 +326,18 @@ ${(0, utils_1.indent)(sql)}
|
|
|
325
326
|
);
|
|
326
327
|
`;
|
|
327
328
|
}
|
|
328
|
-
sqlCreateFunctionCombineLastStage(lastStageName, fieldList) {
|
|
329
|
+
sqlCreateFunctionCombineLastStage(lastStageName, fieldList, orderBy) {
|
|
329
330
|
const fields = fieldList.map(f => f.sqlExpression).join(', ');
|
|
330
331
|
const definitions = this.buildTypeExpression(fieldList);
|
|
331
|
-
|
|
332
|
+
const o = this.sqlCombineLastStageOrderBy(orderBy, fieldList);
|
|
333
|
+
return `SELECT ARRAY_AGG(CAST(ROW(${fields}) as ROW(${definitions}))${o}) FROM ${lastStageName}\n`;
|
|
332
334
|
}
|
|
333
335
|
sqlSelectAliasAsStruct(alias, fieldList) {
|
|
334
|
-
const fields = fieldList
|
|
336
|
+
const fields = fieldList
|
|
337
|
+
.map(f => `${alias}.${this.sqlQuoteIdentifier(f.rawName)}`)
|
|
338
|
+
.join(', ');
|
|
335
339
|
const definitions = this.buildTypeExpression(fieldList);
|
|
336
|
-
return `CAST(ROW(${fields}) as ROW(${definitions})`;
|
|
340
|
+
return `CAST(ROW(${fields}) as ROW(${definitions}))`;
|
|
337
341
|
}
|
|
338
342
|
sqlConvertToCivilTime(expr, timezone, typeDef) {
|
|
339
343
|
// Trino civil time = TIMESTAMPTZ with query timezone as stored timezone
|
|
@@ -341,13 +345,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
341
345
|
if (typeDef.type === 'timestamp') {
|
|
342
346
|
// TIMESTAMP (UTC wall clock) → TIMESTAMPTZ in query timezone
|
|
343
347
|
return {
|
|
344
|
-
sql: `at_timezone(with_timezone(${expr}, 'UTC'),
|
|
348
|
+
sql: `at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(timezone)})`,
|
|
345
349
|
typeDef: { type: 'timestamptz' },
|
|
346
350
|
};
|
|
347
351
|
}
|
|
348
352
|
// TIMESTAMPTZ → TIMESTAMPTZ in query timezone (same instant, different stored tz)
|
|
349
353
|
return {
|
|
350
|
-
sql: `at_timezone(${expr},
|
|
354
|
+
sql: `at_timezone(${expr}, ${this.sqlTimezoneLiteral(timezone)})`,
|
|
351
355
|
typeDef: { type: 'timestamptz' },
|
|
352
356
|
};
|
|
353
357
|
}
|
|
@@ -392,27 +396,27 @@ ${(0, utils_1.indent)(sql)}
|
|
|
392
396
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
393
397
|
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
394
398
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
395
|
-
return `CAST(at_timezone(with_timezone(${expr}, 'UTC'),
|
|
399
|
+
return `CAST(at_timezone(with_timezone(${expr}, 'UTC'), ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
|
|
396
400
|
}
|
|
397
401
|
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
398
402
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
399
|
-
return `CAST(at_timezone(${expr},
|
|
403
|
+
return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS DATE)`;
|
|
400
404
|
}
|
|
401
405
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
402
406
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
403
|
-
return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP),
|
|
407
|
+
return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
|
|
404
408
|
}
|
|
405
409
|
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
406
410
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
407
|
-
return `with_timezone(CAST(${expr} AS TIMESTAMP),
|
|
411
|
+
return `with_timezone(CAST(${expr} AS TIMESTAMP), ${this.sqlTimezoneLiteral(tz)})`;
|
|
408
412
|
}
|
|
409
413
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
410
414
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
411
|
-
return `CAST(at_timezone(${expr},
|
|
415
|
+
return `CAST(at_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)}) AS TIMESTAMP)`;
|
|
412
416
|
}
|
|
413
417
|
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as being in query timezone
|
|
414
418
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
415
|
-
return `with_timezone(${expr},
|
|
419
|
+
return `with_timezone(${expr}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
// No special handling needed, or no query timezone
|
|
@@ -567,13 +571,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
567
571
|
const tz = timezone || qtz(qi);
|
|
568
572
|
if (tz) {
|
|
569
573
|
// Interpret wall clock time in timezone, convert to UTC wall clock, cast to TIMESTAMP
|
|
570
|
-
return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}',
|
|
574
|
+
return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(tz)}), 'UTC') AS TIMESTAMP)`;
|
|
571
575
|
}
|
|
572
576
|
return `TIMESTAMP '${literal}'`;
|
|
573
577
|
}
|
|
574
578
|
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
575
579
|
// Use with_timezone to create a TIMESTAMP WITH TIME ZONE
|
|
576
|
-
return `with_timezone(TIMESTAMP '${literal}',
|
|
580
|
+
return `with_timezone(TIMESTAMP '${literal}', ${this.sqlTimezoneLiteral(timezone)})`;
|
|
577
581
|
}
|
|
578
582
|
sqlTimeExtractExpr(qi, from) {
|
|
579
583
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
@@ -584,11 +588,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
584
588
|
// Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
|
|
585
589
|
if (from.e.typeDef.type === 'timestamp') {
|
|
586
590
|
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
587
|
-
extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'),
|
|
591
|
+
extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'), ${this.sqlTimezoneLiteral(tz)})`;
|
|
588
592
|
}
|
|
589
593
|
else {
|
|
590
594
|
// TIMESTAMPTZ: convert to query timezone
|
|
591
|
-
extractFrom = `at_timezone(${extractFrom},
|
|
595
|
+
extractFrom = `at_timezone(${extractFrom}, ${this.sqlTimezoneLiteral(tz)})`;
|
|
592
596
|
}
|
|
593
597
|
}
|
|
594
598
|
}
|
|
@@ -628,18 +632,18 @@ class PrestoDialect extends TrinoDialect {
|
|
|
628
632
|
sqlTimestampLiteral(qi, literal, timezone) {
|
|
629
633
|
const tz = timezone || qtz(qi);
|
|
630
634
|
if (tz) {
|
|
631
|
-
return `CAST(TIMESTAMP '${literal} ${tz}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
635
|
+
return `CAST(TIMESTAMP '${literal} ${this.checkedTimezoneName(tz)}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
632
636
|
}
|
|
633
637
|
return `TIMESTAMP '${literal}'`;
|
|
634
638
|
}
|
|
635
639
|
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
636
|
-
return `TIMESTAMP '${literal} ${timezone}'`;
|
|
640
|
+
return `TIMESTAMP '${literal} ${this.checkedTimezoneName(timezone)}'`;
|
|
637
641
|
}
|
|
638
642
|
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
639
643
|
// Presto's AT TIME ZONE operator (not function) produces TIMESTAMPTZ
|
|
640
644
|
// Reinterprets the instant in the target timezone
|
|
641
645
|
return {
|
|
642
|
-
sql: `${expr} AT TIME ZONE
|
|
646
|
+
sql: `${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(timezone)}`,
|
|
643
647
|
typeDef: { type: 'timestamptz' },
|
|
644
648
|
};
|
|
645
649
|
}
|
|
@@ -658,26 +662,26 @@ class PrestoDialect extends TrinoDialect {
|
|
|
658
662
|
if (tz && srcTypeDef && dstTypeDef) {
|
|
659
663
|
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
660
664
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
661
|
-
return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE
|
|
665
|
+
return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
662
666
|
}
|
|
663
667
|
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
664
668
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
665
|
-
return `CAST(${expr} AT TIME ZONE
|
|
669
|
+
return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS DATE)`;
|
|
666
670
|
}
|
|
667
671
|
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
668
672
|
// Presto doesn't have a way to interpret TIMESTAMP in a non-UTC timezone,
|
|
669
673
|
// so we build a TIMESTAMPTZ literal string and cast it
|
|
670
674
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
671
|
-
const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
675
|
+
const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
672
676
|
return `CAST(${tstzLiteral} AS TIMESTAMP)`;
|
|
673
677
|
}
|
|
674
678
|
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
675
679
|
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
676
|
-
return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
680
|
+
return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${this.checkedTimezoneName(tz)}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
677
681
|
}
|
|
678
682
|
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
679
683
|
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
680
|
-
return `CAST(${expr} AT TIME ZONE
|
|
684
|
+
return `CAST(${expr} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)} AS TIMESTAMP)`;
|
|
681
685
|
}
|
|
682
686
|
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as UTC
|
|
683
687
|
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
@@ -701,11 +705,11 @@ class PrestoDialect extends TrinoDialect {
|
|
|
701
705
|
if (from.e.typeDef.type === 'timestamp') {
|
|
702
706
|
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
703
707
|
// Presto uses AT TIME ZONE operator
|
|
704
|
-
extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE
|
|
708
|
+
extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
705
709
|
}
|
|
706
710
|
else {
|
|
707
711
|
// TIMESTAMPTZ: convert to query timezone
|
|
708
|
-
extractFrom = `${extractFrom} AT TIME ZONE
|
|
712
|
+
extractFrom = `${extractFrom} AT TIME ZONE ${this.sqlTimezoneLiteral(tz)}`;
|
|
709
713
|
}
|
|
710
714
|
}
|
|
711
715
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, registerDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, decodeDottedTablePath, validateDottedTablePath, } from './dialect';
|
|
1
|
+
export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, DatabricksDialect, SQLServerDialect, registerDialect, getDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, decodeDottedTablePath, validateDottedTablePath, } from './dialect';
|
|
2
2
|
export type { DialectFieldList, DialectFunctionOverloadDef, QueryInfo, MalloyStandardFunctionImplementations, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, DecodeDottedTablePathResult, DottedTablePathOptions, TablePathEscapeStyle, TablePathSegment, ValidateTablePathResult, } from './dialect';
|
|
3
3
|
export type { QueryRecord, StructDef, TableSourceDef, SQLSourceDef, SourceDef, JoinFieldDef, NamedSourceDefs, MalloyQueryData, DateUnit, ExtractUnit, TimestampUnit, TemporalFieldType, QueryData, QueryValue, Expr, FilterCondition, Argument, Parameter, FieldDef, PipeSegment, QueryFieldDef, IndexFieldDef, TurtleDef, SearchValueMapResult, SearchIndexResult, ModelDef, Query, QueryResult, QueryResultDef, QueryRunStats, QueryScalar, NamedQueryDef, NamedModelObject, ExpressionType, FunctionDef, FunctionOverloadDef, FunctionParameterDef, ExpressionValueType, TypeDesc, FunctionParamTypeDesc, DocumentLocation, DocumentRange, DocumentPosition, Sampling, BasicAtomicTypeDef, BasicAtomicDef, AtomicTypeDef, AtomicFieldDef, ArrayDef, ArrayTypeDef, RecordTypeDef, RepeatedRecordTypeDef, RecordDef, RepeatedRecordDef, RecordLiteralNode, StringLiteralNode, ArrayLiteralNode, SourceComponentInfo, DateLiteralNode, TimestampLiteralNode, TimestamptzLiteralNode, TimeLiteralExpr, TypecastExpr, BuildID, BuildManifest, BuildManifestEntry, GivenValue, VirtualMap, } from './model';
|
|
4
4
|
export { isSourceDef, isAtomic, isBasicAtomic, isCompoundArrayData, isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, isRepeatedRecord, isBasicArray, mkArrayDef, mkFieldDef, expressionIsAggregate, expressionIsAnalytic, expressionIsCalculation, expressionIsScalar, expressionIsUngroupedAggregate, indent, composeSQLExpr, isTimestampUnit, isDateUnit, constantExprToSQL, } from './model';
|
package/dist/index.js
CHANGED
|
@@ -37,9 +37,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.
|
|
41
|
-
exports.
|
|
42
|
-
exports.makeDigest = void 0;
|
|
40
|
+
exports.malloyToQuery = exports.constantExprToSQL = exports.isDateUnit = exports.isTimestampUnit = exports.composeSQLExpr = exports.indent = exports.expressionIsUngroupedAggregate = exports.expressionIsScalar = exports.expressionIsCalculation = exports.expressionIsAnalytic = exports.expressionIsAggregate = exports.mkFieldDef = exports.mkArrayDef = exports.isBasicArray = exports.isRepeatedRecord = exports.isSamplingRows = exports.isSamplingPercent = exports.isSamplingEnable = exports.isJoinedSource = exports.isJoined = exports.isCompoundArrayData = exports.isBasicAtomic = exports.isAtomic = exports.isSourceDef = exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.Dialect = exports.spread = exports.literal = exports.variadicParam = exports.param = exports.makeParam = exports.sql = exports.maxScalar = exports.minAggregate = exports.anyExprType = exports.minScalar = exports.overload = exports.qtz = exports.arg = exports.getDialect = exports.registerDialect = exports.SQLServerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.SnowflakeDialect = exports.PostgresDialect = exports.TrinoDialect = exports.StandardSQLDialect = exports.DuckDBDialect = void 0;
|
|
41
|
+
exports.PersistSource = exports.annotationToTaglines = exports.annotationToTag = exports.tagFromAnnotations = exports.annotationsForRoute = exports.payloadOf = exports.routeOf = exports.RoutedNote = exports.Annotations = exports.sqlKey = exports.API = exports.sourceDefToSourceInfo = exports.modelDefToModelInfo = exports.toAsyncGenerator = exports.createConnectionsFromConfig = exports.getRegisteredConnectionTypes = exports.getConnectionTypeDisplayName = exports.getConnectionProperties = exports.registerConnectionType = exports.discoverConfig = exports.defaultConfigOverlays = exports.contextOverlay = exports.envOverlay = exports.MalloyConfig = exports.Manifest = exports.CacheManager = exports.InMemoryModelCache = exports.Explore = exports.DataWriter = exports.Parse = exports.JSONWriter = exports.CSVWriter = exports.QueryMaterializer = exports.Result = exports.PreparedResult = exports.TimestampTimeframe = exports.DateTimeframe = exports.SourceRelationship = exports.JoinRelationship = exports.MalloyError = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.AtomicFieldType = exports.Runtime = exports.Malloy = exports.Model = exports.MalloyTranslator = void 0;
|
|
42
|
+
exports.makeDigest = exports.Reference = exports.EMPTY_BUILD_MANIFEST = void 0;
|
|
43
43
|
var dialect_1 = require("./dialect");
|
|
44
44
|
Object.defineProperty(exports, "DuckDBDialect", { enumerable: true, get: function () { return dialect_1.DuckDBDialect; } });
|
|
45
45
|
Object.defineProperty(exports, "StandardSQLDialect", { enumerable: true, get: function () { return dialect_1.StandardSQLDialect; } });
|
|
@@ -48,7 +48,9 @@ Object.defineProperty(exports, "PostgresDialect", { enumerable: true, get: funct
|
|
|
48
48
|
Object.defineProperty(exports, "SnowflakeDialect", { enumerable: true, get: function () { return dialect_1.SnowflakeDialect; } });
|
|
49
49
|
Object.defineProperty(exports, "MySQLDialect", { enumerable: true, get: function () { return dialect_1.MySQLDialect; } });
|
|
50
50
|
Object.defineProperty(exports, "DatabricksDialect", { enumerable: true, get: function () { return dialect_1.DatabricksDialect; } });
|
|
51
|
+
Object.defineProperty(exports, "SQLServerDialect", { enumerable: true, get: function () { return dialect_1.SQLServerDialect; } });
|
|
51
52
|
Object.defineProperty(exports, "registerDialect", { enumerable: true, get: function () { return dialect_1.registerDialect; } });
|
|
53
|
+
Object.defineProperty(exports, "getDialect", { enumerable: true, get: function () { return dialect_1.getDialect; } });
|
|
52
54
|
Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return dialect_1.arg; } });
|
|
53
55
|
Object.defineProperty(exports, "qtz", { enumerable: true, get: function () { return dialect_1.qtz; } });
|
|
54
56
|
Object.defineProperty(exports, "overload", { enumerable: true, get: function () { return dialect_1.overload; } });
|
|
@@ -52,7 +52,6 @@ class NamedSource extends source_1.Source {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
modelStruct() {
|
|
55
|
-
var _a;
|
|
56
55
|
const modelEnt = this.modelEntry(this.ref);
|
|
57
56
|
const entry = modelEnt === null || modelEnt === void 0 ? void 0 : modelEnt.entry;
|
|
58
57
|
if (!entry) {
|
|
@@ -89,7 +88,6 @@ class NamedSource extends source_1.Source {
|
|
|
89
88
|
return;
|
|
90
89
|
}
|
|
91
90
|
else {
|
|
92
|
-
(_a = this.document()) === null || _a === void 0 ? void 0 : _a.checkExperimentalDialect(this, entry.dialect);
|
|
93
91
|
if ((0, malloy_types_1.isSourceDef)(entry)) {
|
|
94
92
|
// This struct is created as an unmodified reference to `entry`. Mark it
|
|
95
93
|
// with entry's own identity; the modification path (DynamicSpace) clears
|
|
@@ -81,7 +81,7 @@ class RefinedSource extends source_1.Source {
|
|
|
81
81
|
filters.push(el);
|
|
82
82
|
}
|
|
83
83
|
else if (el instanceof timezone_statement_1.TimezoneStatement) {
|
|
84
|
-
newTimezone = el.
|
|
84
|
+
newTimezone = el.timezoneName();
|
|
85
85
|
}
|
|
86
86
|
else {
|
|
87
87
|
errTo.logError('unexpected-source-property', `Unexpected source property: '${errTo.elementType}'`);
|
|
@@ -19,12 +19,13 @@ class SQLSource extends source_1.Source {
|
|
|
19
19
|
this.connectionNameInvalid = false;
|
|
20
20
|
}
|
|
21
21
|
sqlSourceRequest(doc) {
|
|
22
|
+
var _a;
|
|
22
23
|
const partialModel = this.select.containsQueries
|
|
23
24
|
? doc.modelDef()
|
|
24
25
|
: undefined;
|
|
25
26
|
const [valid, phrases] = this.select.sqlPhrases(this.connectionName.refString);
|
|
26
27
|
if (valid) {
|
|
27
|
-
return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel);
|
|
28
|
+
return (0, sql_block_1.getSourceRequest)(phrases, this.connectionName.refString, partialModel, (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.virtualMap);
|
|
28
29
|
}
|
|
29
30
|
return undefined;
|
|
30
31
|
}
|
|
@@ -77,9 +78,6 @@ class SQLSource extends source_1.Source {
|
|
|
77
78
|
compileSQL: sql,
|
|
78
79
|
};
|
|
79
80
|
}
|
|
80
|
-
else if (lookup.status === 'present') {
|
|
81
|
-
doc.checkExperimentalDialect(this, lookup.value.dialect);
|
|
82
|
-
}
|
|
83
81
|
}
|
|
84
82
|
getSourceDef() {
|
|
85
83
|
var _a;
|
|
@@ -12,7 +12,7 @@ const error_factory_1 = require("../error-factory");
|
|
|
12
12
|
const dialect_1 = require("../../../dialect");
|
|
13
13
|
class TableSource extends source_1.Source {
|
|
14
14
|
getSourceDef() {
|
|
15
|
-
var _a, _b
|
|
15
|
+
var _a, _b;
|
|
16
16
|
const info = this.getTableInfo();
|
|
17
17
|
if (info === undefined) {
|
|
18
18
|
return error_factory_1.ErrorFactory.structDef;
|
|
@@ -36,7 +36,6 @@ class TableSource extends source_1.Source {
|
|
|
36
36
|
let msg = `Schema read failure for table '${tablePath}' for connection '${connectionName}'`;
|
|
37
37
|
if (tableDefEntry) {
|
|
38
38
|
if (tableDefEntry.status === 'present') {
|
|
39
|
-
(_c = this.document()) === null || _c === void 0 ? void 0 : _c.checkExperimentalDialect(this, tableDefEntry.value.dialect);
|
|
40
39
|
tableDefEntry.value.location = this.location;
|
|
41
40
|
tableDefEntry.value.fields.forEach(field => {
|
|
42
41
|
field.location = this.location;
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
import type { ExprString } from '../expressions/expr-string';
|
|
1
2
|
import type { QueryBuilder } from '../types/query-builder';
|
|
2
3
|
import { MalloyElement } from '../types/malloy-element';
|
|
3
4
|
import type { QueryPropertyInterface } from '../types/query-property-interface';
|
|
4
5
|
export declare class TimezoneStatement extends MalloyElement implements QueryPropertyInterface {
|
|
5
|
-
readonly
|
|
6
|
+
private readonly timezone;
|
|
6
7
|
elementType: string;
|
|
7
8
|
forceQueryClass: undefined;
|
|
8
9
|
queryRefinementStage: undefined;
|
|
9
|
-
constructor(
|
|
10
|
-
|
|
10
|
+
constructor(timezone: ExprString);
|
|
11
|
+
timezoneName(): string | undefined;
|
|
11
12
|
queryExecute(executeFor: QueryBuilder): void;
|
|
12
13
|
}
|
|
@@ -5,27 +5,29 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.TimezoneStatement = void 0;
|
|
8
|
+
const dialect_1 = require("../../../dialect/dialect");
|
|
8
9
|
const malloy_element_1 = require("../types/malloy-element");
|
|
9
|
-
const luxon_1 = require("luxon");
|
|
10
10
|
class TimezoneStatement extends malloy_element_1.MalloyElement {
|
|
11
|
-
constructor(
|
|
12
|
-
super();
|
|
13
|
-
this.
|
|
11
|
+
constructor(timezone) {
|
|
12
|
+
super({ timezone });
|
|
13
|
+
this.timezone = timezone;
|
|
14
14
|
this.elementType = 'timezone';
|
|
15
15
|
this.forceQueryClass = undefined;
|
|
16
16
|
this.queryRefinementStage = undefined;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
catch {
|
|
24
|
-
return false;
|
|
18
|
+
timezoneName() {
|
|
19
|
+
const name = this.timezone.value;
|
|
20
|
+
if (!dialect_1.Dialect.isSafeTimezoneName(name)) {
|
|
21
|
+
this.logError('invalid-timezone', { timezone: name }, { at: this.timezone.location });
|
|
22
|
+
return undefined;
|
|
25
23
|
}
|
|
24
|
+
return name;
|
|
26
25
|
}
|
|
27
26
|
queryExecute(executeFor) {
|
|
28
|
-
|
|
27
|
+
const timezone = this.timezoneName();
|
|
28
|
+
if (timezone !== undefined) {
|
|
29
|
+
executeFor.resultFS.setTimezone(timezone);
|
|
30
|
+
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
31
33
|
exports.TimezoneStatement = TimezoneStatement;
|
|
@@ -152,6 +152,10 @@ class ImportStatement extends malloy_element_1.ListOf {
|
|
|
152
152
|
}
|
|
153
153
|
const importMe = { ...sourceEntry };
|
|
154
154
|
importMe.as = dstName;
|
|
155
|
+
const dialect = (0, source_def_utils_1.dialectOfEntry)(importMe, importedModel);
|
|
156
|
+
if (dialect !== undefined) {
|
|
157
|
+
trans.checkExperimentalDialect(dialect, this.location);
|
|
158
|
+
}
|
|
155
159
|
doc.setEntry(dstName, { entry: importMe, exported: false });
|
|
156
160
|
// Every source the walk touched, routes included — not just the
|
|
157
161
|
// persistent ones. A `#@ -persist` wrapper that adds a join is
|
|
@@ -152,13 +152,6 @@ export declare class Document extends MalloyElement implements NameSpace {
|
|
|
152
152
|
modelDef(): ModelDef;
|
|
153
153
|
getEntry(str: string): ModelEntry | undefined;
|
|
154
154
|
setEntry(str: string, ent: ModelEntry): void;
|
|
155
|
-
/**
|
|
156
|
-
* Return an error message if this dialect is the first reference to this particular
|
|
157
|
-
* dialect, and the dialect is marked as experimental, and we are not running tests.
|
|
158
|
-
* @param dialect The dialect name
|
|
159
|
-
* @returns The error message or undefined
|
|
160
|
-
*/
|
|
161
|
-
checkExperimentalDialect(me: MalloyElement, dialect: string): void;
|
|
162
155
|
private readonly dialectNameSpaces;
|
|
163
156
|
getDialectNamespace(dialectName: string): NameSpace | undefined;
|
|
164
157
|
}
|
|
@@ -656,9 +656,6 @@ class Document extends MalloyElement {
|
|
|
656
656
|
if (this.globalNameSpace.getEntry(str) !== undefined) {
|
|
657
657
|
this.logError('name-conflict-with-global', `Cannot redefine '${str}', which is in global namespace`);
|
|
658
658
|
}
|
|
659
|
-
if ((0, malloy_types_1.isSourceDef)(ent.entry)) {
|
|
660
|
-
this.checkExperimentalDialect(this, ent.entry.dialect);
|
|
661
|
-
}
|
|
662
659
|
// Track if the model was modified after initialization
|
|
663
660
|
if (this.didInitModel) {
|
|
664
661
|
this.modelWasModified = true;
|
|
@@ -675,21 +672,6 @@ class Document extends MalloyElement {
|
|
|
675
672
|
};
|
|
676
673
|
}
|
|
677
674
|
}
|
|
678
|
-
/**
|
|
679
|
-
* Return an error message if this dialect is the first reference to this particular
|
|
680
|
-
* dialect, and the dialect is marked as experimental, and we are not running tests.
|
|
681
|
-
* @param dialect The dialect name
|
|
682
|
-
* @returns The error message or undefined
|
|
683
|
-
*/
|
|
684
|
-
checkExperimentalDialect(me, dialect) {
|
|
685
|
-
const t = this.translator();
|
|
686
|
-
if (t &&
|
|
687
|
-
t.firstReferenceToDialect(dialect) &&
|
|
688
|
-
(0, dialect_1.getDialect)(dialect).experimental &&
|
|
689
|
-
!t.experimentalDialectEnabled(dialect)) {
|
|
690
|
-
me.logError('experimental-dialect-not-enabled', { dialect });
|
|
691
|
-
}
|
|
692
|
-
}
|
|
693
675
|
getDialectNamespace(dialectName) {
|
|
694
676
|
if (this.dialectNameSpaces.has(dialectName)) {
|
|
695
677
|
return this.dialectNameSpaces.get(dialectName);
|
|
@@ -407,9 +407,11 @@ function expandRefUsage(seed, fields, alwaysJoinNames = []) {
|
|
|
407
407
|
// given usage into the accumulator. Used both in-loop (joins reached
|
|
408
408
|
// via reference paths) and upfront (always-on segment-level joins).
|
|
409
409
|
function activateJoin(joinDef, joinPath) {
|
|
410
|
+
if (!(0, malloy_types_1.isJoined)(joinDef))
|
|
411
|
+
return;
|
|
410
412
|
const joinKey = (0, utils_2.pathToKey)('join', joinPath);
|
|
411
413
|
const thisDep = getJoin(activeJoinGraph, joinKey, joinPath);
|
|
412
|
-
if (
|
|
414
|
+
if (thisDep.checked)
|
|
413
415
|
return;
|
|
414
416
|
thisDep.checked = true;
|
|
415
417
|
const joinFieldUsage = getJoinFieldUsage(joinDef, joinPath);
|
|
@@ -462,8 +464,13 @@ function expandRefUsage(seed, fields, alwaysJoinNames = []) {
|
|
|
462
464
|
ungroupings.push(...joinedUngroupings(refPath, def.ungroupings));
|
|
463
465
|
}
|
|
464
466
|
}
|
|
465
|
-
//
|
|
466
|
-
|
|
467
|
+
// Track the joins along the path. A path which ends at a join and carries
|
|
468
|
+
// a unique key requirement is an aggregate computed over that join's rows,
|
|
469
|
+
// which should then include the join's on-clause fields in the field usage.
|
|
470
|
+
// A path which merely names a join-typed value does not.
|
|
471
|
+
const endsInTraversedJoin = reference.uniqueKeyRequirement !== undefined;
|
|
472
|
+
const joinDepth = reference.path.length - (endsInTraversedJoin ? 0 : 1);
|
|
473
|
+
for (let joinLen = 1; joinLen <= joinDepth; joinLen++) {
|
|
467
474
|
const joinPath = reference.path.slice(0, joinLen);
|
|
468
475
|
const joinDef = inNamespace(joinPath, fieldNameSpace);
|
|
469
476
|
if (!joinDef)
|
|
@@ -803,14 +803,8 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
803
803
|
return this.visitTimezoneStatement(cx.timezoneStatement());
|
|
804
804
|
}
|
|
805
805
|
visitTimezoneStatement(cx) {
|
|
806
|
-
const timezone = this.getPlainStringFrom(cx);
|
|
807
|
-
|
|
808
|
-
if (!timezoneStatement.isValid) {
|
|
809
|
-
this.astError(timezoneStatement, 'invalid-timezone', {
|
|
810
|
-
timezone: timezoneStatement.tz,
|
|
811
|
-
});
|
|
812
|
-
}
|
|
813
|
-
return this.astAt(timezoneStatement, cx);
|
|
806
|
+
const timezone = this.astAt(new ast.ExprString(this.getPlainStringFrom(cx)), cx.string());
|
|
807
|
+
return this.astAt(new ast.TimezoneStatement(timezone), cx);
|
|
814
808
|
}
|
|
815
809
|
visitQueryProperties(pcx) {
|
|
816
810
|
const qProps = this.only(pcx.queryStatement().map(qcx => this.astAt(this.visit(qcx), qcx)), x => ast.isQueryProperty(x) && x, 'query statement');
|