@malloydata/malloy 0.0.321 → 0.0.323
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/dialect/dialect.d.ts +109 -3
- package/dist/dialect/dialect.js +98 -3
- package/dist/dialect/duckdb/duckdb.d.ts +5 -2
- package/dist/dialect/duckdb/duckdb.js +22 -11
- package/dist/dialect/mysql/mysql.d.ts +5 -4
- package/dist/dialect/mysql/mysql.js +31 -47
- package/dist/dialect/pg_impl.d.ts +1 -2
- package/dist/dialect/pg_impl.js +0 -30
- package/dist/dialect/postgres/postgres.d.ts +5 -2
- package/dist/dialect/postgres/postgres.js +28 -11
- package/dist/dialect/snowflake/snowflake.d.ts +5 -3
- package/dist/dialect/snowflake/snowflake.js +23 -22
- package/dist/dialect/standardsql/standardsql.d.ts +9 -3
- package/dist/dialect/standardsql/standardsql.js +42 -33
- package/dist/dialect/trino/trino.d.ts +7 -3
- package/dist/dialect/trino/trino.js +41 -43
- package/dist/lang/ast/error-factory.js +1 -1
- package/dist/lang/ast/field-space/static-space.js +5 -1
- package/dist/lang/ast/types/malloy-element.js +10 -2
- package/dist/model/expression_compiler.js +172 -145
- package/dist/model/filter_compilers.d.ts +0 -2
- package/dist/model/filter_compilers.js +132 -57
- package/dist/model/malloy_types.d.ts +19 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Sampling, AtomicTypeDef,
|
|
1
|
+
import type { Sampling, AtomicTypeDef, TimeExtractExpr, TypecastExpr, RegexMatchExpr, TimeLiteralNode, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, ArrayLiteralNode, TimestampUnit } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { DialectFieldList, OrderByRequest, QueryInfo } from '../dialect';
|
|
4
4
|
import { Dialect } from '../dialect';
|
|
@@ -28,6 +28,10 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
28
28
|
supportsHyperLogLog: boolean;
|
|
29
29
|
likeEscape: boolean;
|
|
30
30
|
quoteTablePath(tablePath: string): string;
|
|
31
|
+
needsCivilTimeComputation(typeDef: AtomicTypeDef, truncateTo: TimestampUnit | undefined, offsetUnit: TimestampUnit | undefined, qi: QueryInfo): {
|
|
32
|
+
needed: boolean;
|
|
33
|
+
tz: string | undefined;
|
|
34
|
+
};
|
|
31
35
|
sqlGroupSetTable(groupSetCount: number): string;
|
|
32
36
|
sqlAnyValue(groupSet: number, fieldName: string): string;
|
|
33
37
|
sqlOrderBy(orderTerms: string[], obr?: OrderByRequest): string;
|
|
@@ -46,9 +50,11 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
46
50
|
sqlSelectAliasAsStruct(alias: string): string;
|
|
47
51
|
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
48
52
|
sqlNowExpr(): string;
|
|
49
|
-
sqlTruncExpr(qi: QueryInfo, trunc: TimeTruncExpr): string;
|
|
50
53
|
sqlTimeExtractExpr(qi: QueryInfo, te: TimeExtractExpr): string;
|
|
51
|
-
|
|
54
|
+
sqlConvertToCivilTime(expr: string, timezone: string): string;
|
|
55
|
+
sqlConvertFromCivilTime(expr: string, timezone: string): string;
|
|
56
|
+
sqlTruncate(expr: string, unit: TimestampUnit, typeDef: AtomicTypeDef, inCivilTime: boolean, timezone?: string): string;
|
|
57
|
+
sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, typeDef: AtomicTypeDef, inCivilTime: boolean, _timezone?: string): string;
|
|
52
58
|
ignoreInProject(fieldName: string): boolean;
|
|
53
59
|
sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
|
|
54
60
|
sqlRegexpMatch(match: RegexMatchExpr): string;
|
|
@@ -40,9 +40,6 @@ function timestampMeasureable(units) {
|
|
|
40
40
|
'day',
|
|
41
41
|
].includes(units);
|
|
42
42
|
}
|
|
43
|
-
function dateMeasureable(units) {
|
|
44
|
-
return ['day', 'week', 'month', 'quarter', 'year'].includes(units);
|
|
45
|
-
}
|
|
46
43
|
const extractMap = {
|
|
47
44
|
'day_of_week': 'dayofweek',
|
|
48
45
|
'day_of_year': 'dayofyear',
|
|
@@ -104,6 +101,17 @@ class StandardSQLDialect extends dialect_1.Dialect {
|
|
|
104
101
|
quoteTablePath(tablePath) {
|
|
105
102
|
return `\`${tablePath}\``;
|
|
106
103
|
}
|
|
104
|
+
needsCivilTimeComputation(typeDef, truncateTo, offsetUnit, qi) {
|
|
105
|
+
// In addition to using "civil" space for units where a query time zone is
|
|
106
|
+
// set, BigQuery also uses civil space for unit operations not supported
|
|
107
|
+
// by the TIMESTAMP functions.
|
|
108
|
+
const calendarUnits = ['day', 'week', 'month', 'quarter', 'year'];
|
|
109
|
+
const isCalendarTruncate = truncateTo !== undefined && calendarUnits.includes(truncateTo);
|
|
110
|
+
const isCalendarOffset = offsetUnit !== undefined && calendarUnits.includes(offsetUnit);
|
|
111
|
+
const needed = malloy_types_1.TD.isTimestamp(typeDef) && (isCalendarTruncate || isCalendarOffset);
|
|
112
|
+
const tz = needed ? qtz(qi) || 'UTC' : undefined;
|
|
113
|
+
return { needed, tz };
|
|
114
|
+
}
|
|
107
115
|
sqlGroupSetTable(groupSetCount) {
|
|
108
116
|
return `CROSS JOIN (SELECT row_number() OVER() -1 group_set FROM UNNEST(GENERATE_ARRAY(0,${groupSetCount},1)))`;
|
|
109
117
|
}
|
|
@@ -212,45 +220,46 @@ ${(0, utils_1.indent)(sql)}
|
|
|
212
220
|
sqlNowExpr() {
|
|
213
221
|
return 'CURRENT_TIMESTAMP()';
|
|
214
222
|
}
|
|
215
|
-
sqlTruncExpr(qi, trunc) {
|
|
216
|
-
const tz = qtz(qi);
|
|
217
|
-
const tzAdd = tz ? `, "${tz}"` : '';
|
|
218
|
-
if (malloy_types_1.TD.isDate(trunc.e.typeDef)) {
|
|
219
|
-
if (dateMeasureable(trunc.units)) {
|
|
220
|
-
return `DATE_TRUNC(${trunc.e.sql},${trunc.units})`;
|
|
221
|
-
}
|
|
222
|
-
return `TIMESTAMP(${trunc.e.sql}${tzAdd})`;
|
|
223
|
-
}
|
|
224
|
-
return `TIMESTAMP_TRUNC(${trunc.e.sql},${trunc.units}${tzAdd})`;
|
|
225
|
-
}
|
|
226
223
|
sqlTimeExtractExpr(qi, te) {
|
|
227
224
|
const extractTo = extractMap[te.units] || te.units;
|
|
228
225
|
const tz = malloy_types_1.TD.isTimestamp(te.e.typeDef) && qtz(qi);
|
|
229
226
|
const tzAdd = tz ? ` AT TIME ZONE '${tz}'` : '';
|
|
230
227
|
return `EXTRACT(${extractTo} FROM ${te.e.sql}${tzAdd})`;
|
|
231
228
|
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
}
|
|
229
|
+
sqlConvertToCivilTime(expr, timezone) {
|
|
230
|
+
return `DATETIME(${expr}, '${timezone}')`;
|
|
231
|
+
}
|
|
232
|
+
sqlConvertFromCivilTime(expr, timezone) {
|
|
233
|
+
return `TIMESTAMP(${expr}, '${timezone}')`;
|
|
234
|
+
}
|
|
235
|
+
sqlTruncate(expr, unit, typeDef, inCivilTime, timezone) {
|
|
236
|
+
if (inCivilTime) {
|
|
237
|
+
// Operating on DATETIME (civil time)
|
|
238
|
+
return `DATETIME_TRUNC(${expr}, ${unit})`;
|
|
239
|
+
}
|
|
240
|
+
// Operating on DATE or TIMESTAMP
|
|
241
|
+
if (malloy_types_1.TD.isDate(typeDef)) {
|
|
242
|
+
return `DATE_TRUNC(${expr}, ${unit})`;
|
|
242
243
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
244
|
+
// TIMESTAMP truncation with optional timezone
|
|
245
|
+
const tzParam = timezone ? `, '${timezone}'` : '';
|
|
246
|
+
return `TIMESTAMP_TRUNC(${expr}, ${unit}${tzParam})`;
|
|
247
|
+
}
|
|
248
|
+
sqlOffsetTime(expr, op, magnitude, unit, typeDef, inCivilTime, _timezone) {
|
|
249
|
+
if (inCivilTime) {
|
|
250
|
+
// Operating on DATETIME (civil time)
|
|
251
|
+
const funcName = op === '+' ? 'DATETIME_ADD' : 'DATETIME_SUB';
|
|
252
|
+
return `${funcName}(${expr}, INTERVAL ${magnitude} ${unit})`;
|
|
246
253
|
}
|
|
247
|
-
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
return
|
|
254
|
+
// Operating on DATE or TIMESTAMP
|
|
255
|
+
const baseType = typeDef.type;
|
|
256
|
+
if (baseType === 'date') {
|
|
257
|
+
const funcName = op === '+' ? 'DATE_ADD' : 'DATE_SUB';
|
|
258
|
+
return `${funcName}(${expr}, INTERVAL ${magnitude} ${unit})`;
|
|
252
259
|
}
|
|
253
|
-
|
|
260
|
+
// TIMESTAMP with sub-day units only (calendar units go through civil time)
|
|
261
|
+
const funcName = op === '+' ? 'TIMESTAMP_ADD' : 'TIMESTAMP_SUB';
|
|
262
|
+
return `${funcName}(${expr}, INTERVAL ${magnitude} ${unit})`;
|
|
254
263
|
}
|
|
255
264
|
ignoreInProject(fieldName) {
|
|
256
265
|
return fieldName === '_PARTITIONTIME';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Expr, Sampling, AtomicTypeDef,
|
|
1
|
+
import type { Expr, Sampling, AtomicTypeDef, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeLiteralNode, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { DialectFieldList, OrderByClauseType, QueryInfo } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
@@ -51,7 +51,10 @@ export declare class TrinoDialect extends PostgresBase {
|
|
|
51
51
|
sqlCreateFunctionCombineLastStage(lastStageName: string, fieldList: DialectFieldList): string;
|
|
52
52
|
sqlSelectAliasAsStruct(alias: string, fieldList: any): string;
|
|
53
53
|
keywords: string[];
|
|
54
|
-
|
|
54
|
+
sqlConvertToCivilTime(expr: string, timezone: string): string;
|
|
55
|
+
sqlConvertFromCivilTime(expr: string, _timezone: string): string;
|
|
56
|
+
sqlTruncate(expr: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
|
|
57
|
+
sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: string, _typeDef: AtomicTypeDef, _inCivilTime: boolean, _timezone?: string): string;
|
|
55
58
|
sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
|
|
56
59
|
sqlRegexpMatch(reCmp: RegexMatchExpr): string;
|
|
57
60
|
sqlMeasureTimeExpr(mf: MeasureTimeExpr): string;
|
|
@@ -72,7 +75,6 @@ export declare class TrinoDialect extends PostgresBase {
|
|
|
72
75
|
sqlStringAggDistinct(distinctKey: string, valueSQL: string, separatorSQL: string): string;
|
|
73
76
|
validateTypeName(sqlType: string): boolean;
|
|
74
77
|
sqlLiteralTime(qi: QueryInfo, lit: TimeLiteralNode): string;
|
|
75
|
-
sqlTruncExpr(qi: QueryInfo, df: TimeTruncExpr): string;
|
|
76
78
|
sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
|
|
77
79
|
sqlLiteralRecord(lit: RecordLiteralNode): string;
|
|
78
80
|
}
|
|
@@ -81,6 +83,8 @@ export declare class PrestoDialect extends TrinoDialect {
|
|
|
81
83
|
supportsPipelinesInViews: boolean;
|
|
82
84
|
supportsLeftJoinUnnest: boolean;
|
|
83
85
|
sqlGenerateUUID(): string;
|
|
86
|
+
sqlLiteralTime(qi: QueryInfo, lit: TimeLiteralNode): string;
|
|
87
|
+
sqlConvertFromCivilTime(expr: string, _timezone: string): string;
|
|
84
88
|
sqlUnnestAlias(source: string, alias: string, _fieldList: DialectFieldList, needDistinctKey: boolean, isArray: boolean, _isInNestedPipeline: boolean): string;
|
|
85
89
|
getDialectFunctions(): {
|
|
86
90
|
[name: string]: DialectFunctionOverloadDef[];
|
|
@@ -330,21 +330,35 @@ ${(0, utils_1.indent)(sql)}
|
|
|
330
330
|
const definitions = this.buildTypeExpression(fieldList);
|
|
331
331
|
return `CAST(ROW(${fields}) as ROW(${definitions})`;
|
|
332
332
|
}
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
333
|
+
sqlConvertToCivilTime(expr, timezone) {
|
|
334
|
+
return `${expr} AT TIME ZONE '${timezone}'`;
|
|
335
|
+
}
|
|
336
|
+
sqlConvertFromCivilTime(expr, _timezone) {
|
|
337
|
+
return `CAST(at_timezone(${expr}, 'UTC') AS TIMESTAMP)`;
|
|
338
|
+
}
|
|
339
|
+
sqlTruncate(expr, unit, _typeDef, _inCivilTime, _timezone) {
|
|
340
|
+
// Trino starts weeks on Monday, Malloy wants Sunday
|
|
341
|
+
// Add 1 day before truncating, subtract 1 day after
|
|
342
|
+
if (unit === 'week') {
|
|
343
|
+
return `(DATE_TRUNC('${unit}', (${expr} + INTERVAL '1' DAY)) - INTERVAL '1' DAY)`;
|
|
339
344
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
345
|
+
return `DATE_TRUNC('${unit}', ${expr})`;
|
|
346
|
+
}
|
|
347
|
+
sqlOffsetTime(expr, op, magnitude, unit, _typeDef, _inCivilTime, _timezone) {
|
|
348
|
+
// Convert quarter/week to supported units
|
|
349
|
+
let offsetUnit = unit;
|
|
350
|
+
let offsetMag = magnitude;
|
|
351
|
+
if (unit === 'quarter') {
|
|
352
|
+
offsetUnit = 'month';
|
|
353
|
+
offsetMag = `${magnitude}*3`;
|
|
343
354
|
}
|
|
344
|
-
if (
|
|
345
|
-
|
|
355
|
+
else if (unit === 'week') {
|
|
356
|
+
offsetUnit = 'day';
|
|
357
|
+
offsetMag = `${magnitude}*7`;
|
|
346
358
|
}
|
|
347
|
-
|
|
359
|
+
// Handle subtraction by negating
|
|
360
|
+
const n = op === '-' ? `(${offsetMag})*-1` : offsetMag;
|
|
361
|
+
return `DATE_ADD('${offsetUnit}', ${n}, ${expr})`;
|
|
348
362
|
}
|
|
349
363
|
sqlCast(qi, cast) {
|
|
350
364
|
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
@@ -494,40 +508,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
494
508
|
}
|
|
495
509
|
const tz = lit.timezone || qtz(qi);
|
|
496
510
|
if (tz) {
|
|
497
|
-
|
|
511
|
+
// Interpret wall clock time in timezone, convert to UTC wall clock, cast to TIMESTAMP
|
|
512
|
+
return `CAST(at_timezone(with_timezone(TIMESTAMP '${lit.literal}', '${tz}'), 'UTC') AS TIMESTAMP)`;
|
|
498
513
|
}
|
|
499
514
|
return `TIMESTAMP '${lit.literal}'`;
|
|
500
515
|
}
|
|
501
|
-
sqlTruncExpr(qi, df) {
|
|
502
|
-
// adjusting for monday/sunday weeks
|
|
503
|
-
const week = df.units === 'week';
|
|
504
|
-
const truncThis = week ? `${df.e.sql} + INTERVAL '1' DAY` : df.e.sql;
|
|
505
|
-
// Only do timezone conversion for timestamps, not dates
|
|
506
|
-
if (malloy_types_1.TD.isTimestamp(df.e.typeDef)) {
|
|
507
|
-
const tz = qtz(qi);
|
|
508
|
-
if (tz) {
|
|
509
|
-
// get a civil version of the time in the query time zone
|
|
510
|
-
const civilSource = `(CAST(${truncThis} AS TIMESTAMP WITH TIME ZONE) AT TIME ZONE '${tz}')`;
|
|
511
|
-
// do truncation in that time space
|
|
512
|
-
let civilTrunc = `DATE_TRUNC('${df.units}', ${civilSource})`;
|
|
513
|
-
if (week) {
|
|
514
|
-
civilTrunc = `(${civilTrunc} - INTERVAL '1' DAY)`;
|
|
515
|
-
}
|
|
516
|
-
// make a tstz from the civil time ... "AT TIME ZONE" of
|
|
517
|
-
// a TIMESTAMP will produce a TIMESTAMP WITH TIME ZONE in that zone
|
|
518
|
-
// where the civil appearance is the same as the TIMESTAMP
|
|
519
|
-
const truncTsTz = `${civilTrunc} AT TIME ZONE '${tz}'`;
|
|
520
|
-
// Now just make a system TIMESTAMP from that
|
|
521
|
-
return `CAST(${truncTsTz} AS TIMESTAMP)`;
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
// For dates (civil time) or timestamps without query timezone
|
|
525
|
-
let result = `DATE_TRUNC('${df.units}', ${truncThis})`;
|
|
526
|
-
if (week) {
|
|
527
|
-
result = `(${result} - INTERVAL '1' DAY)`;
|
|
528
|
-
}
|
|
529
|
-
return result;
|
|
530
|
-
}
|
|
531
516
|
sqlTimeExtractExpr(qi, from) {
|
|
532
517
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
533
518
|
let extractFrom = from.e.sql || '';
|
|
@@ -567,6 +552,19 @@ class PrestoDialect extends TrinoDialect {
|
|
|
567
552
|
sqlGenerateUUID() {
|
|
568
553
|
return 'CAST(UUID() AS VARCHAR)';
|
|
569
554
|
}
|
|
555
|
+
sqlLiteralTime(qi, lit) {
|
|
556
|
+
if (malloy_types_1.TD.isDate(lit.typeDef)) {
|
|
557
|
+
return `DATE '${lit.literal}'`;
|
|
558
|
+
}
|
|
559
|
+
const tz = lit.timezone || qtz(qi);
|
|
560
|
+
if (tz) {
|
|
561
|
+
return `CAST(TIMESTAMP '${lit.literal} ${tz}' AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
562
|
+
}
|
|
563
|
+
return `TIMESTAMP '${lit.literal}'`;
|
|
564
|
+
}
|
|
565
|
+
sqlConvertFromCivilTime(expr, _timezone) {
|
|
566
|
+
return `CAST(${expr} AT TIME ZONE 'UTC' AS TIMESTAMP)`;
|
|
567
|
+
}
|
|
570
568
|
sqlUnnestAlias(source, alias, _fieldList, needDistinctKey, isArray, _isInNestedPipeline) {
|
|
571
569
|
if (isArray) {
|
|
572
570
|
if (needDistinctKey) {
|
|
@@ -141,7 +141,11 @@ class StaticSpace {
|
|
|
141
141
|
type: found instanceof struct_space_field_base_1.StructSpaceFieldBase
|
|
142
142
|
? 'joinReference'
|
|
143
143
|
: 'fieldReference',
|
|
144
|
-
definition
|
|
144
|
+
definition: {
|
|
145
|
+
type: definition.type,
|
|
146
|
+
annotation: definition.annotation,
|
|
147
|
+
location: definition.location,
|
|
148
|
+
},
|
|
145
149
|
location: head.location,
|
|
146
150
|
text: head.refString,
|
|
147
151
|
});
|
|
@@ -118,7 +118,11 @@ class MalloyElement {
|
|
|
118
118
|
this.addReference({
|
|
119
119
|
type: 'queryReference',
|
|
120
120
|
text: key,
|
|
121
|
-
definition:
|
|
121
|
+
definition: {
|
|
122
|
+
type: result.entry.type,
|
|
123
|
+
annotation: result.entry.annotation,
|
|
124
|
+
location: result.entry.location,
|
|
125
|
+
},
|
|
122
126
|
location: reference.location,
|
|
123
127
|
});
|
|
124
128
|
}
|
|
@@ -126,7 +130,11 @@ class MalloyElement {
|
|
|
126
130
|
this.addReference({
|
|
127
131
|
type: 'exploreReference',
|
|
128
132
|
text: key,
|
|
129
|
-
definition:
|
|
133
|
+
definition: {
|
|
134
|
+
type: result.entry.type,
|
|
135
|
+
annotation: result.entry.annotation,
|
|
136
|
+
location: result.entry.location,
|
|
137
|
+
},
|
|
130
138
|
location: reference.location,
|
|
131
139
|
});
|
|
132
140
|
}
|