@malloydata/malloy 0.0.323 → 0.0.325
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/CONTEXT.md +57 -0
- package/dist/api/core.js +2 -0
- package/dist/api/util.js +16 -24
- package/dist/dialect/dialect.d.ts +113 -23
- package/dist/dialect/dialect.js +83 -13
- package/dist/dialect/duckdb/duckdb.d.ts +1 -4
- package/dist/dialect/duckdb/duckdb.js +13 -17
- package/dist/dialect/mysql/mysql.d.ts +9 -4
- package/dist/dialect/mysql/mysql.js +18 -11
- package/dist/dialect/pg_impl.d.ts +11 -2
- package/dist/dialect/pg_impl.js +79 -15
- package/dist/dialect/postgres/postgres.d.ts +1 -4
- package/dist/dialect/postgres/postgres.js +6 -18
- package/dist/dialect/snowflake/snowflake.d.ts +10 -4
- package/dist/dialect/snowflake/snowflake.js +80 -31
- package/dist/dialect/standardsql/standardsql.d.ts +9 -4
- package/dist/dialect/standardsql/standardsql.js +21 -19
- package/dist/dialect/tiny_parser.js +1 -1
- package/dist/dialect/trino/trino.d.ts +19 -6
- package/dist/dialect/trino/trino.js +163 -31
- package/dist/index.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-granular-time.js +26 -8
- package/dist/lang/ast/expressions/expr-props.d.ts +24 -0
- package/dist/lang/ast/expressions/for-range.d.ts +1 -1
- package/dist/lang/ast/expressions/for-range.js +5 -4
- package/dist/lang/ast/expressions/time-literal.d.ts +9 -7
- package/dist/lang/ast/expressions/time-literal.js +43 -50
- package/dist/lang/ast/query-items/field-declaration.js +1 -2
- package/dist/lang/ast/time-utils.js +1 -1
- package/dist/lang/ast/typedesc-utils.d.ts +1 -0
- package/dist/lang/ast/typedesc-utils.js +14 -1
- package/dist/lang/ast/types/expression-def.js +1 -1
- package/dist/lang/ast/types/granular-result.js +2 -1
- package/dist/lang/composite-source-utils.js +1 -1
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +76 -75
- package/dist/lang/lib/Malloy/MalloyLexer.js +1252 -1243
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +77 -75
- package/dist/lang/lib/Malloy/MalloyParser.js +515 -510
- package/dist/lang/malloy-to-stable-query.js +13 -14
- package/dist/lang/test/expr-to-str.js +5 -1
- package/dist/malloy.d.ts +3 -2
- package/dist/malloy.js +6 -0
- package/dist/model/field_instance.js +1 -1
- package/dist/model/filter_compilers.d.ts +2 -1
- package/dist/model/filter_compilers.js +8 -5
- package/dist/model/malloy_types.d.ts +31 -9
- package/dist/model/malloy_types.js +49 -6
- package/dist/model/query_node.d.ts +2 -2
- package/dist/model/query_node.js +1 -0
- package/dist/model/query_query.js +15 -3
- package/dist/to_stable.js +13 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -6
|
@@ -67,9 +67,6 @@ const trinoToMalloyTypes = {
|
|
|
67
67
|
'FLOAT64': {type: 'number', numberType: 'float'},
|
|
68
68
|
'NUMERIC': {type: 'number', numberType: 'float'},
|
|
69
69
|
'BIGNUMERIC': {type: 'number', numberType: 'float'},
|
|
70
|
-
'TIMESTAMP': {type: 'timestamp'},
|
|
71
|
-
'BOOLEAN': {type: 'boolean'},
|
|
72
|
-
'BOOL': {type: 'boolean'},
|
|
73
70
|
'JSON': {type: 'json'},*/
|
|
74
71
|
// TODO (https://cloud.google.com/bigquery/docs/reference/rest/v2/tables#tablefieldschema):
|
|
75
72
|
// BYTES
|
|
@@ -330,10 +327,29 @@ ${(0, utils_1.indent)(sql)}
|
|
|
330
327
|
const definitions = this.buildTypeExpression(fieldList);
|
|
331
328
|
return `CAST(ROW(${fields}) as ROW(${definitions})`;
|
|
332
329
|
}
|
|
333
|
-
sqlConvertToCivilTime(expr, timezone) {
|
|
334
|
-
|
|
330
|
+
sqlConvertToCivilTime(expr, timezone, typeDef) {
|
|
331
|
+
// Trino civil time = TIMESTAMPTZ with query timezone as stored timezone
|
|
332
|
+
// Operations (extract, truncate, etc.) happen in the stored timezone
|
|
333
|
+
if (typeDef.type === 'timestamp') {
|
|
334
|
+
// TIMESTAMP (UTC wall clock) → TIMESTAMPTZ in query timezone
|
|
335
|
+
return {
|
|
336
|
+
sql: `at_timezone(with_timezone(${expr}, 'UTC'), '${timezone}')`,
|
|
337
|
+
typeDef: { type: 'timestamptz' },
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
// TIMESTAMPTZ → TIMESTAMPTZ in query timezone (same instant, different stored tz)
|
|
341
|
+
return {
|
|
342
|
+
sql: `at_timezone(${expr}, '${timezone}')`,
|
|
343
|
+
typeDef: { type: 'timestamptz' },
|
|
344
|
+
};
|
|
335
345
|
}
|
|
336
|
-
sqlConvertFromCivilTime(expr, _timezone) {
|
|
346
|
+
sqlConvertFromCivilTime(expr, _timezone, destTypeDef) {
|
|
347
|
+
// From civil TIMESTAMPTZ (in query timezone) to destination type
|
|
348
|
+
if (destTypeDef.type === 'timestamptz') {
|
|
349
|
+
// Already TIMESTAMPTZ, keep as-is
|
|
350
|
+
return expr;
|
|
351
|
+
}
|
|
352
|
+
// To TIMESTAMP: convert to UTC and cast to plain TIMESTAMP
|
|
337
353
|
return `CAST(at_timezone(${expr}, 'UTC') AS TIMESTAMP)`;
|
|
338
354
|
}
|
|
339
355
|
sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
|
|
@@ -361,16 +377,37 @@ ${(0, utils_1.indent)(sql)}
|
|
|
361
377
|
return `DATE_ADD('${offsetUnit}', ${n}, ${expr})`;
|
|
362
378
|
}
|
|
363
379
|
sqlCast(qi, cast) {
|
|
364
|
-
const {
|
|
380
|
+
const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
365
381
|
const tz = qtz(qi);
|
|
366
382
|
const expr = cast.e.sql || '';
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
383
|
+
// Timezone-aware casts when query timezone is set
|
|
384
|
+
if (tz && srcTypeDef && dstTypeDef) {
|
|
385
|
+
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
386
|
+
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
387
|
+
return `CAST(at_timezone(with_timezone(${expr}, 'UTC'), '${tz}') AS DATE)`;
|
|
388
|
+
}
|
|
389
|
+
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
390
|
+
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
391
|
+
return `CAST(at_timezone(${expr}, '${tz}') AS DATE)`;
|
|
392
|
+
}
|
|
393
|
+
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
394
|
+
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
395
|
+
return `CAST(at_timezone(with_timezone(CAST(${expr} AS TIMESTAMP), '${tz}'), 'UTC') AS TIMESTAMP)`;
|
|
396
|
+
}
|
|
397
|
+
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
398
|
+
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
399
|
+
return `with_timezone(CAST(${expr} AS TIMESTAMP), '${tz}')`;
|
|
400
|
+
}
|
|
401
|
+
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
402
|
+
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
403
|
+
return `CAST(at_timezone(${expr}, '${tz}') AS TIMESTAMP)`;
|
|
404
|
+
}
|
|
405
|
+
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as being in query timezone
|
|
406
|
+
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
407
|
+
return `with_timezone(${expr}, '${tz}')`;
|
|
408
|
+
}
|
|
373
409
|
}
|
|
410
|
+
// No special handling needed, or no query timezone
|
|
374
411
|
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
375
412
|
const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
|
|
376
413
|
return `${castFunc}(${expr} AS ${dstSQLType})`;
|
|
@@ -446,6 +483,8 @@ ${(0, utils_1.indent)(sql)}
|
|
|
446
483
|
return malloyType.numberType === 'integer' ? 'BIGINT' : 'DOUBLE';
|
|
447
484
|
case 'string':
|
|
448
485
|
return 'VARCHAR';
|
|
486
|
+
case 'timestamptz':
|
|
487
|
+
return 'TIMESTAMP WITH TIME ZONE';
|
|
449
488
|
case 'record': {
|
|
450
489
|
const typeSpec = [];
|
|
451
490
|
for (const f of malloyType.fields) {
|
|
@@ -475,7 +514,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
475
514
|
}
|
|
476
515
|
sqlTypeToMalloyType(sqlType) {
|
|
477
516
|
var _a, _b, _c;
|
|
478
|
-
const
|
|
517
|
+
const matchType = sqlType.toLowerCase();
|
|
518
|
+
if (matchType.startsWith('timestamp with time zone')) {
|
|
519
|
+
return { type: 'timestamptz' };
|
|
520
|
+
}
|
|
521
|
+
const baseSqlType = (_b = (_a = matchType.match(/^\w+/)) === null || _a === void 0 ? void 0 : _a.at(0)) !== null && _b !== void 0 ? _b : matchType;
|
|
479
522
|
return ((_c = trinoToMalloyTypes[baseSqlType]) !== null && _c !== void 0 ? _c : {
|
|
480
523
|
type: 'sql native',
|
|
481
524
|
rawType: sqlType,
|
|
@@ -502,24 +545,36 @@ ${(0, utils_1.indent)(sql)}
|
|
|
502
545
|
// Angle Brackets: ARRAY<INT64>
|
|
503
546
|
return sqlType.match(/^[A-Za-z\s(),<>0-9]*$/) !== null;
|
|
504
547
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
const tz =
|
|
548
|
+
sqlDateLiteral(_qi, literal) {
|
|
549
|
+
return `DATE '${literal}'`;
|
|
550
|
+
}
|
|
551
|
+
sqlTimestampLiteral(qi, literal, timezone) {
|
|
552
|
+
const tz = timezone || qtz(qi);
|
|
510
553
|
if (tz) {
|
|
511
554
|
// Interpret wall clock time in timezone, convert to UTC wall clock, cast to TIMESTAMP
|
|
512
|
-
return `CAST(at_timezone(with_timezone(TIMESTAMP '${
|
|
555
|
+
return `CAST(at_timezone(with_timezone(TIMESTAMP '${literal}', '${tz}'), 'UTC') AS TIMESTAMP)`;
|
|
513
556
|
}
|
|
514
|
-
return `TIMESTAMP '${
|
|
557
|
+
return `TIMESTAMP '${literal}'`;
|
|
558
|
+
}
|
|
559
|
+
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
560
|
+
// Use with_timezone to create a TIMESTAMP WITH TIME ZONE
|
|
561
|
+
return `with_timezone(TIMESTAMP '${literal}', '${timezone}')`;
|
|
515
562
|
}
|
|
516
563
|
sqlTimeExtractExpr(qi, from) {
|
|
517
564
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
518
565
|
let extractFrom = from.e.sql || '';
|
|
519
|
-
if (malloy_types_1.TD.
|
|
566
|
+
if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
|
|
520
567
|
const tz = qtz(qi);
|
|
521
568
|
if (tz) {
|
|
522
|
-
|
|
569
|
+
// Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
|
|
570
|
+
if (from.e.typeDef.type === 'timestamp') {
|
|
571
|
+
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
572
|
+
extractFrom = `at_timezone(with_timezone(${extractFrom}, 'UTC'), '${tz}')`;
|
|
573
|
+
}
|
|
574
|
+
else {
|
|
575
|
+
// TIMESTAMPTZ: convert to query timezone
|
|
576
|
+
extractFrom = `at_timezone(${extractFrom}, '${tz}')`;
|
|
577
|
+
}
|
|
523
578
|
}
|
|
524
579
|
}
|
|
525
580
|
const extracted = `EXTRACT(${pgUnits} FROM ${extractFrom})`;
|
|
@@ -552,19 +607,96 @@ class PrestoDialect extends TrinoDialect {
|
|
|
552
607
|
sqlGenerateUUID() {
|
|
553
608
|
return 'CAST(UUID() AS VARCHAR)';
|
|
554
609
|
}
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const tz =
|
|
610
|
+
sqlDateLiteral(_qi, literal) {
|
|
611
|
+
return `DATE '${literal}'`;
|
|
612
|
+
}
|
|
613
|
+
sqlTimestampLiteral(qi, literal, timezone) {
|
|
614
|
+
const tz = timezone || qtz(qi);
|
|
560
615
|
if (tz) {
|
|
561
|
-
return `CAST(TIMESTAMP '${
|
|
616
|
+
return `CAST(TIMESTAMP '${literal} ${tz}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
562
617
|
}
|
|
563
|
-
return `TIMESTAMP '${
|
|
618
|
+
return `TIMESTAMP '${literal}'`;
|
|
619
|
+
}
|
|
620
|
+
sqlTimestamptzLiteral(_qi, literal, timezone) {
|
|
621
|
+
return `TIMESTAMP '${literal} ${timezone}'`;
|
|
564
622
|
}
|
|
565
|
-
|
|
623
|
+
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
624
|
+
// Presto's AT TIME ZONE operator (not function) produces TIMESTAMPTZ
|
|
625
|
+
// Reinterprets the instant in the target timezone
|
|
626
|
+
return {
|
|
627
|
+
sql: `${expr} AT TIME ZONE '${timezone}'`,
|
|
628
|
+
typeDef: { type: 'timestamptz' },
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
sqlConvertFromCivilTime(expr, _timezone, destTypeDef) {
|
|
632
|
+
if (destTypeDef.type === 'timestamptz') {
|
|
633
|
+
return expr;
|
|
634
|
+
}
|
|
566
635
|
return `CAST(${expr} AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
567
636
|
}
|
|
637
|
+
sqlCast(qi, cast) {
|
|
638
|
+
const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
639
|
+
const tz = qtz(qi);
|
|
640
|
+
const expr = cast.e.sql || '';
|
|
641
|
+
// Timezone-aware casts when query timezone is set
|
|
642
|
+
// Presto uses AT TIME ZONE operator instead of Trino's with_timezone/at_timezone functions
|
|
643
|
+
if (tz && srcTypeDef && dstTypeDef) {
|
|
644
|
+
// TIMESTAMP → DATE: interpret as UTC, convert to query timezone
|
|
645
|
+
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
646
|
+
return `CAST((${expr} AT TIME ZONE 'UTC') AT TIME ZONE '${tz}' AS DATE)`;
|
|
647
|
+
}
|
|
648
|
+
// TIMESTAMPTZ → DATE: convert to query timezone
|
|
649
|
+
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
650
|
+
return `CAST(${expr} AT TIME ZONE '${tz}' AS DATE)`;
|
|
651
|
+
}
|
|
652
|
+
// DATE → TIMESTAMP: interpret date in query timezone, return UTC wall clock
|
|
653
|
+
// Presto doesn't have a way to interpret TIMESTAMP in a non-UTC timezone,
|
|
654
|
+
// so we build a TIMESTAMPTZ literal string and cast it
|
|
655
|
+
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
656
|
+
const tstzLiteral = `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
657
|
+
return `CAST(${tstzLiteral} AS TIMESTAMP)`;
|
|
658
|
+
}
|
|
659
|
+
// DATE → TIMESTAMPTZ: interpret date in query timezone
|
|
660
|
+
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
661
|
+
return `CAST(CAST(${expr} AS VARCHAR) || ' 00:00:00 ${tz}' AS TIMESTAMP WITH TIME ZONE)`;
|
|
662
|
+
}
|
|
663
|
+
// TIMESTAMPTZ → TIMESTAMP: convert to query timezone wall clock
|
|
664
|
+
if (malloy_types_1.TD.isTimestamptz(srcTypeDef) && malloy_types_1.TD.isTimestamp(dstTypeDef)) {
|
|
665
|
+
return `CAST(${expr} AT TIME ZONE '${tz}' AS TIMESTAMP)`;
|
|
666
|
+
}
|
|
667
|
+
// TIMESTAMP → TIMESTAMPTZ: interpret TIMESTAMP as UTC
|
|
668
|
+
if (malloy_types_1.TD.isTimestamp(srcTypeDef) && malloy_types_1.TD.isTimestamptz(dstTypeDef)) {
|
|
669
|
+
return `${expr} AT TIME ZONE 'UTC'`;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// No special handling needed, or no query timezone
|
|
673
|
+
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
674
|
+
const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
|
|
675
|
+
return `${castFunc}(${expr} AS ${dstSQLType})`;
|
|
676
|
+
}
|
|
677
|
+
return expr;
|
|
678
|
+
}
|
|
679
|
+
sqlTimeExtractExpr(qi, from) {
|
|
680
|
+
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
681
|
+
let extractFrom = from.e.sql || '';
|
|
682
|
+
if (malloy_types_1.TD.isAnyTimestamp(from.e.typeDef)) {
|
|
683
|
+
const tz = qtz(qi);
|
|
684
|
+
if (tz) {
|
|
685
|
+
// Convert both TIMESTAMP and TIMESTAMPTZ to query timezone for extraction
|
|
686
|
+
if (from.e.typeDef.type === 'timestamp') {
|
|
687
|
+
// TIMESTAMP: interpret as UTC, convert to query timezone
|
|
688
|
+
// Presto uses AT TIME ZONE operator
|
|
689
|
+
extractFrom = `(${extractFrom} AT TIME ZONE 'UTC') AT TIME ZONE '${tz}'`;
|
|
690
|
+
}
|
|
691
|
+
else {
|
|
692
|
+
// TIMESTAMPTZ: convert to query timezone
|
|
693
|
+
extractFrom = `${extractFrom} AT TIME ZONE '${tz}'`;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const extracted = `EXTRACT(${pgUnits} FROM ${extractFrom})`;
|
|
698
|
+
return from.units === 'day_of_week' ? `mod(${extracted}+1,7)` : extracted;
|
|
699
|
+
}
|
|
568
700
|
sqlUnnestAlias(source, alias, _fieldList, needDistinctKey, isArray, _isInNestedPipeline) {
|
|
569
701
|
if (isArray) {
|
|
570
702
|
if (needDistinctKey) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { DuckDBDialect, StandardSQLDialect, TrinoDialect, PostgresDialect, SnowflakeDialect, MySQLDialect, registerDialect, arg, qtz, overload, minScalar, anyExprType, minAggregate, maxScalar, sql, makeParam, param, variadicParam, literal, spread, Dialect, TinyParser, } from './dialect';
|
|
2
2
|
export type { DialectFieldList, DialectFunctionOverloadDef, QueryInfo, MalloyStandardFunctionImplementations, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, TinyToken, } from './dialect';
|
|
3
|
-
export type { QueryDataRow, 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, NamedQuery, NamedModelObject, ExpressionType, FunctionDef, FunctionOverloadDef, FunctionParameterDef, ExpressionValueType, TypeDesc, FunctionParamTypeDesc, DocumentLocation, DocumentRange, DocumentPosition, Sampling, Annotation, BasicAtomicTypeDef, BasicAtomicDef, AtomicTypeDef, AtomicFieldDef, ArrayDef, ArrayTypeDef, RecordTypeDef, RepeatedRecordTypeDef, RecordDef, RepeatedRecordDef, RecordLiteralNode, StringLiteralNode, ArrayLiteralNode, SourceComponentInfo,
|
|
3
|
+
export type { QueryDataRow, 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, NamedQuery, NamedModelObject, ExpressionType, FunctionDef, FunctionOverloadDef, FunctionParameterDef, ExpressionValueType, TypeDesc, FunctionParamTypeDesc, DocumentLocation, DocumentRange, DocumentPosition, Sampling, Annotation, BasicAtomicTypeDef, BasicAtomicDef, AtomicTypeDef, AtomicFieldDef, ArrayDef, ArrayTypeDef, RecordTypeDef, RepeatedRecordTypeDef, RecordDef, RepeatedRecordDef, RecordLiteralNode, StringLiteralNode, ArrayLiteralNode, SourceComponentInfo, DateLiteralNode, TimestampLiteralNode, TimestamptzLiteralNode, TimeLiteralExpr, TypecastExpr, } from './model';
|
|
4
4
|
export { isSourceDef, isBasicAtomic, isJoined, isJoinedSource, isSamplingEnable, isSamplingPercent, isSamplingRows, isRepeatedRecord, isBasicArray, mkArrayDef, mkFieldDef, expressionIsAggregate, expressionIsAnalytic, expressionIsCalculation, expressionIsScalar, expressionIsUngroupedAggregate, indent, composeSQLExpr, isTimestampUnit, isDateUnit, constantExprToSQL, } from './model';
|
|
5
5
|
export { malloyToQuery, MalloyTranslator, } from './lang';
|
|
6
6
|
export type { LogMessage, TranslateResponse } from './lang';
|
|
@@ -98,18 +98,36 @@ class ExprGranularTime extends expression_def_1.ExpressionDef {
|
|
|
98
98
|
getExpression(fs) {
|
|
99
99
|
const timeframe = this.units;
|
|
100
100
|
const exprVal = this.expr.getExpression(fs);
|
|
101
|
+
let timeType;
|
|
102
|
+
let tsVal;
|
|
101
103
|
if (malloy_types_1.TD.isTemporal(exprVal)) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
if (exprVal.type === 'date') {
|
|
105
|
+
if (!(0, malloy_types_1.isDateUnit)(timeframe)) {
|
|
106
|
+
return this.loggedErrorExpr('unsupported-type-for-time-truncation', `Cannot truncate date to timestamp unit '${timeframe}'`);
|
|
107
|
+
}
|
|
108
|
+
tsVal = { ...exprVal, timeframe: timeframe };
|
|
109
|
+
timeType = { type: 'date', timeframe };
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
// timestamp or timestamptz
|
|
113
|
+
tsVal = { ...exprVal, timeframe: timeframe };
|
|
114
|
+
if (exprVal.type === 'timestamptz') {
|
|
115
|
+
timeType = { type: 'timestamptz', timeframe };
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
timeType = { type: 'timestamp', timeframe };
|
|
119
|
+
}
|
|
120
|
+
}
|
|
106
121
|
if (this.truncate) {
|
|
107
122
|
tsVal.value = {
|
|
108
123
|
node: 'trunc',
|
|
109
|
-
e: (0, malloy_types_1.mkTemporal)(exprVal.value,
|
|
124
|
+
e: (0, malloy_types_1.mkTemporal)(exprVal.value, timeType),
|
|
110
125
|
units: timeframe,
|
|
111
126
|
};
|
|
112
127
|
}
|
|
128
|
+
else {
|
|
129
|
+
tsVal.value = exprVal.value;
|
|
130
|
+
}
|
|
113
131
|
return tsVal;
|
|
114
132
|
}
|
|
115
133
|
if (exprVal.type !== 'error') {
|
|
@@ -145,9 +163,9 @@ class ExprGranularTime extends expression_def_1.ExpressionDef {
|
|
|
145
163
|
toRange(fs) {
|
|
146
164
|
const begin = this.getExpression(fs);
|
|
147
165
|
const one = { node: 'numberLiteral', literal: '1' };
|
|
148
|
-
if (begin
|
|
149
|
-
const beginTS = expr_time_1.ExprTime.fromValue(
|
|
150
|
-
const endTS = new expr_time_1.ExprTime(
|
|
166
|
+
if (malloy_types_1.TD.isAnyTimestamp(begin)) {
|
|
167
|
+
const beginTS = expr_time_1.ExprTime.fromValue(begin.type, begin);
|
|
168
|
+
const endTS = new expr_time_1.ExprTime(begin.type, (0, time_utils_1.timeOffset)(begin.type, begin.value, '+', one, this.units), [begin]);
|
|
151
169
|
return new range_1.Range(beginTS, endTS);
|
|
152
170
|
}
|
|
153
171
|
const beginDate = new expr_time_1.ExprTime('date', begin.value, [begin]);
|
|
@@ -48,6 +48,18 @@ export declare class ExprProps extends ExpressionDef {
|
|
|
48
48
|
morphic?: {
|
|
49
49
|
[x: string]: import("../../../model/malloy_types").Expr;
|
|
50
50
|
};
|
|
51
|
+
} | {
|
|
52
|
+
requiresGroupBy: import("../../../model/malloy_types").RequiredGroupBy[] | undefined;
|
|
53
|
+
type: "timestamptz";
|
|
54
|
+
timeframe?: import("../../../model/malloy_types").TimestampUnit;
|
|
55
|
+
expressionType: import("../../../model/malloy_types").ExpressionType;
|
|
56
|
+
evalSpace: import("../../../model/malloy_types").EvalSpace;
|
|
57
|
+
fieldUsage: import("../../../model/malloy_types").FieldUsage[];
|
|
58
|
+
ungroupings?: import("../../../model/malloy_types").AggregateUngrouping[];
|
|
59
|
+
value: import("../../../model/malloy_types").Expr;
|
|
60
|
+
morphic?: {
|
|
61
|
+
[x: string]: import("../../../model/malloy_types").Expr;
|
|
62
|
+
};
|
|
51
63
|
} | {
|
|
52
64
|
requiresGroupBy: import("../../../model/malloy_types").RequiredGroupBy[] | undefined;
|
|
53
65
|
type: "number";
|
|
@@ -189,5 +201,17 @@ export declare class ExprProps extends ExpressionDef {
|
|
|
189
201
|
morphic?: {
|
|
190
202
|
[x: string]: import("../../../model/malloy_types").Expr;
|
|
191
203
|
};
|
|
204
|
+
} | {
|
|
205
|
+
requiresGroupBy: import("../../../model/malloy_types").RequiredGroupBy[] | undefined;
|
|
206
|
+
type: "timestamptz";
|
|
207
|
+
timeframe?: import("../../../model/malloy_types").TimestampUnit;
|
|
208
|
+
expressionType: import("../../../model/malloy_types").ExpressionType;
|
|
209
|
+
evalSpace: import("../../../model/malloy_types").EvalSpace;
|
|
210
|
+
fieldUsage: import("../../../model/malloy_types").FieldUsage[];
|
|
211
|
+
ungroupings?: import("../../../model/malloy_types").AggregateUngrouping[];
|
|
212
|
+
value: import("../../../model/malloy_types").Expr;
|
|
213
|
+
morphic?: {
|
|
214
|
+
[x: string]: import("../../../model/malloy_types").Expr;
|
|
215
|
+
};
|
|
192
216
|
};
|
|
193
217
|
}
|
|
@@ -8,7 +8,7 @@ export declare class ForRange extends ExpressionDef {
|
|
|
8
8
|
readonly duration: ExpressionDef;
|
|
9
9
|
readonly timeframe: Timeframe;
|
|
10
10
|
elementType: string;
|
|
11
|
-
legalChildTypes: import("
|
|
11
|
+
legalChildTypes: import("../../../model/malloy_types").TypeDesc[];
|
|
12
12
|
constructor(from: ExpressionDef, duration: ExpressionDef, timeframe: Timeframe);
|
|
13
13
|
apply(fs: FieldSpace, op: BinaryMalloyOperator, expr: ExpressionDef): ExprValue;
|
|
14
14
|
requestExpression(_fs: FieldSpace): undefined;
|
|
@@ -59,6 +59,7 @@ exports.ForRange = void 0;
|
|
|
59
59
|
const ast_utils_1 = require("../ast-utils");
|
|
60
60
|
const TDU = __importStar(require("../typedesc-utils"));
|
|
61
61
|
const time_utils_1 = require("../time-utils");
|
|
62
|
+
const malloy_types_1 = require("../../../model/malloy_types");
|
|
62
63
|
const expr_value_1 = require("../types/expr-value");
|
|
63
64
|
const expression_def_1 = require("../types/expression-def");
|
|
64
65
|
const expr_time_1 = require("./expr-time");
|
|
@@ -93,10 +94,10 @@ class ForRange extends expression_def_1.ExpressionDef {
|
|
|
93
94
|
// If the duration resolution is smaller than date, we have
|
|
94
95
|
// to do the computaion with timestamps.
|
|
95
96
|
let rangeType = (0, time_utils_1.resolution)(units);
|
|
96
|
-
// Next, if the beginning of the range is a timestamp, then we
|
|
97
|
-
// also have to do the computation as a timestamp
|
|
98
|
-
if (startV
|
|
99
|
-
rangeType =
|
|
97
|
+
// Next, if the beginning of the range is a timestamp or timestamptz, then we
|
|
98
|
+
// also have to do the computation as a timestamp (or timestamptz)
|
|
99
|
+
if (malloy_types_1.TD.isAnyTimestamp(startV)) {
|
|
100
|
+
rangeType = startV.type;
|
|
100
101
|
}
|
|
101
102
|
// everything is dates, do date math
|
|
102
103
|
if (checkV.type === 'date' && rangeType === 'date') {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type * as Malloy from '@malloydata/malloy-interfaces';
|
|
2
|
-
import type { TemporalFieldType, TimestampUnit,
|
|
2
|
+
import type { TemporalFieldType, TimestampUnit, TimeLiteralExpr } from '../../../model/malloy_types';
|
|
3
3
|
import type { ExprValue } from '../types/expr-value';
|
|
4
4
|
import type { FieldSpace } from '../types/field-space';
|
|
5
5
|
import { ExpressionDef } from '../types/expression-def';
|
|
@@ -21,16 +21,18 @@ export declare abstract class TimeLiteral extends ExpressionDef {
|
|
|
21
21
|
nextLit?: string;
|
|
22
22
|
timeZone?: string;
|
|
23
23
|
constructor(tm: TimeText, units: TimestampUnit | undefined, timeType: TemporalFieldType);
|
|
24
|
-
protected makeLiteral(val: string, typ: TemporalFieldType, units: TimestampUnit | undefined):
|
|
25
|
-
protected makeValue(val: string, dataType: TemporalFieldType): TimeResult;
|
|
24
|
+
protected makeLiteral(fs: FieldSpace, val: string, typ: TemporalFieldType, units: TimestampUnit | undefined): TimeLiteralExpr;
|
|
25
|
+
protected makeValue(fs: FieldSpace, val: string, dataType: TemporalFieldType): TimeResult;
|
|
26
26
|
getStableLiteral(): Malloy.LiteralValue;
|
|
27
27
|
getValue(): (import("../../../model/malloy_types").DateTypeDef & import("../../../model/malloy_types").TypeInfo & import("../types/expr-result").WithValue & {
|
|
28
28
|
timeframe?: TimestampUnit;
|
|
29
29
|
}) | (import("../../../model/malloy_types").TimestampTypeDef & import("../../../model/malloy_types").TypeInfo & import("../types/expr-result").WithValue & {
|
|
30
30
|
timeframe?: TimestampUnit;
|
|
31
|
+
}) | (import("../../../model/malloy_types").TimestamptzTypeDef & import("../../../model/malloy_types").TypeInfo & import("../types/expr-result").WithValue & {
|
|
32
|
+
timeframe?: TimestampUnit;
|
|
31
33
|
});
|
|
32
|
-
getExpression(
|
|
33
|
-
getNext(): ExprValue | undefined;
|
|
34
|
+
getExpression(fs: FieldSpace): ExprValue;
|
|
35
|
+
getNext(fs: FieldSpace): ExprValue | undefined;
|
|
34
36
|
granular(): boolean;
|
|
35
37
|
}
|
|
36
38
|
export declare class LiteralTimestamp extends TimeLiteral {
|
|
@@ -60,8 +62,8 @@ export declare class LiteralHour extends GranularLiteral {
|
|
|
60
62
|
*/
|
|
61
63
|
declare abstract class DateBasedLiteral extends GranularLiteral {
|
|
62
64
|
constructor(tm: TimeText, units: TimestampUnit, nextLit: string);
|
|
63
|
-
getExpression(
|
|
64
|
-
getNext(): ExprValue | undefined;
|
|
65
|
+
getExpression(fs: FieldSpace): ExprValue;
|
|
66
|
+
getNext(fs: FieldSpace): ExprValue | undefined;
|
|
65
67
|
}
|
|
66
68
|
export declare class LiteralDay extends DateBasedLiteral {
|
|
67
69
|
elementType: string;
|
|
@@ -29,6 +29,7 @@ const expr_value_1 = require("../types/expr-value");
|
|
|
29
29
|
const range_1 = require("./range");
|
|
30
30
|
const expr_time_1 = require("./expr-time");
|
|
31
31
|
const expression_def_1 = require("../types/expression-def");
|
|
32
|
+
const dialect_1 = require("../../../dialect/dialect");
|
|
32
33
|
class TimeFormatError extends Error {
|
|
33
34
|
}
|
|
34
35
|
exports.TimeFormatError = TimeFormatError;
|
|
@@ -69,24 +70,11 @@ class TimeLiteral extends expression_def_1.ExpressionDef {
|
|
|
69
70
|
this.timeZone = tm.tzSpec;
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
|
-
makeLiteral(val, typ, units) {
|
|
73
|
-
|
|
74
|
-
node: 'timeLiteral',
|
|
75
|
-
literal: val,
|
|
76
|
-
typeDef: typ === 'timestamp'
|
|
77
|
-
? { type: typ, timeframe: units }
|
|
78
|
-
: {
|
|
79
|
-
type: typ,
|
|
80
|
-
timeframe: units !== undefined && (0, malloy_types_1.isDateUnit)(units) ? units : undefined,
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
if (this.timeZone) {
|
|
84
|
-
timeFrag.timezone = this.timeZone;
|
|
85
|
-
}
|
|
86
|
-
return timeFrag;
|
|
73
|
+
makeLiteral(fs, val, typ, units) {
|
|
74
|
+
return dialect_1.Dialect.makeTimeLiteralNode(fs.dialectObj(), val, this.timeZone, units, typ);
|
|
87
75
|
}
|
|
88
|
-
makeValue(val, dataType) {
|
|
89
|
-
const value = this.makeLiteral(val, dataType, this.units);
|
|
76
|
+
makeValue(fs, val, dataType) {
|
|
77
|
+
const value = this.makeLiteral(fs, val, dataType, this.units);
|
|
90
78
|
return (0, expr_value_1.literalTimeResult)({
|
|
91
79
|
value,
|
|
92
80
|
dataType: value.typeDef,
|
|
@@ -96,48 +84,53 @@ class TimeLiteral extends expression_def_1.ExpressionDef {
|
|
|
96
84
|
getStableLiteral() {
|
|
97
85
|
const value = this.getValue();
|
|
98
86
|
let granularity = value.timeframe;
|
|
99
|
-
|
|
100
|
-
|
|
87
|
+
const node = value.value;
|
|
88
|
+
if (node.node === 'dateLiteral') {
|
|
89
|
+
if (granularity === 'hour' ||
|
|
90
|
+
granularity === 'minute' ||
|
|
91
|
+
granularity === 'second') {
|
|
92
|
+
// TODO should probably just throw...
|
|
93
|
+
granularity = 'day';
|
|
94
|
+
}
|
|
101
95
|
return {
|
|
102
|
-
kind: '
|
|
103
|
-
|
|
96
|
+
kind: 'date_literal',
|
|
97
|
+
date_value: node.literal,
|
|
104
98
|
granularity,
|
|
105
99
|
};
|
|
106
100
|
}
|
|
107
|
-
|
|
108
|
-
const timezone = value.value.timezone;
|
|
109
|
-
if (value.type === 'timestamp') {
|
|
101
|
+
else if (node.node === 'timestampLiteral') {
|
|
110
102
|
return {
|
|
111
103
|
kind: 'timestamp_literal',
|
|
112
|
-
timestamp_value:
|
|
104
|
+
timestamp_value: node.literal,
|
|
113
105
|
granularity,
|
|
114
|
-
timezone,
|
|
106
|
+
timezone: node.timezone,
|
|
115
107
|
};
|
|
116
108
|
}
|
|
117
|
-
else {
|
|
118
|
-
if (granularity === 'hour' ||
|
|
119
|
-
granularity === 'minute' ||
|
|
120
|
-
granularity === 'second') {
|
|
121
|
-
// TODO should probably just throw...
|
|
122
|
-
granularity = 'day';
|
|
123
|
-
}
|
|
109
|
+
else if (node.node === 'timestamptzLiteral') {
|
|
124
110
|
return {
|
|
125
|
-
kind: '
|
|
126
|
-
|
|
111
|
+
kind: 'timestamp_literal',
|
|
112
|
+
timestamp_value: node.literal,
|
|
127
113
|
granularity,
|
|
128
|
-
timezone,
|
|
114
|
+
timezone: node.timezone,
|
|
129
115
|
};
|
|
130
116
|
}
|
|
117
|
+
throw new Error(`Unexpected time literal node type: ${node.node}`);
|
|
131
118
|
}
|
|
132
119
|
getValue() {
|
|
133
|
-
|
|
120
|
+
// Used for stable literals - no dialect context, so pass undefined
|
|
121
|
+
const value = dialect_1.Dialect.makeTimeLiteralNode(undefined, this.literalPart, this.timeZone, this.units, this.timeType);
|
|
122
|
+
return (0, expr_value_1.literalTimeResult)({
|
|
123
|
+
value,
|
|
124
|
+
dataType: value.typeDef,
|
|
125
|
+
timeframe: this.units,
|
|
126
|
+
});
|
|
134
127
|
}
|
|
135
|
-
getExpression(
|
|
136
|
-
return this.makeValue(this.literalPart, this.timeType);
|
|
128
|
+
getExpression(fs) {
|
|
129
|
+
return this.makeValue(fs, this.literalPart, this.timeType);
|
|
137
130
|
}
|
|
138
|
-
getNext() {
|
|
131
|
+
getNext(fs) {
|
|
139
132
|
if (this.nextLit) {
|
|
140
|
-
return this.makeValue(this.nextLit, this.timeType);
|
|
133
|
+
return this.makeValue(fs, this.nextLit, this.timeType);
|
|
141
134
|
}
|
|
142
135
|
}
|
|
143
136
|
granular() {
|
|
@@ -197,7 +190,7 @@ class GranularLiteral extends TimeLiteral {
|
|
|
197
190
|
apply(fs, op, left) {
|
|
198
191
|
// We have a chance to write our own range comparison will all constants.
|
|
199
192
|
let rangeStart = this.getExpression(fs);
|
|
200
|
-
let rangeEnd = this.getNext();
|
|
193
|
+
let rangeEnd = this.getNext(fs);
|
|
201
194
|
if (rangeEnd) {
|
|
202
195
|
const testValue = left.getExpression(fs);
|
|
203
196
|
if (testValue.type === 'date' && op === '=' && this.units === 'day') {
|
|
@@ -210,7 +203,7 @@ class GranularLiteral extends TimeLiteral {
|
|
|
210
203
|
// }
|
|
211
204
|
return super.apply(fs, op, left);
|
|
212
205
|
}
|
|
213
|
-
if (testValue
|
|
206
|
+
if (malloy_types_1.TD.isAnyTimestamp(testValue) &&
|
|
214
207
|
op === '=' &&
|
|
215
208
|
this.units === undefined) {
|
|
216
209
|
// TODO remove the === 'second' check above and warn
|
|
@@ -222,7 +215,7 @@ class GranularLiteral extends TimeLiteral {
|
|
|
222
215
|
// }
|
|
223
216
|
return super.apply(fs, op, left);
|
|
224
217
|
}
|
|
225
|
-
if (testValue
|
|
218
|
+
if (malloy_types_1.TD.isAnyTimestamp(testValue)) {
|
|
226
219
|
const newStart = (0, expression_def_1.getMorphicValue)(rangeStart, 'timestamp');
|
|
227
220
|
const newEnd = (0, expression_def_1.getMorphicValue)(rangeEnd, 'timestamp');
|
|
228
221
|
if (newStart && newEnd) {
|
|
@@ -274,14 +267,14 @@ class DateBasedLiteral extends GranularLiteral {
|
|
|
274
267
|
constructor(tm, units, nextLit) {
|
|
275
268
|
super(tm, units, 'date', nextLit);
|
|
276
269
|
}
|
|
277
|
-
getExpression(
|
|
278
|
-
const dateValue = this.makeValue(this.literalPart, 'date');
|
|
279
|
-
const timestamp = this.makeLiteral(`${this.literalPart} 00:00:00`, 'timestamp', this.units);
|
|
270
|
+
getExpression(fs) {
|
|
271
|
+
const dateValue = this.makeValue(fs, this.literalPart, 'date');
|
|
272
|
+
const timestamp = this.makeLiteral(fs, `${this.literalPart} 00:00:00`, 'timestamp', this.units);
|
|
280
273
|
return { ...dateValue, morphic: { timestamp }, evalSpace: 'literal' };
|
|
281
274
|
}
|
|
282
|
-
getNext() {
|
|
283
|
-
const dateValue = this.makeValue(this.nextLit, 'date');
|
|
284
|
-
const timestamp = this.makeLiteral(`${this.nextLit} 00:00:00`, 'timestamp', this.units);
|
|
275
|
+
getNext(fs) {
|
|
276
|
+
const dateValue = this.makeValue(fs, this.nextLit, 'date');
|
|
277
|
+
const timestamp = this.makeLiteral(fs, `${this.nextLit} 00:00:00`, 'timestamp', this.units);
|
|
285
278
|
return { ...dateValue, morphic: { timestamp } };
|
|
286
279
|
}
|
|
287
280
|
}
|
|
@@ -126,8 +126,7 @@ class AtomicFieldDeclaration extends malloy_element_1.MalloyElement {
|
|
|
126
126
|
if ((0, malloy_types_1.isAtomicFieldType)(exprValue.type) && exprValue.type !== 'error') {
|
|
127
127
|
this.typecheckExprValue(exprValue);
|
|
128
128
|
const ret = (0, malloy_types_1.mkFieldDef)(TDU.atomicDef(exprValue), exprName);
|
|
129
|
-
if ((ret
|
|
130
|
-
(0, granular_result_1.isGranularResult)(exprValue)) {
|
|
129
|
+
if (malloy_types_1.TD.isTemporal(ret) && (0, granular_result_1.isGranularResult)(exprValue)) {
|
|
131
130
|
ret.timeframe = exprValue.timeframe;
|
|
132
131
|
}
|
|
133
132
|
ret.location = this.location;
|
|
@@ -4,6 +4,7 @@ export declare const numberT: TypeDesc;
|
|
|
4
4
|
export declare const stringT: TypeDesc;
|
|
5
5
|
export declare const dateT: TypeDesc;
|
|
6
6
|
export declare const timestampT: TypeDesc;
|
|
7
|
+
export declare const timestamptzT: TypeDesc;
|
|
7
8
|
export declare const boolT: TypeDesc;
|
|
8
9
|
export declare const errorT: TypeDesc;
|
|
9
10
|
export declare const aggregateBoolT: TypeDesc;
|