@malloydata/malloy 0.0.434 → 0.0.436
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/build_targets.d.ts +5 -6
- package/dist/api/foundation/build_targets.js +74 -6
- package/dist/api/foundation/compile.d.ts +0 -1
- package/dist/api/foundation/compile.js +0 -3
- package/dist/api/foundation/core.d.ts +16 -4
- package/dist/api/foundation/core.js +23 -23
- package/dist/api/foundation/index.d.ts +1 -1
- package/dist/api/foundation/runtime.d.ts +21 -3
- package/dist/api/foundation/runtime.js +45 -54
- package/dist/api/foundation/types.d.ts +43 -4
- package/dist/connection/dbmeta.d.ts +29 -0
- package/dist/connection/dbmeta.js +46 -0
- package/dist/connection/index.d.ts +1 -0
- package/dist/connection/index.js +1 -0
- package/dist/dialect/databricks/databricks.d.ts +11 -12
- package/dist/dialect/databricks/databricks.js +24 -36
- package/dist/dialect/dialect.d.ts +70 -171
- package/dist/dialect/dialect.js +123 -92
- package/dist/dialect/dialect_map.js +2 -0
- package/dist/dialect/duckdb/duckdb.d.ts +2 -2
- package/dist/dialect/duckdb/duckdb.js +1 -1
- package/dist/dialect/index.d.ts +3 -2
- package/dist/dialect/index.js +4 -2
- package/dist/dialect/mysql/mysql.d.ts +11 -12
- package/dist/dialect/mysql/mysql.js +20 -32
- package/dist/dialect/pg_impl.d.ts +9 -10
- package/dist/dialect/pg_impl.js +27 -73
- package/dist/dialect/postgres/postgres.d.ts +4 -4
- package/dist/dialect/postgres/postgres.js +4 -12
- package/dist/dialect/snowflake/snowflake.d.ts +11 -13
- package/dist/dialect/snowflake/snowflake.js +30 -82
- 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 +101 -0
- package/dist/dialect/sqlserver/sqlserver.js +537 -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 +11 -16
- package/dist/dialect/standardsql/standardsql.js +22 -73
- package/dist/dialect/trino/trino.d.ts +16 -18
- package/dist/dialect/trino/trino.js +127 -181
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -4
- package/dist/lang/ast/expressions/boolean-value.d.ts +24 -0
- package/dist/lang/ast/expressions/boolean-value.js +41 -0
- package/dist/lang/ast/expressions/case.js +7 -0
- package/dist/lang/ast/expressions/expr-alternation-tree.js +11 -0
- package/dist/lang/ast/expressions/expr-cast.js +5 -0
- package/dist/lang/ast/expressions/expr-coalesce.js +4 -0
- package/dist/lang/ast/expressions/expr-func.js +13 -2
- package/dist/lang/ast/expressions/expr-minus.js +5 -2
- package/dist/lang/ast/expressions/expr-null.js +4 -0
- package/dist/lang/ast/expressions/pick-when.js +7 -0
- package/dist/lang/ast/field-space/index-field-space.js +13 -1
- package/dist/lang/ast/field-space/query-spaces.js +11 -0
- package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
- package/dist/lang/ast/query-items/field-declaration.d.ts +5 -0
- package/dist/lang/ast/query-items/field-declaration.js +22 -16
- package/dist/lang/ast/query-items/field-references.d.ts +6 -0
- package/dist/lang/ast/query-items/field-references.js +23 -0
- package/dist/lang/ast/query-properties/filters.js +3 -1
- package/dist/lang/ast/source-elements/named-source.js +0 -2
- package/dist/lang/ast/source-elements/sql-source.js +0 -3
- package/dist/lang/ast/source-elements/table-source.js +1 -2
- package/dist/lang/ast/statements/import-statement.js +17 -0
- package/dist/lang/ast/types/expression-def.js +17 -2
- package/dist/lang/ast/types/malloy-element.d.ts +24 -8
- package/dist/lang/ast/types/malloy-element.js +63 -44
- package/dist/lang/ast/view-elements/reference-view.js +4 -0
- package/dist/lang/ast/view-elements/refine-utils.js +1 -0
- package/dist/lang/composite-source-utils.js +10 -17
- package/dist/lang/parse-log.d.ts +12 -0
- package/dist/lang/parse-log.js +2 -0
- package/dist/lang/parse-malloy.d.ts +17 -2
- package/dist/lang/parse-malloy.js +48 -14
- 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 +11 -1
- package/dist/lang/test/test-translator.js +51 -3
- package/dist/model/expression_compiler.js +9 -8
- package/dist/model/filter_compilers.js +13 -12
- package/dist/model/given_binding.d.ts +8 -0
- package/dist/model/given_binding.js +13 -0
- package/dist/model/index.d.ts +1 -1
- package/dist/model/index.js +2 -1
- package/dist/model/query_query.d.ts +3 -1
- package/dist/model/query_query.js +157 -139
- package/dist/model/source_def_utils.d.ts +7 -1
- package/dist/model/source_def_utils.js +20 -0
- package/dist/model/sql_compiled.d.ts +19 -20
- package/dist/model/sql_compiled.js +29 -17
- 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 +13 -1
- package/package.json +7 -5
package/dist/dialect/dialect.js
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.Dialect = exports.dayIndex = exports.EscapeStyle = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
|
|
7
|
+
exports.Dialect = exports.dayIndex = exports.CivilTime = exports.EscapeStyle = exports.MAX_DECIMAL38 = exports.MIN_DECIMAL38 = exports.MAX_INT128 = exports.MIN_INT128 = exports.MAX_INT64 = exports.MIN_INT64 = exports.MAX_INT32 = exports.MIN_INT32 = void 0;
|
|
8
8
|
exports.inDays = inDays;
|
|
9
|
-
exports.
|
|
9
|
+
exports.queryZone = queryZone;
|
|
10
10
|
exports.turtleGroupSetCondition = turtleGroupSetCondition;
|
|
11
11
|
const malloy_types_1 = require("../model/malloy_types");
|
|
12
12
|
const table_path_1 = require("./table-path");
|
|
@@ -37,6 +37,14 @@ exports.EscapeStyle = {
|
|
|
37
37
|
Backslash: 'backslash',
|
|
38
38
|
Unset: 'unset',
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* The two kinds of civil time: a calendar date, and a date with a time of
|
|
42
|
+
* day. Neither has a zone.
|
|
43
|
+
*/
|
|
44
|
+
exports.CivilTime = {
|
|
45
|
+
Date: 'date',
|
|
46
|
+
DateAndTime: 'date_and_time',
|
|
47
|
+
};
|
|
40
48
|
const allUnits = [
|
|
41
49
|
'microsecond',
|
|
42
50
|
'millisecond',
|
|
@@ -53,14 +61,10 @@ exports.dayIndex = allUnits.indexOf('day');
|
|
|
53
61
|
function inDays(units) {
|
|
54
62
|
return allUnits.indexOf(units) >= exports.dayIndex;
|
|
55
63
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (tz === undefined || tz === qi.systemTimezone) {
|
|
61
|
-
return undefined;
|
|
62
|
-
}
|
|
63
|
-
return tz;
|
|
64
|
+
/** The query time zone, or UTC when the query has none. */
|
|
65
|
+
function queryZone(qi) {
|
|
66
|
+
var _a;
|
|
67
|
+
return (_a = qi.queryTimezone) !== null && _a !== void 0 ? _a : 'UTC';
|
|
64
68
|
}
|
|
65
69
|
/**
|
|
66
70
|
* The per-element condition for an aggregate turtle (sqlAggregateTurtle). On
|
|
@@ -81,6 +85,12 @@ function turtleGroupSetCondition(groupSet, filterSQL) {
|
|
|
81
85
|
}
|
|
82
86
|
class Dialect {
|
|
83
87
|
constructor() {
|
|
88
|
+
/**
|
|
89
|
+
* What the compiler consults, not `experimental` itself. The test harness
|
|
90
|
+
* calls `acceptExperimental()` on the dialect under test so the shared
|
|
91
|
+
* suites can run on an experimental dialect without every test enabling it.
|
|
92
|
+
*/
|
|
93
|
+
this.experimentalAccepted = false;
|
|
84
94
|
// -- we should add flags with default values from now on so as to not break
|
|
85
95
|
// dialects outside our repository
|
|
86
96
|
//
|
|
@@ -106,6 +116,9 @@ class Dialect {
|
|
|
106
116
|
this.readsNestedData = true;
|
|
107
117
|
// ORDER BY 1 DESC
|
|
108
118
|
this.orderByClause = 'ordinal';
|
|
119
|
+
// GROUP BY 1, or GROUP BY the dimension expressions for an engine whose
|
|
120
|
+
// GROUP BY takes no ordinals
|
|
121
|
+
this.groupByClause = 'ordinal';
|
|
109
122
|
// null will match in a function signature
|
|
110
123
|
this.nullMatchesFunctionSignature = true;
|
|
111
124
|
// support select * replace(...)
|
|
@@ -147,6 +160,8 @@ class Dialect {
|
|
|
147
160
|
this.booleanType = 'supported';
|
|
148
161
|
// Like characters are escaped with ESCAPE clause
|
|
149
162
|
this.likeEscape = true;
|
|
163
|
+
// Characters LIKE reads as wildcards beyond % and _, escaped like them
|
|
164
|
+
this.likeExtraWildcards = [];
|
|
150
165
|
/**
|
|
151
166
|
* Mappings from integer value ranges to Malloy number types.
|
|
152
167
|
*
|
|
@@ -209,6 +224,12 @@ class Dialect {
|
|
|
209
224
|
*/
|
|
210
225
|
this.identifierEscapeStyle = exports.EscapeStyle.Unset;
|
|
211
226
|
}
|
|
227
|
+
isExperimental() {
|
|
228
|
+
return this.experimental && !this.experimentalAccepted;
|
|
229
|
+
}
|
|
230
|
+
acceptExperimental() {
|
|
231
|
+
this.experimentalAccepted = true;
|
|
232
|
+
}
|
|
212
233
|
// Generate the lateral join bag clause for window function partitioning.
|
|
213
234
|
// The expressions are dimension fields that need to be referenced by name
|
|
214
235
|
// in PARTITION BY clauses. Must be overridden by any dialect that sets
|
|
@@ -219,6 +240,14 @@ class Dialect {
|
|
|
219
240
|
}
|
|
220
241
|
throw new Error('Internal error: sqlLateralJoinBag called but cantPartitionWindowFunctionsOnExpressions is false');
|
|
221
242
|
}
|
|
243
|
+
/** The ordering and row limit which end a SELECT stage */
|
|
244
|
+
sqlOrderByLimit(orderTerms, limit) {
|
|
245
|
+
let s = orderTerms.length > 0 ? this.sqlOrderBy(orderTerms, 'query') + '\n' : '';
|
|
246
|
+
if (limit !== undefined) {
|
|
247
|
+
s += `LIMIT ${limit}\n`;
|
|
248
|
+
}
|
|
249
|
+
return s;
|
|
250
|
+
}
|
|
222
251
|
/**
|
|
223
252
|
* Determine the Malloy number type for a numeric literal.
|
|
224
253
|
*/
|
|
@@ -406,96 +435,70 @@ class Dialect {
|
|
|
406
435
|
ignoreInProject(_fieldName) {
|
|
407
436
|
return false;
|
|
408
437
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
* Default implementation:
|
|
417
|
-
* - Returns true for timestamps with truncation or calendar-unit offsets
|
|
418
|
-
* - Uses the query timezone from QueryInfo, or 'UTC' if none specified
|
|
419
|
-
* - Returns false for dates or sub-day offsets (hour, minute, second)
|
|
420
|
-
*
|
|
421
|
-
* Dialects can override this if they have different rules about which operations
|
|
422
|
-
* require civil time computation.
|
|
423
|
-
*
|
|
424
|
-
* @param typeDef The Malloy type of the base expression
|
|
425
|
-
* @param truncateTo The truncation unit, if any
|
|
426
|
-
* @param offsetUnit The offset unit, if any
|
|
427
|
-
* @param qi Query information including timezone settings
|
|
428
|
-
* @returns Object with `needed` boolean and optional `tz` string
|
|
429
|
-
*/
|
|
430
|
-
needsCivilTimeComputation(typeDef, truncateTo, offsetUnit, qi) {
|
|
431
|
-
// Calendar units that can cross DST boundaries
|
|
432
|
-
const calendarUnits = ['day', 'week', 'month', 'quarter', 'year'];
|
|
433
|
-
const isCalendarTruncate = truncateTo !== undefined && calendarUnits.includes(truncateTo);
|
|
434
|
-
const isCalendarOffset = offsetUnit !== undefined && calendarUnits.includes(offsetUnit);
|
|
435
|
-
const tz = qtz(qi);
|
|
436
|
-
// Timestamps with calendar truncation/offset need civil time computation
|
|
437
|
-
// BUT only if there's actually a timezone to convert to/from
|
|
438
|
-
const needed = malloy_types_1.TD.isAnyTimestamp(typeDef) &&
|
|
439
|
-
(isCalendarTruncate || isCalendarOffset) &&
|
|
440
|
-
tz !== undefined;
|
|
441
|
-
return { needed, tz };
|
|
438
|
+
sqlTimeExtractExpr(qi, from) {
|
|
439
|
+
const time = from.e;
|
|
440
|
+
if (malloy_types_1.TD.isAnyTimestamp(time.typeDef)) {
|
|
441
|
+
const civil = this.sqlConvertToCivilTime(time.sql, queryZone(qi), time.typeDef);
|
|
442
|
+
return this.sqlCivilExtract(civil, from.units, exports.CivilTime.DateAndTime);
|
|
443
|
+
}
|
|
444
|
+
return this.sqlCivilExtract(time.sql, from.units, exports.CivilTime.Date);
|
|
442
445
|
}
|
|
443
446
|
/**
|
|
444
|
-
*
|
|
445
|
-
*
|
|
446
|
-
*
|
|
447
|
-
* Much of the complexity has to do with getting timezone values set up so that
|
|
448
|
-
* they will truncate/offset in the query timezone instead of UTC.
|
|
449
|
-
*
|
|
450
|
-
* The intention is that this implementation will work for all dialects, and all
|
|
451
|
-
* the dialect peculiarities are handled with the new primitives introduced to
|
|
452
|
-
* support this function:
|
|
453
|
-
* - needsCivilTimeComputation: Determines if operation needs civil time
|
|
454
|
-
* - sqlConvertToCivilTime/sqlConvertFromCivilTime: Timezone conversion
|
|
455
|
-
* - sqlTruncate: Truncation operation
|
|
456
|
-
* - sqlOffsetTime: Interval arithmetic
|
|
457
|
-
*
|
|
458
|
-
* OFFSET TIMESTAMP BEHAVIOR:
|
|
459
|
-
* - Plain timestamps (offset=false): Always return plain TIMESTAMP in UTC
|
|
460
|
-
* - Offset timestamps (offset=true):
|
|
461
|
-
* - Simple path: Operate in native embedded timezone, return TIMESTAMPTZ
|
|
462
|
-
* - Civil path: Convert to query timezone (preserving instant), operate there,
|
|
463
|
-
* return TIMESTAMPTZ in query timezone via sqlConvertFromCivilTime
|
|
464
|
-
* - BigQuery/MySQL: No offset timestamp support, this logic never runs
|
|
465
|
-
*
|
|
466
|
-
* @param baseExpr The time expression to operate on (already compiled, with .sql populated)
|
|
467
|
-
* @param qi Query information including timezone
|
|
468
|
-
* @param truncateTo Optional truncation unit (year, month, day, etc.)
|
|
469
|
-
* @param offset Optional offset to apply (after truncation if both present)
|
|
447
|
+
* Truncation and/or offset, the offset applied after the truncation.
|
|
448
|
+
* Doing both in one pass converts to and from civil time only once.
|
|
470
449
|
*/
|
|
471
450
|
sqlTruncAndOffset(baseExpr, qi, truncateTo, offset) {
|
|
472
|
-
|
|
473
|
-
if (malloy_types_1.TD.isAnyTimestamp(
|
|
474
|
-
|
|
475
|
-
if (needsCivil && tz) {
|
|
476
|
-
// Civil time path: convert to local time, operate, convert back to UTC
|
|
477
|
-
const civilResult = this.sqlConvertToCivilTime(baseExpr.sql, tz, baseExpr.typeDef);
|
|
478
|
-
let expr = civilResult.sql;
|
|
479
|
-
const civilTypeDef = civilResult.typeDef;
|
|
480
|
-
if (truncateTo) {
|
|
481
|
-
expr = this.sqlTruncate(expr, truncateTo, civilTypeDef, true, tz);
|
|
482
|
-
}
|
|
483
|
-
if (offset) {
|
|
484
|
-
expr = this.sqlOffsetTime(expr, offset.op, offset.magnitude, offset.unit, civilTypeDef, true, tz);
|
|
485
|
-
}
|
|
486
|
-
return this.sqlConvertFromCivilTime(expr, tz, baseExpr.typeDef);
|
|
487
|
-
}
|
|
451
|
+
const timeType = baseExpr.typeDef;
|
|
452
|
+
if (!malloy_types_1.TD.isAnyTimestamp(timeType)) {
|
|
453
|
+
return this.civilTruncAndOffset(baseExpr.sql, exports.CivilTime.Date, truncateTo, offset);
|
|
488
454
|
}
|
|
489
|
-
|
|
490
|
-
|
|
455
|
+
const usesQueryZone = truncateTo !== undefined ||
|
|
456
|
+
(offset !== undefined && (0, malloy_types_1.isDateUnit)(offset.unit));
|
|
457
|
+
const zone = usesQueryZone ? queryZone(qi) : 'UTC';
|
|
458
|
+
const civil = this.sqlConvertToCivilTime(baseExpr.sql, zone, timeType);
|
|
459
|
+
return this.sqlConvertFromCivilTime(this.civilTruncAndOffset(civil, exports.CivilTime.DateAndTime, truncateTo, offset), zone, timeType);
|
|
460
|
+
}
|
|
461
|
+
civilTruncAndOffset(expr, civilType, truncateTo, offset) {
|
|
462
|
+
let sql = expr;
|
|
491
463
|
if (truncateTo) {
|
|
492
|
-
sql = this.sqlTruncate(sql, truncateTo,
|
|
464
|
+
sql = this.sqlTruncate(sql, truncateTo, civilType);
|
|
493
465
|
}
|
|
494
466
|
if (offset) {
|
|
495
|
-
sql = this.sqlOffsetTime(sql, offset.op, offset.magnitude, offset.unit,
|
|
467
|
+
sql = this.sqlOffsetTime(sql, offset.op, offset.magnitude, offset.unit, civilType);
|
|
496
468
|
}
|
|
497
469
|
return sql;
|
|
498
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* A cast between date, timestamp and timestamptz, or undefined for any
|
|
473
|
+
* other cast.
|
|
474
|
+
*/
|
|
475
|
+
sqlTimeCast(qi, cast) {
|
|
476
|
+
var _a;
|
|
477
|
+
const { srcTypeDef, dstTypeDef } = this.sqlCastPrep(cast);
|
|
478
|
+
const expr = (_a = cast.e.sql) !== null && _a !== void 0 ? _a : '';
|
|
479
|
+
if (malloy_types_1.TD.isAnyTimestamp(srcTypeDef)) {
|
|
480
|
+
if (malloy_types_1.TD.isDate(dstTypeDef)) {
|
|
481
|
+
return this.sqlCivilToDate(this.sqlConvertToCivilTime(expr, queryZone(qi), srcTypeDef));
|
|
482
|
+
}
|
|
483
|
+
if (malloy_types_1.TD.isAnyTimestamp(dstTypeDef) &&
|
|
484
|
+
dstTypeDef.type !== srcTypeDef.type) {
|
|
485
|
+
// The same instant in another SQL type. A round trip through civil
|
|
486
|
+
// time in any zone keeps the instant; UTC costs the least.
|
|
487
|
+
return this.sqlConvertFromCivilTime(this.sqlConvertToCivilTime(expr, 'UTC', srcTypeDef), 'UTC', dstTypeDef);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
if (malloy_types_1.TD.isDate(srcTypeDef) && malloy_types_1.TD.isAnyTimestamp(dstTypeDef)) {
|
|
491
|
+
return this.sqlConvertFromCivilTime(this.sqlDateToCivil(expr), queryZone(qi), dstTypeDef);
|
|
492
|
+
}
|
|
493
|
+
return undefined;
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* The timestamp at which `timezone`, or the query time zone when there is
|
|
497
|
+
* none, shows the civil time `literal`.
|
|
498
|
+
*/
|
|
499
|
+
sqlTimestampLiteral(qi, literal, timezone) {
|
|
500
|
+
return this.sqlConvertFromCivilTime(this.sqlCivilLiteral(literal), timezone !== null && timezone !== void 0 ? timezone : queryZone(qi), { type: 'timestamp' });
|
|
501
|
+
}
|
|
499
502
|
/**
|
|
500
503
|
* Reject characters that can break SQL literals or
|
|
501
504
|
* be changed by SQL formatting. Timezone-name validity belongs to the
|
|
@@ -549,6 +552,7 @@ class Dialect {
|
|
|
549
552
|
* @returns The SQL translation of the expression, or undefined
|
|
550
553
|
*/
|
|
551
554
|
exprToSQL(qi, df) {
|
|
555
|
+
var _a;
|
|
552
556
|
switch (df.node) {
|
|
553
557
|
case 'now':
|
|
554
558
|
return this.sqlNowExpr();
|
|
@@ -578,7 +582,7 @@ class Dialect {
|
|
|
578
582
|
case 'extract':
|
|
579
583
|
return this.sqlTimeExtractExpr(qi, df);
|
|
580
584
|
case 'cast':
|
|
581
|
-
return this.sqlCast(qi, df);
|
|
585
|
+
return (_a = this.sqlTimeCast(qi, df)) !== null && _a !== void 0 ? _a : this.sqlCast(qi, df);
|
|
582
586
|
case 'regexpMatch':
|
|
583
587
|
return this.sqlRegexpMatch(df);
|
|
584
588
|
case '/': {
|
|
@@ -623,7 +627,9 @@ class Dialect {
|
|
|
623
627
|
_func) {
|
|
624
628
|
return 'sqlAggDistinct called but not implemented';
|
|
625
629
|
}
|
|
626
|
-
|
|
630
|
+
// onBaseTable: the source is a table the engine can sample in place; a
|
|
631
|
+
// dialect whose sampling reads the table itself ignores a sample elsewhere
|
|
632
|
+
sqlSampleTable(tableSQL, sample, _onBaseTable = true) {
|
|
627
633
|
if (sample !== undefined) {
|
|
628
634
|
throw new Error(`Sampling is not supported on dialect ${this.name}.`);
|
|
629
635
|
}
|
|
@@ -694,6 +700,11 @@ class Dialect {
|
|
|
694
700
|
escapeActive = false;
|
|
695
701
|
escapeClause = true;
|
|
696
702
|
}
|
|
703
|
+
else if (this.likeEscape && this.likeExtraWildcards.includes(c)) {
|
|
704
|
+
escaped += '^' + c;
|
|
705
|
+
escapeActive = false;
|
|
706
|
+
escapeClause = true;
|
|
707
|
+
}
|
|
697
708
|
else {
|
|
698
709
|
if (escapeActive) {
|
|
699
710
|
if (this.likeEscape) {
|
|
@@ -720,10 +731,30 @@ class Dialect {
|
|
|
720
731
|
*/
|
|
721
732
|
sqlBoolean(bv) {
|
|
722
733
|
if (this.booleanType === 'none') {
|
|
723
|
-
return bv ? '(1=1)' : '(1
|
|
734
|
+
return bv ? '(1=1)' : '(1=0)';
|
|
724
735
|
}
|
|
725
736
|
return bv ? 'true' : 'false';
|
|
726
737
|
}
|
|
738
|
+
/**
|
|
739
|
+
* Malloy's `not`: of NULL, true. A dialect with booleanType 'none' has no
|
|
740
|
+
* boolean value for COALESCE to return, so the test is written as a
|
|
741
|
+
* condition.
|
|
742
|
+
*/
|
|
743
|
+
sqlNot(condition) {
|
|
744
|
+
if (this.booleanType === 'none') {
|
|
745
|
+
return `(CASE WHEN ${condition} THEN 1 ELSE 0 END = 0)`;
|
|
746
|
+
}
|
|
747
|
+
return `COALESCE(NOT ${condition},TRUE)`;
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* A condition Malloy defines as true when SQL says NULL: `!=` and `!~`
|
|
751
|
+
*/
|
|
752
|
+
sqlNullIsTrue(condition) {
|
|
753
|
+
if (this.booleanType === 'none') {
|
|
754
|
+
return `(CASE WHEN NOT (${condition}) THEN 1 ELSE 0 END = 0)`;
|
|
755
|
+
}
|
|
756
|
+
return `COALESCE(${condition},true)`;
|
|
757
|
+
}
|
|
727
758
|
/**
|
|
728
759
|
* What a boolean value looks like in a query result
|
|
729
760
|
*/
|
|
@@ -14,6 +14,7 @@ const standardsql_1 = require("./standardsql");
|
|
|
14
14
|
const trino_1 = require("./trino");
|
|
15
15
|
const mysql_1 = require("./mysql");
|
|
16
16
|
const databricks_1 = require("./databricks");
|
|
17
|
+
const sqlserver_1 = require("./sqlserver");
|
|
17
18
|
const dialectMap = new Map();
|
|
18
19
|
function getDialect(name) {
|
|
19
20
|
const d = dialectMap.get(name);
|
|
@@ -36,4 +37,5 @@ registerDialect(new trino_1.TrinoDialect());
|
|
|
36
37
|
registerDialect(new trino_1.PrestoDialect());
|
|
37
38
|
registerDialect(new mysql_1.MySQLDialect());
|
|
38
39
|
registerDialect(new databricks_1.DatabricksDialect());
|
|
40
|
+
registerDialect(new sqlserver_1.SQLServerDialect());
|
|
39
41
|
//# sourceMappingURL=dialect_map.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sampling, AtomicTypeDef, RegexMatchExpr, MeasureTimeExpr, BasicAtomicTypeDef, RecordLiteralNode, OrderBy, TimestampUnit } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
|
-
import type { CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
|
|
3
|
+
import type { CivilTime, CompiledOrderBy, DialectFieldList, FieldReferenceType, IntegerTypeMapping } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
5
5
|
import type { ValidateTablePathResult } from '../table-path';
|
|
6
6
|
export declare class DuckDBDialect extends PostgresBase {
|
|
@@ -62,7 +62,7 @@ export declare class DuckDBDialect extends PostgresBase {
|
|
|
62
62
|
castToString(expression: string): string;
|
|
63
63
|
concat(...values: string[]): string;
|
|
64
64
|
validateTypeName(sqlType: string): boolean;
|
|
65
|
-
sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit,
|
|
65
|
+
sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
|
|
66
66
|
sqlRegexpMatch(df: RegexMatchExpr): string;
|
|
67
67
|
sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
|
|
68
68
|
sqlLiteralRecord(lit: RecordLiteralNode): string;
|
|
@@ -332,7 +332,7 @@ class DuckDBDialect extends pg_impl_1.PostgresBase {
|
|
|
332
332
|
// Brackets: INT[ ]
|
|
333
333
|
return sqlType.match(/^[A-Za-z\s(),[\]0-9]*$/) !== null;
|
|
334
334
|
}
|
|
335
|
-
sqlOffsetTime(expr, op, magnitude, unit,
|
|
335
|
+
sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
|
|
336
336
|
// DuckDB doesn't support INTERVAL '1' WEEK, convert to days
|
|
337
337
|
let offsetUnit = unit;
|
|
338
338
|
let offsetMag = magnitude;
|
package/dist/dialect/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type { DialectFunctionOverloadDef, DefinitionBlueprint, DefinitionBlueprintMap, OverloadedDefinitionBlueprint, } from './functions/util';
|
|
2
2
|
export { arg, anyExprType, makeParam, overload, minScalar, minAggregate, maxScalar, spread, param, variadicParam, literal, sql, } from './functions/util';
|
|
3
|
-
export { Dialect,
|
|
4
|
-
export type { DialectFieldList, CompiledOrderBy, LateralJoinExpression, QueryInfo, FieldReferenceType, } from './dialect';
|
|
3
|
+
export { Dialect, queryZone } from './dialect';
|
|
4
|
+
export type { DialectFieldList, CompiledOrderBy, LateralJoinExpression, QueryInfo, FieldReferenceType, GroupByClauseType, OrderByClauseType, } from './dialect';
|
|
5
5
|
export { StandardSQLDialect } from './standardsql';
|
|
6
6
|
export { PostgresDialect } from './postgres';
|
|
7
7
|
export { DuckDBDialect } from './duckdb';
|
|
@@ -9,6 +9,7 @@ export { SnowflakeDialect } from './snowflake';
|
|
|
9
9
|
export { TrinoDialect } from './trino';
|
|
10
10
|
export { MySQLDialect } from './mysql';
|
|
11
11
|
export { DatabricksDialect } from './databricks';
|
|
12
|
+
export { SQLServerDialect } from './sqlserver';
|
|
12
13
|
export { getDialect, registerDialect } from './dialect_map';
|
|
13
14
|
export { getMalloyStandardFunctions } from './functions';
|
|
14
15
|
export type { MalloyStandardFunctionImplementations } from './functions';
|
package/dist/dialect/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* SPDX-License-Identifier: MIT
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.
|
|
7
|
+
exports.validateDottedTablePath = exports.decodeDottedTablePath = exports.TinyParser = exports.getMalloyStandardFunctions = exports.registerDialect = exports.getDialect = exports.SQLServerDialect = exports.DatabricksDialect = exports.MySQLDialect = exports.TrinoDialect = exports.SnowflakeDialect = exports.DuckDBDialect = exports.PostgresDialect = exports.StandardSQLDialect = exports.queryZone = exports.Dialect = exports.sql = exports.literal = exports.variadicParam = exports.param = exports.spread = exports.maxScalar = exports.minAggregate = exports.minScalar = exports.overload = exports.makeParam = exports.anyExprType = exports.arg = void 0;
|
|
8
8
|
var util_1 = require("./functions/util");
|
|
9
9
|
Object.defineProperty(exports, "arg", { enumerable: true, get: function () { return util_1.arg; } });
|
|
10
10
|
Object.defineProperty(exports, "anyExprType", { enumerable: true, get: function () { return util_1.anyExprType; } });
|
|
@@ -20,7 +20,7 @@ Object.defineProperty(exports, "literal", { enumerable: true, get: function () {
|
|
|
20
20
|
Object.defineProperty(exports, "sql", { enumerable: true, get: function () { return util_1.sql; } });
|
|
21
21
|
var dialect_1 = require("./dialect");
|
|
22
22
|
Object.defineProperty(exports, "Dialect", { enumerable: true, get: function () { return dialect_1.Dialect; } });
|
|
23
|
-
Object.defineProperty(exports, "
|
|
23
|
+
Object.defineProperty(exports, "queryZone", { enumerable: true, get: function () { return dialect_1.queryZone; } });
|
|
24
24
|
var standardsql_1 = require("./standardsql");
|
|
25
25
|
Object.defineProperty(exports, "StandardSQLDialect", { enumerable: true, get: function () { return standardsql_1.StandardSQLDialect; } });
|
|
26
26
|
var postgres_1 = require("./postgres");
|
|
@@ -35,6 +35,8 @@ var mysql_1 = require("./mysql");
|
|
|
35
35
|
Object.defineProperty(exports, "MySQLDialect", { enumerable: true, get: function () { return mysql_1.MySQLDialect; } });
|
|
36
36
|
var databricks_1 = require("./databricks");
|
|
37
37
|
Object.defineProperty(exports, "DatabricksDialect", { enumerable: true, get: function () { return databricks_1.DatabricksDialect; } });
|
|
38
|
+
var sqlserver_1 = require("./sqlserver");
|
|
39
|
+
Object.defineProperty(exports, "SQLServerDialect", { enumerable: true, get: function () { return sqlserver_1.SQLServerDialect; } });
|
|
38
40
|
var dialect_map_1 = require("./dialect_map");
|
|
39
41
|
Object.defineProperty(exports, "getDialect", { enumerable: true, get: function () { return dialect_map_1.getDialect; } });
|
|
40
42
|
Object.defineProperty(exports, "registerDialect", { enumerable: true, get: function () { return dialect_map_1.registerDialect; } });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Sampling, MeasureTimeExpr, RegexMatchExpr,
|
|
2
|
-
import type { BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, OrderByClauseType, QueryInfo } from '../dialect';
|
|
1
|
+
import type { Sampling, MeasureTimeExpr, RegexMatchExpr, TypecastExpr, BasicAtomicTypeDef, AtomicTypeDef, ArrayLiteralNode, RecordLiteralNode, ATimestampTypeDef, TimestampUnit, ExtractUnit } from '../../model/malloy_types';
|
|
2
|
+
import type { CivilTime, BooleanTypeSupport, CompiledOrderBy, DialectFieldList, FieldReferenceType, OrderByClauseType, QueryInfo } from '../dialect';
|
|
3
3
|
import { Dialect } from '../dialect';
|
|
4
4
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
5
5
|
export declare class MySQLDialect extends Dialect {
|
|
@@ -60,18 +60,17 @@ export declare class MySQLDialect extends Dialect {
|
|
|
60
60
|
sqlSelectAliasAsStruct(_alias: string, _fieldList: DialectFieldList): string;
|
|
61
61
|
sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
|
|
62
62
|
sqlNowExpr(): string;
|
|
63
|
-
sqlConvertToCivilTime(expr: string, timezone: string, _typeDef:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
sqlCast(qi: QueryInfo, cast: TypecastExpr): string;
|
|
63
|
+
sqlConvertToCivilTime(expr: string, timezone: string, _typeDef: ATimestampTypeDef): string;
|
|
64
|
+
sqlConvertFromCivilTime(expr: string, timezone: string, _destTypeDef: ATimestampTypeDef): string;
|
|
65
|
+
sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
|
|
66
|
+
sqlOffsetTime(expr: string, op: '+' | '-', magnitude: string, unit: TimestampUnit, _civilType: CivilTime): string;
|
|
67
|
+
sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
|
|
68
|
+
sqlCivilToDate(expr: string): string;
|
|
69
|
+
sqlDateToCivil(expr: string): string;
|
|
70
|
+
sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
|
|
72
71
|
sqlRegexpMatch(df: RegexMatchExpr): string;
|
|
73
72
|
sqlDateLiteral(_qi: QueryInfo, literal: string): string;
|
|
74
|
-
|
|
73
|
+
sqlCivilLiteral(literal: string): string;
|
|
75
74
|
sqlTimestamptzLiteral(_qi: QueryInfo, _literal: string, _timezone: string): string;
|
|
76
75
|
sqlMeasureTimeExpr(df: MeasureTimeExpr): string;
|
|
77
76
|
sqlAggDistinct(_key: string, _values: string[], _func: (valNames: string[]) => string): string;
|
|
@@ -351,17 +351,18 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
351
351
|
sqlNowExpr() {
|
|
352
352
|
return 'LOCALTIMESTAMP';
|
|
353
353
|
}
|
|
354
|
+
// Civil time is a DATETIME, which has no zone
|
|
354
355
|
sqlConvertToCivilTime(expr, timezone, _typeDef) {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
typeDef: { type: 'timestamp' },
|
|
359
|
-
};
|
|
356
|
+
return timezone === 'UTC'
|
|
357
|
+
? expr
|
|
358
|
+
: `CONVERT_TZ(${expr}, 'UTC', ${this.sqlTimezoneLiteral(timezone)})`;
|
|
360
359
|
}
|
|
361
360
|
sqlConvertFromCivilTime(expr, timezone, _destTypeDef) {
|
|
362
|
-
return
|
|
361
|
+
return timezone === 'UTC'
|
|
362
|
+
? expr
|
|
363
|
+
: `CONVERT_TZ(${expr}, ${this.sqlTimezoneLiteral(timezone)}, 'UTC')`;
|
|
363
364
|
}
|
|
364
|
-
sqlTruncate(expr, unit,
|
|
365
|
+
sqlTruncate(expr, unit, _civilType) {
|
|
365
366
|
// For week truncation, adjust to Sunday first
|
|
366
367
|
// DAYOFWEEK returns 1=Sunday, 2=Monday, etc., so subtract (DAYOFWEEK-1) days
|
|
367
368
|
const adjustedExpr = unit === 'week'
|
|
@@ -392,7 +393,7 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
392
393
|
}
|
|
393
394
|
return `TIMESTAMP(DATE_FORMAT(${adjustedExpr}, ${format}))`;
|
|
394
395
|
}
|
|
395
|
-
sqlOffsetTime(expr, op, magnitude, unit,
|
|
396
|
+
sqlOffsetTime(expr, op, magnitude, unit, _civilType) {
|
|
396
397
|
// Convert quarter/week to supported units
|
|
397
398
|
let offsetUnit = unit;
|
|
398
399
|
let offsetMag = magnitude;
|
|
@@ -407,27 +408,18 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
407
408
|
const interval = `INTERVAL ${offsetMag} ${offsetUnit}`;
|
|
408
409
|
return `(${expr} ${op} ${interval})`;
|
|
409
410
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
}
|
|
419
|
-
return `${msUnits}(${extractFrom})`;
|
|
411
|
+
sqlCivilExtract(expr, units, _civilType) {
|
|
412
|
+
return `${msExtractionMap[units] || units}(${expr})`;
|
|
413
|
+
}
|
|
414
|
+
sqlCivilToDate(expr) {
|
|
415
|
+
return `CAST(${expr} AS DATE)`;
|
|
416
|
+
}
|
|
417
|
+
sqlDateToCivil(expr) {
|
|
418
|
+
return `CAST(${expr} AS DATETIME)`;
|
|
420
419
|
}
|
|
421
|
-
sqlCast(
|
|
420
|
+
sqlCast(_qi, cast) {
|
|
422
421
|
const srcSQL = cast.e.sql || 'internal-error-in-sql-generation';
|
|
423
|
-
const {
|
|
424
|
-
const tz = (0, dialect_1.qtz)(qi);
|
|
425
|
-
if (op === 'timestamp::date' && tz) {
|
|
426
|
-
return `CAST(CONVERT_TZ(${srcSQL}, 'UTC', ${this.sqlTimezoneLiteral(tz)}) AS DATE) `;
|
|
427
|
-
}
|
|
428
|
-
else if (op === 'date::timestamp' && tz) {
|
|
429
|
-
return ` CONVERT_TZ(${srcSQL}, ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
|
|
430
|
-
}
|
|
422
|
+
const { srcTypeDef, dstTypeDef, dstSQLType } = this.sqlCastPrep(cast);
|
|
431
423
|
if (!malloy_types_1.TD.eq(srcTypeDef, dstTypeDef)) {
|
|
432
424
|
if (cast.safe) {
|
|
433
425
|
throw new Error("Mysql dialect doesn't support Safe Cast");
|
|
@@ -445,11 +437,7 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
445
437
|
sqlDateLiteral(_qi, literal) {
|
|
446
438
|
return `DATE '${literal}'`;
|
|
447
439
|
}
|
|
448
|
-
|
|
449
|
-
const tz = timezone || (0, dialect_1.qtz)(qi);
|
|
450
|
-
if (tz) {
|
|
451
|
-
return `CONVERT_TZ('${literal}', ${this.sqlTimezoneLiteral(tz)}, 'UTC')`;
|
|
452
|
-
}
|
|
440
|
+
sqlCivilLiteral(literal) {
|
|
453
441
|
return `TIMESTAMP '${literal}'`;
|
|
454
442
|
}
|
|
455
443
|
sqlTimestamptzLiteral(_qi, _literal, _timezone) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ArrayLiteralNode,
|
|
2
|
-
import type { QueryInfo } from './dialect';
|
|
1
|
+
import type { ArrayLiteralNode, ATimestampTypeDef, RecordLiteralNode, ExtractUnit, RegexMatchExpr, TimestampUnit, TypecastExpr } from '../model/malloy_types';
|
|
2
|
+
import type { CivilTime, QueryInfo } from './dialect';
|
|
3
3
|
import { Dialect } from './dialect';
|
|
4
4
|
export declare const timeExtractMap: Record<string, string>;
|
|
5
5
|
/**
|
|
@@ -14,18 +14,17 @@ export declare abstract class PostgresBase extends Dialect {
|
|
|
14
14
|
identifierQuoteChar: string;
|
|
15
15
|
sqlLiteralString(literal: string): string;
|
|
16
16
|
sqlNowExpr(): string;
|
|
17
|
-
|
|
18
|
-
sqlCast(
|
|
17
|
+
sqlCivilExtract(expr: string, units: ExtractUnit, _civilType: CivilTime): string;
|
|
18
|
+
sqlCast(_qi: QueryInfo, cast: TypecastExpr): string;
|
|
19
19
|
sqlRegexpMatch(df: RegexMatchExpr): string;
|
|
20
20
|
sqlDateLiteral(_qi: QueryInfo, literal: string): string;
|
|
21
|
-
|
|
21
|
+
sqlCivilLiteral(literal: string): string;
|
|
22
22
|
sqlTimestamptzLiteral(_qi: QueryInfo, literal: string, timezone: string): string;
|
|
23
23
|
sqlLiteralRecord(_lit: RecordLiteralNode): string;
|
|
24
24
|
sqlLiteralArray(lit: ArrayLiteralNode): string;
|
|
25
|
-
sqlConvertToCivilTime(expr: string, timezone: string, typeDef:
|
|
26
|
-
sql: string;
|
|
27
|
-
typeDef: AtomicTypeDef;
|
|
28
|
-
};
|
|
25
|
+
sqlConvertToCivilTime(expr: string, timezone: string, typeDef: ATimestampTypeDef): string;
|
|
29
26
|
sqlConvertFromCivilTime(expr: string, timezone: string, destTypeDef: ATimestampTypeDef): string;
|
|
30
|
-
sqlTruncate(expr: string, unit: TimestampUnit,
|
|
27
|
+
sqlTruncate(expr: string, unit: TimestampUnit, _civilType: CivilTime): string;
|
|
28
|
+
sqlCivilToDate(expr: string): string;
|
|
29
|
+
sqlDateToCivil(expr: string): string;
|
|
31
30
|
}
|