@malloydata/malloy 0.0.203-dev241021134413 → 0.0.203-dev241022191525
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 +11 -0
- package/dist/dialect/dialect.js +27 -0
- package/dist/dialect/duckdb/duckdb.js +2 -2
- package/dist/dialect/mysql/mysql.js +12 -15
- package/dist/dialect/pg_impl.js +7 -9
- package/dist/dialect/snowflake/snowflake.js +12 -17
- package/dist/dialect/standardsql/standardsql.js +13 -16
- package/dist/dialect/trino/trino.js +9 -12
- package/dist/lang/ast/expressions/expr-granular-time.js +1 -1
- package/dist/lang/ast/expressions/expr-record-literal.d.ts +1 -1
- package/dist/lang/ast/expressions/expr-record-literal.js +28 -25
- package/dist/lang/ast/expressions/expr-time-extract.js +3 -3
- package/dist/lang/ast/expressions/expr-time.js +2 -2
- package/dist/lang/ast/expressions/for-range.js +1 -1
- package/dist/lang/ast/expressions/time-literal.js +1 -1
- package/dist/lang/ast/query-builders/index-builder.d.ts +1 -0
- package/dist/lang/ast/query-builders/index-builder.js +4 -0
- package/dist/lang/ast/query-builders/reduce-builder.d.ts +1 -0
- package/dist/lang/ast/query-builders/reduce-builder.js +4 -0
- package/dist/lang/ast/source-properties/join.js +1 -0
- package/dist/lang/ast/time-utils.d.ts +1 -3
- package/dist/lang/ast/time-utils.js +24 -34
- package/dist/lang/ast/types/expression-def.js +2 -2
- package/dist/lang/ast/types/query-builder.d.ts +1 -0
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +49 -14
- package/dist/lang/lib/Malloy/MalloyParser.js +1784 -1538
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +48 -11
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +30 -7
- package/dist/lang/malloy-to-ast.d.ts +4 -1
- package/dist/lang/malloy-to-ast.js +21 -32
- package/dist/lang/test/query.spec.js +7 -2
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/model/malloy_query.d.ts +1 -0
- package/dist/model/malloy_query.js +15 -1
- package/dist/model/malloy_types.d.ts +43 -14
- package/dist/model/malloy_types.js +83 -2
- package/package.json +1 -1
|
@@ -123,5 +123,16 @@ export declare abstract class Dialect {
|
|
|
123
123
|
abstract sqlTypeToMalloyType(sqlType: string): LeafAtomicTypeDef;
|
|
124
124
|
abstract malloyTypeToSQLType(malloyType: AtomicTypeDef): string;
|
|
125
125
|
abstract validateTypeName(sqlType: string): boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Helper function for sql cast implementations. Handles the
|
|
128
|
+
* wrangling of the raw type and also inferring the source
|
|
129
|
+
* type if it was not provided.
|
|
130
|
+
*/
|
|
131
|
+
sqlCastPrep(cast: TypecastExpr): {
|
|
132
|
+
op: string;
|
|
133
|
+
srcTypeDef: LeafAtomicTypeDef | undefined;
|
|
134
|
+
dstTypeDef: LeafAtomicTypeDef | undefined;
|
|
135
|
+
dstSQLType: string;
|
|
136
|
+
};
|
|
126
137
|
}
|
|
127
138
|
export {};
|
package/dist/dialect/dialect.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.Dialect = exports.qtz = exports.inDays = exports.dayIndex = exports.isDialectFieldStruct = void 0;
|
|
26
|
+
const malloy_types_1 = require("../model/malloy_types");
|
|
26
27
|
function isDialectFieldStruct(d) {
|
|
27
28
|
return d.type === 'struct';
|
|
28
29
|
}
|
|
@@ -201,6 +202,32 @@ class Dialect {
|
|
|
201
202
|
''
|
|
202
203
|
)`;
|
|
203
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Helper function for sql cast implementations. Handles the
|
|
207
|
+
* wrangling of the raw type and also inferring the source
|
|
208
|
+
* type if it was not provided.
|
|
209
|
+
*/
|
|
210
|
+
sqlCastPrep(cast) {
|
|
211
|
+
let srcTypeDef = cast.srcType || cast.e.typeDef;
|
|
212
|
+
const src = (srcTypeDef === null || srcTypeDef === void 0 ? void 0 : srcTypeDef.type) || 'unknown';
|
|
213
|
+
if (srcTypeDef && !(0, malloy_types_1.isLeafAtomic)(srcTypeDef)) {
|
|
214
|
+
srcTypeDef = undefined;
|
|
215
|
+
}
|
|
216
|
+
if ((0, malloy_types_1.isRawCast)(cast)) {
|
|
217
|
+
return {
|
|
218
|
+
op: `${src}::'${cast.dstSQLType}'`,
|
|
219
|
+
srcTypeDef,
|
|
220
|
+
dstTypeDef: undefined,
|
|
221
|
+
dstSQLType: cast.dstSQLType,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
op: `${src}::${cast.dstType.type}`,
|
|
226
|
+
srcTypeDef,
|
|
227
|
+
dstTypeDef: cast.dstType,
|
|
228
|
+
dstSQLType: this.malloyTypeToSQLType(cast.dstType),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
204
231
|
}
|
|
205
232
|
exports.Dialect = Dialect;
|
|
206
233
|
//# sourceMappingURL=dialect.js.map
|
|
@@ -330,10 +330,10 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
330
330
|
let lVal = from.sql || '';
|
|
331
331
|
let rVal = to.sql || '';
|
|
332
332
|
if (!(0, dialect_1.inDays)(df.units)) {
|
|
333
|
-
if (from.
|
|
333
|
+
if (malloy_types_1.TD.isDate(from.typeDef)) {
|
|
334
334
|
lVal = `${lVal}::TIMESTAMP`;
|
|
335
335
|
}
|
|
336
|
-
if (to.
|
|
336
|
+
if (malloy_types_1.TD.isDate(to.typeDef)) {
|
|
337
337
|
rVal = `${rVal}::TIMESTAMP`;
|
|
338
338
|
}
|
|
339
339
|
}
|
|
@@ -278,7 +278,7 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
278
278
|
if (trunc.units === 'week') {
|
|
279
279
|
truncThis = `DATE_SUB(${truncThis}, INTERVAL DAYOFWEEK(${truncThis}) - 1 DAY)`;
|
|
280
280
|
}
|
|
281
|
-
if (trunc.e.
|
|
281
|
+
if (malloy_types_1.TD.isTimestamp(trunc.e.typeDef)) {
|
|
282
282
|
const tz = (0, dialect_1.qtz)(qi);
|
|
283
283
|
if (tz) {
|
|
284
284
|
const civilSource = `(CONVERT_TZ(${truncThis}, 'UTC','${tz}'))`;
|
|
@@ -318,7 +318,7 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
318
318
|
sqlTimeExtractExpr(qi, te) {
|
|
319
319
|
const msUnits = msExtractionMap[te.units] || te.units;
|
|
320
320
|
let extractFrom = te.e.sql;
|
|
321
|
-
if (te.e.
|
|
321
|
+
if (malloy_types_1.TD.isTimestamp(te.e.typeDef)) {
|
|
322
322
|
const tz = (0, dialect_1.qtz)(qi);
|
|
323
323
|
if (tz) {
|
|
324
324
|
extractFrom = `CONVERT_TZ(${extractFrom}, 'UTC', '${tz}')`;
|
|
@@ -341,34 +341,31 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
341
341
|
return `(${df.kids.base.sql})${df.op}${interval}`;
|
|
342
342
|
}
|
|
343
343
|
sqlCast(qi, cast) {
|
|
344
|
-
const
|
|
344
|
+
const srcSQL = cast.e.sql || 'internal-error-in-sql-generation';
|
|
345
|
+
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
345
346
|
const tz = (0, dialect_1.qtz)(qi);
|
|
346
347
|
if (op === 'timestamp::date' && tz) {
|
|
347
|
-
return `CAST(CONVERT_TZ(${
|
|
348
|
+
return `CAST(CONVERT_TZ(${srcSQL}, 'UTC', '${tz}') AS DATE) `;
|
|
348
349
|
}
|
|
349
350
|
else if (op === 'date::timestamp' && tz) {
|
|
350
|
-
return ` CONVERT_TZ(${
|
|
351
|
+
return ` CONVERT_TZ(${srcSQL}, '${tz}', 'UTC')`;
|
|
351
352
|
}
|
|
352
|
-
if (
|
|
353
|
-
const dstType = typeof cast.dstType === 'string'
|
|
354
|
-
? this.malloyTypeToSQLType({ type: cast.dstType })
|
|
355
|
-
: cast.dstType.raw;
|
|
353
|
+
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
356
354
|
if (cast.safe) {
|
|
357
355
|
throw new Error("Mysql dialect doesn't support Safe Cast");
|
|
358
356
|
}
|
|
359
|
-
if (
|
|
360
|
-
return `CONCAT(${
|
|
357
|
+
if (malloy_types_1.TD.isString(dstTypeDef)) {
|
|
358
|
+
return `CONCAT(${srcSQL}, '')`;
|
|
361
359
|
}
|
|
362
|
-
return `CAST(${
|
|
360
|
+
return `CAST(${srcSQL} AS ${dstSQLType})`;
|
|
363
361
|
}
|
|
364
|
-
|
|
365
|
-
return cast.e.sql || 'weirdly undefined';
|
|
362
|
+
return srcSQL;
|
|
366
363
|
}
|
|
367
364
|
sqlRegexpMatch(df) {
|
|
368
365
|
return `REGEXP_LIKE(${df.kids.expr.sql}, ${df.kids.regex.sql})`;
|
|
369
366
|
}
|
|
370
367
|
sqlLiteralTime(qi, lt) {
|
|
371
|
-
if (lt.
|
|
368
|
+
if (malloy_types_1.TD.isDate(lt.typeDef)) {
|
|
372
369
|
return `DATE '${lt.literal}'`;
|
|
373
370
|
}
|
|
374
371
|
const tz = lt.timezone || (0, dialect_1.qtz)(qi);
|
package/dist/dialect/pg_impl.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.PostgresBase = exports.timeExtractMap = void 0;
|
|
10
|
+
const malloy_types_1 = require("../model/malloy_types");
|
|
10
11
|
const dialect_1 = require("./dialect");
|
|
11
12
|
exports.timeExtractMap = {
|
|
12
13
|
'day_of_week': 'dow',
|
|
@@ -21,7 +22,7 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
21
22
|
// adjusting for monday/sunday weeks
|
|
22
23
|
const week = df.units === 'week';
|
|
23
24
|
const truncThis = week ? `${df.e.sql} + INTERVAL '1' DAY` : df.e.sql;
|
|
24
|
-
if (df.e.
|
|
25
|
+
if (malloy_types_1.TD.isTimestamp(df.e.typeDef)) {
|
|
25
26
|
const tz = (0, dialect_1.qtz)(qi);
|
|
26
27
|
if (tz) {
|
|
27
28
|
const civilSource = `(${truncThis}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
@@ -44,7 +45,7 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
44
45
|
sqlTimeExtractExpr(qi, from) {
|
|
45
46
|
const units = exports.timeExtractMap[from.units] || from.units;
|
|
46
47
|
let extractFrom = from.e.sql;
|
|
47
|
-
if (from.e.
|
|
48
|
+
if (malloy_types_1.TD.isTimestamp(from.e.typeDef)) {
|
|
48
49
|
const tz = (0, dialect_1.qtz)(qi);
|
|
49
50
|
if (tz) {
|
|
50
51
|
extractFrom = `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
@@ -55,7 +56,7 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
55
56
|
}
|
|
56
57
|
sqlCast(qi, cast) {
|
|
57
58
|
const expr = cast.e.sql || '';
|
|
58
|
-
const op =
|
|
59
|
+
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
59
60
|
const tz = (0, dialect_1.qtz)(qi);
|
|
60
61
|
if (op === 'timestamp::date' && tz) {
|
|
61
62
|
const tstz = `${expr}::TIMESTAMPTZ`;
|
|
@@ -64,12 +65,9 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
64
65
|
else if (op === 'date::timestamp' && tz) {
|
|
65
66
|
return `CAST((${expr})::TIMESTAMP AT TIME ZONE '${tz}' AS TIMESTAMP)`;
|
|
66
67
|
}
|
|
67
|
-
if (
|
|
68
|
-
const dstType = typeof cast.dstType === 'string'
|
|
69
|
-
? this.malloyTypeToSQLType({ type: cast.dstType })
|
|
70
|
-
: cast.dstType.raw;
|
|
68
|
+
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
71
69
|
const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
|
|
72
|
-
return `${castFunc}(${expr} AS ${
|
|
70
|
+
return `${castFunc}(${expr} AS ${dstSQLType})`;
|
|
73
71
|
}
|
|
74
72
|
return expr;
|
|
75
73
|
}
|
|
@@ -77,7 +75,7 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
77
75
|
return `${df.kids.expr.sql} ~ ${df.kids.regex.sql}`;
|
|
78
76
|
}
|
|
79
77
|
sqlLiteralTime(qi, lt) {
|
|
80
|
-
if (lt.
|
|
78
|
+
if (malloy_types_1.TD.isDate(lt.typeDef)) {
|
|
81
79
|
return `DATE '${lt.literal}'`;
|
|
82
80
|
}
|
|
83
81
|
const tz = lt.timezone || (0, dialect_1.qtz)(qi);
|
|
@@ -239,7 +239,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
239
239
|
sqlTruncExpr(qi, te) {
|
|
240
240
|
const tz = (0, dialect_1.qtz)(qi);
|
|
241
241
|
let truncThis = te.e.sql;
|
|
242
|
-
if (tz && te.e.
|
|
242
|
+
if (tz && malloy_types_1.TD.isTimestamp(te.e.typeDef)) {
|
|
243
243
|
truncThis = `CONVERT_TIMEZONE('${tz}',${truncThis})`;
|
|
244
244
|
}
|
|
245
245
|
return `DATE_TRUNC('${te.units}',${truncThis})`;
|
|
@@ -248,7 +248,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
248
248
|
const extractUnits = extractionMap[from.units] || from.units;
|
|
249
249
|
let extractFrom = from.e.sql;
|
|
250
250
|
const tz = (0, dialect_1.qtz)(qi);
|
|
251
|
-
if (tz && from.e.
|
|
251
|
+
if (tz && malloy_types_1.TD.isTimestamp(from.e.typeDef)) {
|
|
252
252
|
extractFrom = `CONVERT_TIMEZONE('${tz}', ${extractFrom})`;
|
|
253
253
|
}
|
|
254
254
|
return `EXTRACT(${extractUnits} FROM ${extractFrom})`;
|
|
@@ -271,10 +271,11 @@ ${(0, utils_1.indent)(sql)}
|
|
|
271
271
|
}
|
|
272
272
|
sqlCast(qi, cast) {
|
|
273
273
|
const src = cast.e.sql || '';
|
|
274
|
-
|
|
274
|
+
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
275
|
+
if (malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
275
276
|
return src;
|
|
276
277
|
}
|
|
277
|
-
if (cast.safe &&
|
|
278
|
+
if (cast.safe && !malloy_types_1.TD.isString(srcTypeDef)) {
|
|
278
279
|
// safe cast is only supported for a few combinations of src -> dst types
|
|
279
280
|
// so we will not support it in the general case
|
|
280
281
|
// see: https://docs.snowflake.com/en/sql-reference/functions/try_cast
|
|
@@ -283,22 +284,19 @@ ${(0, utils_1.indent)(sql)}
|
|
|
283
284
|
}
|
|
284
285
|
const tz = (0, dialect_1.qtz)(qi);
|
|
285
286
|
// casting timestamps and dates
|
|
286
|
-
if (
|
|
287
|
+
if (op === 'timestamp::date') {
|
|
287
288
|
let castExpr = src;
|
|
288
289
|
if (tz) {
|
|
289
290
|
castExpr = `CONVERT_TIMEZONE('${tz}', ${castExpr})`;
|
|
290
291
|
}
|
|
291
292
|
return `TO_DATE(${castExpr})`;
|
|
292
293
|
}
|
|
293
|
-
else if (
|
|
294
|
+
else if (op === 'date::timestamp') {
|
|
294
295
|
const retExpr = `TO_TIMESTAMP(${src})`;
|
|
295
296
|
return this.atTz(retExpr, tz);
|
|
296
297
|
}
|
|
297
|
-
const dstType = typeof cast.dstType === 'string'
|
|
298
|
-
? this.malloyTypeToSQLType({ type: cast.dstType })
|
|
299
|
-
: cast.dstType.raw;
|
|
300
298
|
const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
|
|
301
|
-
return `${castFunc}(${src} AS ${
|
|
299
|
+
return `${castFunc}(${src} AS ${dstSQLType})`;
|
|
302
300
|
}
|
|
303
301
|
sqlLiteralTime(qi, lf) {
|
|
304
302
|
var _a;
|
|
@@ -313,19 +311,16 @@ ${(0, utils_1.indent)(sql)}
|
|
|
313
311
|
ret = `${retTimeString} || ${targetTimeZoneSuffix}`;
|
|
314
312
|
ret = `(${ret})::TIMESTAMP_TZ`;
|
|
315
313
|
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
return `TO_DATE(${ret})`;
|
|
319
|
-
case 'timestamp': {
|
|
320
|
-
return ret;
|
|
321
|
-
}
|
|
314
|
+
if (malloy_types_1.TD.isDate(lf.typeDef)) {
|
|
315
|
+
return `TO_DATE(${ret})`;
|
|
322
316
|
}
|
|
317
|
+
return ret;
|
|
323
318
|
}
|
|
324
319
|
sqlMeasureTimeExpr(df) {
|
|
325
320
|
const from = df.kids.left;
|
|
326
321
|
const to = df.kids.right;
|
|
327
322
|
let extractUnits = 'nanoseconds';
|
|
328
|
-
if (from.
|
|
323
|
+
if (malloy_types_1.TD.isDate(from.typeDef) || malloy_types_1.TD.isDate(to.typeDef)) {
|
|
329
324
|
extractUnits = 'seconds';
|
|
330
325
|
}
|
|
331
326
|
return `TIMESTAMPDIFF(
|
|
@@ -308,7 +308,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
308
308
|
sqlTruncExpr(qi, trunc) {
|
|
309
309
|
const tz = qtz(qi);
|
|
310
310
|
const tzAdd = tz ? `, "${tz}"` : '';
|
|
311
|
-
if (trunc.e.
|
|
311
|
+
if (malloy_types_1.TD.isDate(trunc.e.typeDef)) {
|
|
312
312
|
if (dateMeasureable(trunc.units)) {
|
|
313
313
|
return `DATE_TRUNC(${trunc.e.sql},${trunc.units})`;
|
|
314
314
|
}
|
|
@@ -318,13 +318,13 @@ ${(0, utils_1.indent)(sql)}
|
|
|
318
318
|
}
|
|
319
319
|
sqlTimeExtractExpr(qi, te) {
|
|
320
320
|
const extractTo = extractMap[te.units] || te.units;
|
|
321
|
-
const tz = te.e.
|
|
321
|
+
const tz = malloy_types_1.TD.isTimestamp(te.e.typeDef) && qtz(qi);
|
|
322
322
|
const tzAdd = tz ? ` AT TIME ZONE '${tz}'` : '';
|
|
323
323
|
return `EXTRACT(${extractTo} FROM ${te.e.sql}${tzAdd})`;
|
|
324
324
|
}
|
|
325
325
|
sqlAlterTimeExpr(df) {
|
|
326
326
|
const from = df.kids.base;
|
|
327
|
-
let dataType = from.
|
|
327
|
+
let dataType = from === null || from === void 0 ? void 0 : from.typeDef.type;
|
|
328
328
|
let sql = from.sql;
|
|
329
329
|
if (df.units !== 'day' && timestampMeasureable(df.units)) {
|
|
330
330
|
// The units must be done in timestamp, no matter the input type
|
|
@@ -340,16 +340,16 @@ ${(0, utils_1.indent)(sql)}
|
|
|
340
340
|
const funcTail = df.op === '+' ? '_ADD' : '_SUB';
|
|
341
341
|
const funcName = `${dataType.toUpperCase()}${funcTail}`;
|
|
342
342
|
const newTime = `${funcName}(${sql}, INTERVAL ${df.kids.delta.sql} ${df.units})`;
|
|
343
|
-
if (dataType === from.
|
|
343
|
+
if (dataType === from.typeDef.type) {
|
|
344
344
|
return newTime;
|
|
345
345
|
}
|
|
346
|
-
return `${from.
|
|
346
|
+
return `${from.typeDef.type.toUpperCase()}(${newTime})`;
|
|
347
347
|
}
|
|
348
348
|
ignoreInProject(fieldName) {
|
|
349
349
|
return fieldName === '_PARTITIONTIME';
|
|
350
350
|
}
|
|
351
351
|
sqlCast(qi, cast) {
|
|
352
|
-
const op =
|
|
352
|
+
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
353
353
|
const tz = qtz(qi);
|
|
354
354
|
const src = cast.e.sql || '';
|
|
355
355
|
if (op === 'timestamp::date' && tz) {
|
|
@@ -358,12 +358,9 @@ ${(0, utils_1.indent)(sql)}
|
|
|
358
358
|
if (op === 'date::timestamp' && tz) {
|
|
359
359
|
return `TIMESTAMP(${src}, '${tz}')`;
|
|
360
360
|
}
|
|
361
|
-
if (
|
|
362
|
-
const dstType = typeof cast.dstType === 'string'
|
|
363
|
-
? this.malloyTypeToSQLType({ type: cast.dstType })
|
|
364
|
-
: cast.dstType.raw;
|
|
361
|
+
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
365
362
|
const castFunc = cast.safe ? 'SAFE_CAST' : 'CAST';
|
|
366
|
-
return `${castFunc}(${src} AS ${
|
|
363
|
+
return `${castFunc}(${src} AS ${dstSQLType})`;
|
|
367
364
|
}
|
|
368
365
|
return src;
|
|
369
366
|
}
|
|
@@ -371,10 +368,10 @@ ${(0, utils_1.indent)(sql)}
|
|
|
371
368
|
return `REGEXP_CONTAINS(${match.kids.expr.sql},${match.kids.regex.sql})`;
|
|
372
369
|
}
|
|
373
370
|
sqlLiteralTime(qi, lit) {
|
|
374
|
-
if (lit.
|
|
371
|
+
if (malloy_types_1.TD.isDate(lit.typeDef)) {
|
|
375
372
|
return `DATE('${lit.literal}')`;
|
|
376
373
|
}
|
|
377
|
-
else if (lit.
|
|
374
|
+
else if (malloy_types_1.TD.isTimestamp(lit.typeDef)) {
|
|
378
375
|
let timestampArgs = `'${lit.literal}'`;
|
|
379
376
|
const tz = lit.timezone || qtz(qi);
|
|
380
377
|
if (tz && tz !== 'UTC') {
|
|
@@ -383,7 +380,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
383
380
|
return `TIMESTAMP(${timestampArgs})`;
|
|
384
381
|
}
|
|
385
382
|
else {
|
|
386
|
-
throw new Error(`Unsupported Literal time format ${lit.
|
|
383
|
+
throw new Error(`Unsupported Literal time format ${lit.typeDef}`);
|
|
387
384
|
}
|
|
388
385
|
}
|
|
389
386
|
sqlMeasureTimeExpr(measure) {
|
|
@@ -405,10 +402,10 @@ ${(0, utils_1.indent)(sql)}
|
|
|
405
402
|
if (!timestampMeasureable(measureIn)) {
|
|
406
403
|
throw new Error(`Measure in '${measureIn} not implemented`);
|
|
407
404
|
}
|
|
408
|
-
if (from.
|
|
405
|
+
if (!malloy_types_1.TD.eq(from.typeDef, to.typeDef)) {
|
|
409
406
|
throw new Error("Can't measure difference between different types");
|
|
410
407
|
}
|
|
411
|
-
if (from.
|
|
408
|
+
if (malloy_types_1.TD.isDate(from.typeDef)) {
|
|
412
409
|
lVal = `TIMESTAMP(${lVal})`;
|
|
413
410
|
rVal = `TIMESTAMP(${rVal})`;
|
|
414
411
|
}
|
|
@@ -370,22 +370,19 @@ ${(0, utils_1.indent)(sql)}
|
|
|
370
370
|
return `DATE_ADD('${timeframe}', ${n}, ${df.kids.base.sql})`;
|
|
371
371
|
}
|
|
372
372
|
sqlCast(qi, cast) {
|
|
373
|
-
const op =
|
|
373
|
+
const { op, srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
374
374
|
const tz = qtz(qi);
|
|
375
375
|
const expr = cast.e.sql || '';
|
|
376
|
-
if (op === 'timestamp
|
|
376
|
+
if (op === 'timestamp::date' && tz) {
|
|
377
377
|
const tstz = `CAST(${expr} as TIMESTAMP)`;
|
|
378
378
|
return `CAST((${tstz}) AT TIME ZONE '${tz}' AS DATE)`;
|
|
379
379
|
}
|
|
380
|
-
else if (op === 'date
|
|
380
|
+
else if (op === 'date::timestamp' && tz) {
|
|
381
381
|
return `CAST(CONCAT(CAST(CAST(${expr} AS TIMESTAMP) AS VARCHAR), ' ${tz}') AS TIMESTAMP WITH TIME ZONE)`;
|
|
382
382
|
}
|
|
383
|
-
if (
|
|
384
|
-
const dstType = typeof cast.dstType === 'string'
|
|
385
|
-
? this.malloyTypeToSQLType({ type: cast.dstType })
|
|
386
|
-
: cast.dstType.raw;
|
|
383
|
+
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
387
384
|
const castFunc = cast.safe ? 'TRY_CAST' : 'CAST';
|
|
388
|
-
return `${castFunc}(${expr} AS ${
|
|
385
|
+
return `${castFunc}(${expr} AS ${dstSQLType})`;
|
|
389
386
|
}
|
|
390
387
|
return expr;
|
|
391
388
|
}
|
|
@@ -411,10 +408,10 @@ ${(0, utils_1.indent)(sql)}
|
|
|
411
408
|
if (!timestampMeasureable(measureIn)) {
|
|
412
409
|
throw new Error(`Measure in '${measureIn} not implemented`);
|
|
413
410
|
}
|
|
414
|
-
if (from.
|
|
411
|
+
if (!malloy_types_1.TD.eq(from.typeDef, to.typeDef)) {
|
|
415
412
|
throw new Error("Can't measure difference between different types");
|
|
416
413
|
}
|
|
417
|
-
if (from.
|
|
414
|
+
if (malloy_types_1.TD.isDate(from.typeDef)) {
|
|
418
415
|
lVal = `CAST(${lVal} AS TIMESTAMP)`;
|
|
419
416
|
rVal = `CAST(${rVal} AS TIMESTAMP)`;
|
|
420
417
|
}
|
|
@@ -496,7 +493,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
496
493
|
return sqlType.match(/^[A-Za-z\s(),<>0-9]*$/) !== null;
|
|
497
494
|
}
|
|
498
495
|
sqlLiteralTime(qi, lit) {
|
|
499
|
-
if (lit.
|
|
496
|
+
if (malloy_types_1.TD.isDate(lit.typeDef)) {
|
|
500
497
|
return `DATE '${lit.literal}'`;
|
|
501
498
|
}
|
|
502
499
|
const tz = lit.timezone || qtz(qi);
|
|
@@ -508,7 +505,7 @@ ${(0, utils_1.indent)(sql)}
|
|
|
508
505
|
sqlTimeExtractExpr(qi, from) {
|
|
509
506
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
510
507
|
let extractFrom = from.e.sql || '';
|
|
511
|
-
if (from.e.
|
|
508
|
+
if (malloy_types_1.TD.isTimestamp(from.e.typeDef)) {
|
|
512
509
|
const tz = qtz(qi);
|
|
513
510
|
if (tz) {
|
|
514
511
|
extractFrom = `at_timezone(${extractFrom},'${tz}')`;
|
|
@@ -61,7 +61,7 @@ class ExprGranularTime extends expression_def_1.ExpressionDef {
|
|
|
61
61
|
if (this.truncate) {
|
|
62
62
|
tsVal.value = {
|
|
63
63
|
node: 'trunc',
|
|
64
|
-
e:
|
|
64
|
+
e: (0, malloy_types_1.mkTemporal)(exprVal.value, exprVal.dataType),
|
|
65
65
|
units: timeframe,
|
|
66
66
|
};
|
|
67
67
|
}
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.RecordLiteral = exports.RecordElement = void 0;
|
|
10
|
-
const model_1 = require("../../../model");
|
|
11
10
|
const expression_def_1 = require("../types/expression-def");
|
|
12
11
|
const malloy_element_1 = require("../types/malloy-element");
|
|
13
12
|
class RecordElement extends malloy_element_1.MalloyElement {
|
|
@@ -27,30 +26,34 @@ class RecordLiteral extends expression_def_1.ExpressionDef {
|
|
|
27
26
|
this.elementType = 'record literal';
|
|
28
27
|
this.has({ pairs });
|
|
29
28
|
}
|
|
30
|
-
getExpression(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
29
|
+
getExpression(_fs) {
|
|
30
|
+
throw new Error('get expression on record todo');
|
|
31
|
+
// const recLit: RecordLiteralNode = {
|
|
32
|
+
// node: 'recordLiteral',
|
|
33
|
+
// kids: {},
|
|
34
|
+
// };
|
|
35
|
+
// let resultExprType: ExpressionType = 'scalar';
|
|
36
|
+
// for (const el of this.pairs) {
|
|
37
|
+
// const xVal = el.value.getExpression(fs);
|
|
38
|
+
// const expr: TypedExpr = {typeDef: 'error', ...xVal.value};
|
|
39
|
+
// if (TD.isError(expr.typeDef) && isAtomicFieldType(xVal.dataType)) {
|
|
40
|
+
// expr.typeDef = xVal.dataType;
|
|
41
|
+
// }
|
|
42
|
+
// if (TD.isError(expr.typeDef) && xVal.dataType !== 'error') {
|
|
43
|
+
// this.logError(
|
|
44
|
+
// 'illegal-record-property-type',
|
|
45
|
+
// `Type '${xVal.dataType}' not a legal record value`
|
|
46
|
+
// );
|
|
47
|
+
// }
|
|
48
|
+
// recLit.kids[el.key] = expr;
|
|
49
|
+
// resultExprType = maxExpressionType(xVal.expressionType, resultExprType);
|
|
50
|
+
// }
|
|
51
|
+
// return {
|
|
52
|
+
// dataType: 'record',
|
|
53
|
+
// value: recLit,
|
|
54
|
+
// expressionType: resultExprType,
|
|
55
|
+
// evalSpace: 'literal',
|
|
56
|
+
// };
|
|
54
57
|
}
|
|
55
58
|
}
|
|
56
59
|
exports.RecordLiteral = RecordLiteral;
|
|
@@ -105,8 +105,8 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
|
|
|
105
105
|
node: 'timeDiff',
|
|
106
106
|
units: extractTo,
|
|
107
107
|
kids: {
|
|
108
|
-
left:
|
|
109
|
-
right:
|
|
108
|
+
left: (0, malloy_types_1.mkTemporal)(first.value, valueType),
|
|
109
|
+
right: (0, malloy_types_1.mkTemporal)(last.value, valueType),
|
|
110
110
|
},
|
|
111
111
|
},
|
|
112
112
|
};
|
|
@@ -120,7 +120,7 @@ class ExprTimeExtract extends expression_def_1.ExpressionDef {
|
|
|
120
120
|
evalSpace: argV.evalSpace,
|
|
121
121
|
value: {
|
|
122
122
|
node: 'extract',
|
|
123
|
-
e:
|
|
123
|
+
e: (0, malloy_types_1.mkTemporal)(argV.value, argV.dataType),
|
|
124
124
|
units: extractTo,
|
|
125
125
|
},
|
|
126
126
|
};
|
|
@@ -46,11 +46,11 @@ class ExprTime extends expression_def_1.ExpressionDef {
|
|
|
46
46
|
const toTs = {
|
|
47
47
|
node: 'cast',
|
|
48
48
|
safe: false,
|
|
49
|
-
dstType: timeType,
|
|
49
|
+
dstType: { type: timeType },
|
|
50
50
|
e: expr.value,
|
|
51
51
|
};
|
|
52
52
|
if ((0, malloy_types_1.isTemporalField)(expr.dataType)) {
|
|
53
|
-
toTs.srcType = expr.dataType;
|
|
53
|
+
toTs.srcType = { type: expr.dataType };
|
|
54
54
|
}
|
|
55
55
|
value = toTs;
|
|
56
56
|
}
|
|
@@ -84,7 +84,7 @@ class ForRange extends expression_def_1.ExpressionDef {
|
|
|
84
84
|
from = tsVersion;
|
|
85
85
|
}
|
|
86
86
|
else {
|
|
87
|
-
from = (0, time_utils_1.
|
|
87
|
+
from = (0, time_utils_1.castTo)('timestamp', from, 'date');
|
|
88
88
|
}
|
|
89
89
|
rangeStart = new expr_time_1.ExprTime('timestamp', from, startV.expressionType);
|
|
90
90
|
}
|
|
@@ -14,6 +14,7 @@ export declare class IndexBuilder implements QueryBuilder {
|
|
|
14
14
|
sample?: Sampling;
|
|
15
15
|
resultFS: IndexFieldSpace;
|
|
16
16
|
inputFS: QueryInputSpace;
|
|
17
|
+
alwaysJoins: string[];
|
|
17
18
|
readonly type = "index";
|
|
18
19
|
constructor(inputFS: SourceFieldSpace, refineThis: PipeSegment | undefined, isNestIn: QueryOperationSpace | undefined, astEl: MalloyElement);
|
|
19
20
|
execute(qp: QueryProperty): void;
|
|
@@ -33,6 +33,7 @@ const index_field_space_1 = require("../field-space/index-field-space");
|
|
|
33
33
|
class IndexBuilder {
|
|
34
34
|
constructor(inputFS, refineThis, isNestIn, astEl) {
|
|
35
35
|
this.filters = [];
|
|
36
|
+
this.alwaysJoins = [];
|
|
36
37
|
this.type = 'index';
|
|
37
38
|
this.resultFS = new index_field_space_1.IndexFieldSpace(inputFS, refineThis, isNestIn, astEl);
|
|
38
39
|
this.inputFS = this.resultFS.inputSpace();
|
|
@@ -91,6 +92,9 @@ class IndexBuilder {
|
|
|
91
92
|
if (this.sample) {
|
|
92
93
|
indexSegment.sample = this.sample;
|
|
93
94
|
}
|
|
95
|
+
if (this.alwaysJoins.length > 0) {
|
|
96
|
+
indexSegment.alwaysJoins = [...this.alwaysJoins];
|
|
97
|
+
}
|
|
94
98
|
return indexSegment;
|
|
95
99
|
}
|
|
96
100
|
}
|
|
@@ -10,6 +10,7 @@ import { MalloyElement } from '../types/malloy-element';
|
|
|
10
10
|
export declare abstract class QuerySegmentBuilder implements QueryBuilder {
|
|
11
11
|
order?: Top | Ordering;
|
|
12
12
|
limit?: number;
|
|
13
|
+
alwaysJoins: string[];
|
|
13
14
|
abstract inputFS: QueryInputSpace;
|
|
14
15
|
abstract resultFS: QueryOperationSpace;
|
|
15
16
|
abstract readonly type: 'grouping' | 'project';
|
|
@@ -33,6 +33,7 @@ const query_spaces_1 = require("../field-space/query-spaces");
|
|
|
33
33
|
const definition_list_1 = require("../types/definition-list");
|
|
34
34
|
class QuerySegmentBuilder {
|
|
35
35
|
constructor() {
|
|
36
|
+
this.alwaysJoins = [];
|
|
36
37
|
this.filters = [];
|
|
37
38
|
}
|
|
38
39
|
execute(qp) {
|
|
@@ -113,6 +114,9 @@ class QuerySegmentBuilder {
|
|
|
113
114
|
else if (oldFilters) {
|
|
114
115
|
to.filterList = [...oldFilters, ...this.filters];
|
|
115
116
|
}
|
|
117
|
+
if (this.alwaysJoins.length > 0) {
|
|
118
|
+
to.alwaysJoins = [...this.alwaysJoins];
|
|
119
|
+
}
|
|
116
120
|
}
|
|
117
121
|
}
|
|
118
122
|
exports.QuerySegmentBuilder = QuerySegmentBuilder;
|
|
@@ -4,7 +4,5 @@ export declare function timeOffset(timeType: TemporalFieldType, from: Expr, op:
|
|
|
4
4
|
export declare function castTo(castType: CastType | {
|
|
5
5
|
raw: string;
|
|
6
6
|
}, from: Expr, fromType: FieldValueType, safe?: boolean): TypecastExpr;
|
|
7
|
-
export declare function castTimestampToDate(from: Expr, safe?: boolean): TypecastExpr;
|
|
8
|
-
export declare function castDateToTimestamp(from: Expr, safe?: boolean): TypecastExpr;
|
|
9
7
|
export declare function resolution(timeframe: string): TemporalFieldType;
|
|
10
|
-
export declare function
|
|
8
|
+
export declare function mkTimeResult(t: TimeResult, tt: TimestampUnit | undefined): TimeResult;
|