@malloydata/malloy 0.0.34-dev230523182411 → 0.0.34
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 +20 -5
- package/dist/dialect/dialect.js +40 -7
- package/dist/dialect/duckdb.d.ts +5 -5
- package/dist/dialect/duckdb.js +54 -120
- package/dist/dialect/postgres.d.ts +5 -5
- package/dist/dialect/postgres.js +50 -38
- package/dist/dialect/standardsql.d.ts +5 -5
- package/dist/dialect/standardsql.js +78 -42
- package/dist/lang/ast/elements/refined-source.js +8 -0
- package/dist/lang/ast/executors/reduce-executor.js +4 -0
- package/dist/lang/ast/expressions/expr-cast.js +1 -1
- package/dist/lang/ast/expressions/expr-granular-time.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-granular-time.js +10 -19
- package/dist/lang/ast/expressions/expr-time-extract.js +31 -4
- package/dist/lang/ast/expressions/time-literal.d.ts +12 -6
- package/dist/lang/ast/expressions/time-literal.js +74 -67
- package/dist/lang/ast/field-space/dynamic-space.d.ts +2 -0
- package/dist/lang/ast/field-space/dynamic-space.js +6 -0
- package/dist/lang/ast/field-space/query-spaces.js +3 -0
- package/dist/lang/ast/index.d.ts +1 -0
- package/dist/lang/ast/index.js +1 -0
- package/dist/lang/ast/source-properties/timezone-statement.d.ts +6 -0
- package/dist/lang/ast/source-properties/timezone-statement.js +35 -0
- package/dist/lang/ast/sources/named-source.js +1 -1
- package/dist/lang/ast/sources/sql-source.js +4 -0
- package/dist/lang/ast/time-utils.d.ts +2 -1
- package/dist/lang/ast/time-utils.js +5 -1
- package/dist/lang/ast/types/expression-def.d.ts +2 -0
- package/dist/lang/ast/types/expression-def.js +48 -18
- package/dist/lang/ast/types/query-property.d.ts +2 -1
- package/dist/lang/ast/types/query-property.js +3 -1
- package/dist/lang/ast/types/source-property.d.ts +2 -1
- package/dist/lang/ast/types/source-property.js +3 -1
- package/dist/lang/lib/Malloy/MalloyLexer.d.ts +3 -3
- package/dist/lang/lib/Malloy/MalloyLexer.js +958 -972
- package/dist/lang/lib/Malloy/MalloyParser.d.ts +52 -33
- package/dist/lang/lib/Malloy/MalloyParser.js +1243 -1128
- package/dist/lang/lib/Malloy/MalloyParserListener.d.ts +24 -0
- package/dist/lang/lib/Malloy/MalloyParserVisitor.d.ts +15 -0
- package/dist/lang/malloy-to-ast.d.ts +2 -0
- package/dist/lang/malloy-to-ast.js +6 -0
- package/dist/lang/test/parse-expects.d.ts +1 -0
- package/dist/lang/test/parse-expects.js +11 -1
- package/dist/lang/test/parse.spec.js +29 -37
- package/dist/malloy.js +31 -23
- package/dist/model/malloy_query.d.ts +2 -0
- package/dist/model/malloy_query.js +42 -41
- package/dist/model/malloy_types.d.ts +3 -2
- package/package.json +1 -1
|
@@ -4,6 +4,20 @@ interface DialectField {
|
|
|
4
4
|
sqlExpression: string;
|
|
5
5
|
sqlOutputName: string;
|
|
6
6
|
}
|
|
7
|
+
/**
|
|
8
|
+
* Data which dialect methods need in order to correctly generate SQL.
|
|
9
|
+
* Initially this is just timezone related, but I made this an interface
|
|
10
|
+
* so it would be extensible with other useful information which might not be
|
|
11
|
+
* available until runtime (e.g. version number of db)
|
|
12
|
+
* mtoy TODO rename this interface to something other than "QueryInfo"
|
|
13
|
+
*/
|
|
14
|
+
export interface QueryInfo {
|
|
15
|
+
queryTimezone?: string;
|
|
16
|
+
systemTimezone?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const dayIndex: number;
|
|
19
|
+
export declare function inDays(units: string): boolean;
|
|
20
|
+
export declare function qtz(qi: QueryInfo): string | undefined;
|
|
7
21
|
/**
|
|
8
22
|
* Someday this might be used to control how a function call in malloy is
|
|
9
23
|
* translated into a function call in SQL. Today this is just so that
|
|
@@ -48,19 +62,20 @@ export declare abstract class Dialect {
|
|
|
48
62
|
sqlDateToString(sqlDateExp: string): string;
|
|
49
63
|
abstract sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
50
64
|
abstract sqlNow(): Expr;
|
|
51
|
-
abstract sqlTrunc(sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
52
|
-
abstract sqlExtract(sqlTime: TimeValue, units: ExtractUnit): Expr;
|
|
65
|
+
abstract sqlTrunc(qi: QueryInfo, sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
66
|
+
abstract sqlExtract(qi: QueryInfo, sqlTime: TimeValue, units: ExtractUnit): Expr;
|
|
53
67
|
abstract sqlMeasureTime(from: TimeValue, to: TimeValue, units: TimestampUnit): Expr;
|
|
54
68
|
abstract sqlAlterTime(op: '+' | '-', expr: TimeValue, n: Expr, timeframe: TimestampUnit): Expr;
|
|
55
69
|
ignoreInProject(_fieldName: string): boolean;
|
|
56
|
-
abstract sqlCast(cast: TypecastFragment): Expr;
|
|
57
|
-
abstract sqlLiteralTime(timeString: string, type: TimeFieldType, timezone
|
|
70
|
+
abstract sqlCast(qi: QueryInfo, cast: TypecastFragment): Expr;
|
|
71
|
+
abstract sqlLiteralTime(qi: QueryInfo, timeString: string, type: TimeFieldType, timezone?: string): string;
|
|
58
72
|
abstract sqlLiteralString(literak: string): string;
|
|
59
73
|
abstract sqlRegexpMatch(expr: Expr, regex: string): Expr;
|
|
60
74
|
getFunctionInfo(functionName: string): FunctionInfo | undefined;
|
|
61
|
-
dialectExpr(df: DialectFragment): Expr;
|
|
75
|
+
dialectExpr(qi: QueryInfo, df: DialectFragment): Expr;
|
|
62
76
|
sqlSumDistinct(_key: string, _value: string, _funcName: string): string;
|
|
63
77
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
64
78
|
sqlOrderBy(orderTerms: string[]): string;
|
|
79
|
+
sqlTzStr(qi: QueryInfo): string;
|
|
65
80
|
}
|
|
66
81
|
export {};
|
package/dist/dialect/dialect.js
CHANGED
|
@@ -22,8 +22,35 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.Dialect = void 0;
|
|
25
|
+
exports.Dialect = exports.qtz = exports.inDays = exports.dayIndex = void 0;
|
|
26
26
|
const malloy_types_1 = require("../model/malloy_types");
|
|
27
|
+
const allUnits = [
|
|
28
|
+
'microsecond',
|
|
29
|
+
'millisecond',
|
|
30
|
+
'second',
|
|
31
|
+
'minute',
|
|
32
|
+
'hour',
|
|
33
|
+
'day',
|
|
34
|
+
'week',
|
|
35
|
+
'month',
|
|
36
|
+
'quarter',
|
|
37
|
+
'year',
|
|
38
|
+
];
|
|
39
|
+
exports.dayIndex = allUnits.indexOf('day');
|
|
40
|
+
function inDays(units) {
|
|
41
|
+
return allUnits.indexOf(units) >= exports.dayIndex;
|
|
42
|
+
}
|
|
43
|
+
exports.inDays = inDays;
|
|
44
|
+
// Return the active query timezone, if it different than the
|
|
45
|
+
// "native" timezone for timestamps.
|
|
46
|
+
function qtz(qi) {
|
|
47
|
+
const tz = qi.queryTimezone;
|
|
48
|
+
if (tz === undefined || tz === qi.systemTimezone) {
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
return tz;
|
|
52
|
+
}
|
|
53
|
+
exports.qtz = qtz;
|
|
27
54
|
class Dialect {
|
|
28
55
|
sqlFinalStage(_lastStageName, _fields) {
|
|
29
56
|
throw new Error('Dialect has no final Stage but called Anyway');
|
|
@@ -40,7 +67,7 @@ class Dialect {
|
|
|
40
67
|
getFunctionInfo(functionName) {
|
|
41
68
|
return this.functionInfo[functionName.toLowerCase()];
|
|
42
69
|
}
|
|
43
|
-
dialectExpr(df) {
|
|
70
|
+
dialectExpr(qi, df) {
|
|
44
71
|
switch (df.function) {
|
|
45
72
|
case 'now':
|
|
46
73
|
return this.sqlNow();
|
|
@@ -49,11 +76,11 @@ class Dialect {
|
|
|
49
76
|
case 'delta':
|
|
50
77
|
return this.sqlAlterTime(df.op, df.base, df.delta, df.units);
|
|
51
78
|
case 'trunc':
|
|
52
|
-
return this.sqlTrunc(df.expr, df.units);
|
|
79
|
+
return this.sqlTrunc(qi, df.expr, df.units);
|
|
53
80
|
case 'extract':
|
|
54
|
-
return this.sqlExtract(df.expr, df.units);
|
|
81
|
+
return this.sqlExtract(qi, df.expr, df.units);
|
|
55
82
|
case 'cast':
|
|
56
|
-
return this.sqlCast(df);
|
|
83
|
+
return this.sqlCast(qi, df);
|
|
57
84
|
case 'regexpMatch':
|
|
58
85
|
return this.sqlRegexpMatch(df.expr, df.regexp);
|
|
59
86
|
case 'div': {
|
|
@@ -62,8 +89,11 @@ class Dialect {
|
|
|
62
89
|
}
|
|
63
90
|
return (0, malloy_types_1.mkExpr) `${df.numerator}/${df.denominator}`;
|
|
64
91
|
}
|
|
65
|
-
case 'timeLiteral':
|
|
66
|
-
return [
|
|
92
|
+
case 'timeLiteral': {
|
|
93
|
+
return [
|
|
94
|
+
this.sqlLiteralTime(qi, df.literal, df.literalType, df.timezone),
|
|
95
|
+
];
|
|
96
|
+
}
|
|
67
97
|
case 'stringLiteral':
|
|
68
98
|
return [this.sqlLiteralString(df.literal)];
|
|
69
99
|
}
|
|
@@ -80,6 +110,9 @@ class Dialect {
|
|
|
80
110
|
sqlOrderBy(orderTerms) {
|
|
81
111
|
return `ORDER BY ${orderTerms.join(',')}`;
|
|
82
112
|
}
|
|
113
|
+
sqlTzStr(qi) {
|
|
114
|
+
return `"${qi.queryTimezone}"`;
|
|
115
|
+
}
|
|
83
116
|
}
|
|
84
117
|
exports.Dialect = Dialect;
|
|
85
118
|
//# sourceMappingURL=dialect.js.map
|
package/dist/dialect/duckdb.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DateUnit, Expr, ExtractUnit, Sampling, StructDef, TimeFieldType, TimeValue, TimestampUnit, TypecastFragment } from '../model/malloy_types';
|
|
2
|
-
import { Dialect, DialectFieldList, FunctionInfo } from './dialect';
|
|
2
|
+
import { Dialect, DialectFieldList, FunctionInfo, QueryInfo } from './dialect';
|
|
3
3
|
export declare class DuckDBDialect extends Dialect {
|
|
4
4
|
name: string;
|
|
5
5
|
defaultNumberType: string;
|
|
@@ -39,12 +39,12 @@ export declare class DuckDBDialect extends Dialect {
|
|
|
39
39
|
getFunctionInfo(functionName: string): FunctionInfo | undefined;
|
|
40
40
|
sqlMeasureTime(from: TimeValue, to: TimeValue, units: string): Expr;
|
|
41
41
|
sqlNow(): Expr;
|
|
42
|
-
sqlTrunc(sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
43
|
-
sqlExtract(from: TimeValue, units: ExtractUnit): Expr;
|
|
42
|
+
sqlTrunc(qi: QueryInfo, sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
43
|
+
sqlExtract(qi: QueryInfo, from: TimeValue, units: ExtractUnit): Expr;
|
|
44
44
|
sqlAlterTime(op: '+' | '-', expr: TimeValue, n: Expr, timeframe: DateUnit): Expr;
|
|
45
|
-
sqlCast(cast: TypecastFragment): Expr;
|
|
45
|
+
sqlCast(qi: QueryInfo, cast: TypecastFragment): Expr;
|
|
46
46
|
sqlRegexpMatch(expr: Expr, regexp: string): Expr;
|
|
47
|
-
sqlLiteralTime(timeString: string, type: TimeFieldType,
|
|
47
|
+
sqlLiteralTime(qi: QueryInfo, timeString: string, type: TimeFieldType, timezone: string | undefined): string;
|
|
48
48
|
sqlSumDistinct(key: string, value: string, funcName: string): string;
|
|
49
49
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
50
50
|
sqlOrderBy(orderTerms: string[]): string;
|
package/dist/dialect/duckdb.js
CHANGED
|
@@ -28,85 +28,6 @@ const utils_1 = require("../model/utils");
|
|
|
28
28
|
const dialect_1 = require("./dialect");
|
|
29
29
|
// need to refactor runSQL to take a SQLBlock instead of just a sql string.
|
|
30
30
|
const hackSplitComment = '-- hack: split on this';
|
|
31
|
-
const _keywords = `
|
|
32
|
-
ALL
|
|
33
|
-
ANALYSE
|
|
34
|
-
ANALYZE
|
|
35
|
-
AND
|
|
36
|
-
ANY
|
|
37
|
-
ARRAY
|
|
38
|
-
AS
|
|
39
|
-
ASC_P
|
|
40
|
-
ASYMMETRIC
|
|
41
|
-
BOTH
|
|
42
|
-
CASE
|
|
43
|
-
CAST
|
|
44
|
-
CHECK_P
|
|
45
|
-
COLLATE
|
|
46
|
-
COLUMN
|
|
47
|
-
CONSTRAINT
|
|
48
|
-
CREATE_P
|
|
49
|
-
CURRENT_CATALOG
|
|
50
|
-
CURRENT_DATE
|
|
51
|
-
CURRENT_ROLE
|
|
52
|
-
CURRENT_TIME
|
|
53
|
-
CURRENT_TIMESTAMP
|
|
54
|
-
CURRENT_USER
|
|
55
|
-
DEFAULT
|
|
56
|
-
DEFERRABLE
|
|
57
|
-
DESC_P
|
|
58
|
-
DISTINCT
|
|
59
|
-
DO
|
|
60
|
-
ELSE
|
|
61
|
-
END_P
|
|
62
|
-
EXCEPT
|
|
63
|
-
FALSE_P
|
|
64
|
-
FETCH
|
|
65
|
-
FOR
|
|
66
|
-
FOREIGN
|
|
67
|
-
FROM
|
|
68
|
-
GRANT
|
|
69
|
-
GROUP_P
|
|
70
|
-
HAVING
|
|
71
|
-
IN_P
|
|
72
|
-
INITIALLY
|
|
73
|
-
INTERSECT
|
|
74
|
-
INTO
|
|
75
|
-
LATERAL_P
|
|
76
|
-
LEADING
|
|
77
|
-
LIMIT
|
|
78
|
-
LOCALTIME
|
|
79
|
-
LOCALTIMESTAMP
|
|
80
|
-
NOT
|
|
81
|
-
NULL_P
|
|
82
|
-
OFFSET
|
|
83
|
-
ON
|
|
84
|
-
ONLY
|
|
85
|
-
OR
|
|
86
|
-
ORDER
|
|
87
|
-
PLACING
|
|
88
|
-
PRIMARY
|
|
89
|
-
REFERENCES
|
|
90
|
-
RETURNING
|
|
91
|
-
SELECT
|
|
92
|
-
SESSION_USER
|
|
93
|
-
SOME
|
|
94
|
-
SYMMETRIC
|
|
95
|
-
TABLE
|
|
96
|
-
THEN
|
|
97
|
-
TO
|
|
98
|
-
TRAILING
|
|
99
|
-
TRUE_P
|
|
100
|
-
UNION
|
|
101
|
-
UNIQUE
|
|
102
|
-
USER
|
|
103
|
-
USING
|
|
104
|
-
VARIADIC
|
|
105
|
-
WHEN
|
|
106
|
-
WHERE
|
|
107
|
-
WINDOW
|
|
108
|
-
WITH
|
|
109
|
-
`.split(/\s/);
|
|
110
31
|
const castMap = {
|
|
111
32
|
number: 'double precision',
|
|
112
33
|
string: 'varchar',
|
|
@@ -115,11 +36,6 @@ const pgExtractionMap = {
|
|
|
115
36
|
day_of_week: 'dow',
|
|
116
37
|
day_of_year: 'doy',
|
|
117
38
|
};
|
|
118
|
-
const inSeconds = {
|
|
119
|
-
second: 1,
|
|
120
|
-
minute: 60,
|
|
121
|
-
hour: 3600,
|
|
122
|
-
};
|
|
123
39
|
class DuckDBDialect extends dialect_1.Dialect {
|
|
124
40
|
constructor() {
|
|
125
41
|
super(...arguments);
|
|
@@ -268,42 +184,52 @@ class DuckDBDialect extends dialect_1.Dialect {
|
|
|
268
184
|
sqlMeasureTime(from, to, units) {
|
|
269
185
|
let lVal = from.value;
|
|
270
186
|
let rVal = to.value;
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
if (from.valueType !== 'date') {
|
|
280
|
-
lVal = (0, malloy_types_1.mkExpr) `CAST((${lVal}) AS DATE)`;
|
|
281
|
-
}
|
|
282
|
-
if (to.valueType !== 'date') {
|
|
283
|
-
rVal = (0, malloy_types_1.mkExpr) `CAST((${rVal}) AS DATE)`;
|
|
284
|
-
}
|
|
285
|
-
if (units === 'week') {
|
|
286
|
-
// DuckDB's weeks start on Monday, but Malloy's weeks start on Sunday
|
|
287
|
-
lVal = (0, malloy_types_1.mkExpr) `(${lVal} + INTERVAL 1 DAY)`;
|
|
288
|
-
rVal = (0, malloy_types_1.mkExpr) `(${rVal} + INTERVAL 1 DAY)`;
|
|
187
|
+
if (!(0, dialect_1.inDays)(units)) {
|
|
188
|
+
if (from.valueType === 'date') {
|
|
189
|
+
lVal = (0, malloy_types_1.mkExpr) `(${lVal})::TIMESTAMP`;
|
|
190
|
+
}
|
|
191
|
+
if (to.valueType === 'date') {
|
|
192
|
+
rVal = (0, malloy_types_1.mkExpr) `(${rVal})::TIMESTAMP`;
|
|
193
|
+
}
|
|
289
194
|
}
|
|
290
|
-
return (0, malloy_types_1.mkExpr) `
|
|
195
|
+
return (0, malloy_types_1.mkExpr) `DATE_SUB('${units}',${lVal},${rVal})`;
|
|
291
196
|
}
|
|
292
197
|
sqlNow() {
|
|
293
|
-
return (0, malloy_types_1.mkExpr) `
|
|
198
|
+
return (0, malloy_types_1.mkExpr) `LOCALTIMESTAMP`;
|
|
294
199
|
}
|
|
295
|
-
sqlTrunc(sqlTime, units) {
|
|
200
|
+
sqlTrunc(qi, sqlTime, units) {
|
|
296
201
|
// adjusting for monday/sunday weeks
|
|
297
202
|
const week = units === 'week';
|
|
298
203
|
const truncThis = week
|
|
299
204
|
? (0, malloy_types_1.mkExpr) `${sqlTime.value} + INTERVAL 1 DAY`
|
|
300
205
|
: sqlTime.value;
|
|
301
|
-
|
|
302
|
-
|
|
206
|
+
if (sqlTime.valueType === 'timestamp') {
|
|
207
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
208
|
+
if (tz) {
|
|
209
|
+
const civilSource = (0, malloy_types_1.mkExpr) `(${truncThis}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
210
|
+
let civilTrunc = (0, malloy_types_1.mkExpr) `DATE_TRUNC('${units}', ${civilSource})`;
|
|
211
|
+
// MTOY todo ... only need to do this if this is a date ...
|
|
212
|
+
civilTrunc = (0, malloy_types_1.mkExpr) `${civilTrunc}::TIMESTAMP`;
|
|
213
|
+
const truncTsTz = (0, malloy_types_1.mkExpr) `${civilTrunc} AT TIME ZONE '${tz}'`;
|
|
214
|
+
return (0, malloy_types_1.mkExpr) `(${truncTsTz})::TIMESTAMP`;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
let result = (0, malloy_types_1.mkExpr) `DATE_TRUNC('${units}', ${truncThis})`;
|
|
218
|
+
if (week) {
|
|
219
|
+
result = (0, malloy_types_1.mkExpr) `(${result} - INTERVAL 1 DAY)`;
|
|
220
|
+
}
|
|
221
|
+
return result;
|
|
303
222
|
}
|
|
304
|
-
sqlExtract(from, units) {
|
|
223
|
+
sqlExtract(qi, from, units) {
|
|
305
224
|
const pgUnits = pgExtractionMap[units] || units;
|
|
306
|
-
|
|
225
|
+
let extractFrom = from.value;
|
|
226
|
+
if (from.valueType === 'timestamp') {
|
|
227
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
228
|
+
if (tz) {
|
|
229
|
+
extractFrom = (0, malloy_types_1.mkExpr) `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const extracted = (0, malloy_types_1.mkExpr) `EXTRACT(${pgUnits} FROM ${extractFrom})`;
|
|
307
233
|
return units === 'day_of_week' ? (0, malloy_types_1.mkExpr) `(${extracted}+1)` : extracted;
|
|
308
234
|
}
|
|
309
235
|
sqlAlterTime(op, expr, n, timeframe) {
|
|
@@ -316,28 +242,36 @@ class DuckDBDialect extends dialect_1.Dialect {
|
|
|
316
242
|
n = (0, malloy_types_1.mkExpr) `${n}*7`;
|
|
317
243
|
}
|
|
318
244
|
const interval = (0, malloy_types_1.mkExpr) `INTERVAL (${n}) ${timeframe}`;
|
|
319
|
-
return (0, malloy_types_1.mkExpr)
|
|
320
|
-
}
|
|
321
|
-
sqlCast(cast) {
|
|
245
|
+
return (0, malloy_types_1.mkExpr) `${expr.value} ${op} ${interval}`;
|
|
246
|
+
}
|
|
247
|
+
sqlCast(qi, cast) {
|
|
248
|
+
const op = `${cast.srcType}::${cast.dstType}`;
|
|
249
|
+
const castTo = castMap[cast.dstType] || cast.dstType;
|
|
250
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
251
|
+
if (op === 'timestamp::date' && tz) {
|
|
252
|
+
const tstz = (0, malloy_types_1.mkExpr) `${cast.expr}::TIMESTAMPTZ`;
|
|
253
|
+
return (0, malloy_types_1.mkExpr) `CAST((${tstz}) AT TIME ZONE '${tz}' AS DATE)`;
|
|
254
|
+
}
|
|
255
|
+
else if (op === 'date::timestamp' && tz) {
|
|
256
|
+
return (0, malloy_types_1.mkExpr) `CAST((${cast.expr})::TIMESTAMP AT TIME ZONE '${tz}' AS TIMESTAMP)`;
|
|
257
|
+
}
|
|
322
258
|
if (cast.dstType !== cast.srcType) {
|
|
323
|
-
|
|
324
|
-
return (0, malloy_types_1.mkExpr) `cast(${cast.expr} as ${castTo})`;
|
|
259
|
+
return (0, malloy_types_1.mkExpr) `CAST(${cast.expr} AS ${castTo})`;
|
|
325
260
|
}
|
|
326
261
|
return cast.expr;
|
|
327
262
|
}
|
|
328
263
|
sqlRegexpMatch(expr, regexp) {
|
|
329
264
|
return (0, malloy_types_1.mkExpr) `REGEXP_MATCHES(${expr}, ${regexp})`;
|
|
330
265
|
}
|
|
331
|
-
sqlLiteralTime(timeString, type,
|
|
266
|
+
sqlLiteralTime(qi, timeString, type, timezone) {
|
|
332
267
|
if (type === 'date') {
|
|
333
268
|
return `DATE '${timeString}'`;
|
|
334
269
|
}
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
else {
|
|
339
|
-
throw new Error(`Unknown Literal time format ${type}`);
|
|
270
|
+
const tz = timezone || (0, dialect_1.qtz)(qi);
|
|
271
|
+
if (tz) {
|
|
272
|
+
return `TIMESTAMPTZ '${timeString} ${tz}'::TIMESTAMP`;
|
|
340
273
|
}
|
|
274
|
+
return `TIMESTAMP '${timeString}'`;
|
|
341
275
|
}
|
|
342
276
|
sqlSumDistinct(key, value, funcName) {
|
|
343
277
|
// return `sum_distinct(list({key:${key}, val: ${value}}))`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DateUnit, Expr, ExtractUnit, Sampling, TimeFieldType, TimeValue, TimestampUnit, TypecastFragment } from '../model/malloy_types';
|
|
2
|
-
import { Dialect, DialectFieldList, FunctionInfo } from './dialect';
|
|
2
|
+
import { Dialect, DialectFieldList, FunctionInfo, QueryInfo } from './dialect';
|
|
3
3
|
export declare class PostgresDialect extends Dialect {
|
|
4
4
|
name: string;
|
|
5
5
|
defaultNumberType: string;
|
|
@@ -37,12 +37,12 @@ export declare class PostgresDialect extends Dialect {
|
|
|
37
37
|
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
38
38
|
sqlCreateTableAsSelect(_tableName: string, _sql: string): string;
|
|
39
39
|
sqlNow(): Expr;
|
|
40
|
-
sqlTrunc(sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
41
|
-
sqlExtract(from: TimeValue, units: ExtractUnit): Expr;
|
|
40
|
+
sqlTrunc(qi: QueryInfo, sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
41
|
+
sqlExtract(qi: QueryInfo, from: TimeValue, units: ExtractUnit): Expr;
|
|
42
42
|
sqlAlterTime(op: '+' | '-', expr: TimeValue, n: Expr, timeframe: DateUnit): Expr;
|
|
43
|
-
sqlCast(cast: TypecastFragment): Expr;
|
|
43
|
+
sqlCast(qi: QueryInfo, cast: TypecastFragment): Expr;
|
|
44
44
|
sqlRegexpMatch(expr: Expr, regexp: string): Expr;
|
|
45
|
-
sqlLiteralTime(timeString: string, type: TimeFieldType,
|
|
45
|
+
sqlLiteralTime(qi: QueryInfo, timeString: string, type: TimeFieldType, timezone: string | undefined): string;
|
|
46
46
|
getFunctionInfo(functionName: string): FunctionInfo | undefined;
|
|
47
47
|
sqlMeasureTime(from: TimeValue, to: TimeValue, units: string): Expr;
|
|
48
48
|
sqlSumDistinct(key: string, value: string, funcName: string): string;
|
package/dist/dialect/postgres.js
CHANGED
|
@@ -47,6 +47,8 @@ const inSeconds = {
|
|
|
47
47
|
second: 1,
|
|
48
48
|
minute: 60,
|
|
49
49
|
hour: 3600,
|
|
50
|
+
day: 24 * 3600,
|
|
51
|
+
week: 7 * 24 * 3600,
|
|
50
52
|
};
|
|
51
53
|
class PostgresDialect extends dialect_1.Dialect {
|
|
52
54
|
constructor() {
|
|
@@ -218,18 +220,39 @@ class PostgresDialect extends dialect_1.Dialect {
|
|
|
218
220
|
sqlNow() {
|
|
219
221
|
return (0, malloy_types_1.mkExpr) `CURRENT_TIMESTAMP`;
|
|
220
222
|
}
|
|
221
|
-
sqlTrunc(sqlTime, units) {
|
|
223
|
+
sqlTrunc(qi, sqlTime, units) {
|
|
222
224
|
// adjusting for monday/sunday weeks
|
|
223
225
|
const week = units === 'week';
|
|
224
226
|
const truncThis = week
|
|
225
|
-
? (0, malloy_types_1.mkExpr) `${sqlTime.value}+
|
|
227
|
+
? (0, malloy_types_1.mkExpr) `${sqlTime.value} + INTERVAL '1' DAY`
|
|
226
228
|
: sqlTime.value;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
if (sqlTime.valueType === 'timestamp') {
|
|
230
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
231
|
+
if (tz) {
|
|
232
|
+
const civilSource = (0, malloy_types_1.mkExpr) `(${truncThis}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
233
|
+
let civilTrunc = (0, malloy_types_1.mkExpr) `DATE_TRUNC('${units}', ${civilSource})`;
|
|
234
|
+
// MTOY todo ... only need to do this if this is a date ...
|
|
235
|
+
civilTrunc = (0, malloy_types_1.mkExpr) `${civilTrunc}::TIMESTAMP`;
|
|
236
|
+
const truncTsTz = (0, malloy_types_1.mkExpr) `${civilTrunc} AT TIME ZONE '${tz}'`;
|
|
237
|
+
return (0, malloy_types_1.mkExpr) `(${truncTsTz})::TIMESTAMP`;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
let result = (0, malloy_types_1.mkExpr) `DATE_TRUNC('${units}', ${truncThis})`;
|
|
241
|
+
if (week) {
|
|
242
|
+
result = (0, malloy_types_1.mkExpr) `(${result} - INTERVAL '1' DAY)`;
|
|
243
|
+
}
|
|
244
|
+
return result;
|
|
229
245
|
}
|
|
230
|
-
sqlExtract(from, units) {
|
|
246
|
+
sqlExtract(qi, from, units) {
|
|
231
247
|
const pgUnits = pgExtractionMap[units] || units;
|
|
232
|
-
|
|
248
|
+
let extractFrom = from.value;
|
|
249
|
+
if (from.valueType === 'timestamp') {
|
|
250
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
251
|
+
if (tz) {
|
|
252
|
+
extractFrom = (0, malloy_types_1.mkExpr) `(${extractFrom}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const extracted = (0, malloy_types_1.mkExpr) `EXTRACT(${pgUnits} FROM ${extractFrom})`;
|
|
233
256
|
return units === 'day_of_week' ? (0, malloy_types_1.mkExpr) `(${extracted}+1)` : extracted;
|
|
234
257
|
}
|
|
235
258
|
sqlAlterTime(op, expr, n, timeframe) {
|
|
@@ -240,26 +263,34 @@ class PostgresDialect extends dialect_1.Dialect {
|
|
|
240
263
|
const interval = (0, malloy_types_1.mkExpr) `make_interval(${pgMakeIntervalMap[timeframe]}=>${n})`;
|
|
241
264
|
return (0, malloy_types_1.mkExpr) `((${expr.value})${op}${interval})`;
|
|
242
265
|
}
|
|
243
|
-
sqlCast(cast) {
|
|
266
|
+
sqlCast(qi, cast) {
|
|
267
|
+
const op = `${cast.srcType}::${cast.dstType}`;
|
|
268
|
+
const castTo = castMap[cast.dstType] || cast.dstType;
|
|
269
|
+
const tz = (0, dialect_1.qtz)(qi);
|
|
270
|
+
if (op === 'timestamp::date' && tz) {
|
|
271
|
+
const tstz = (0, malloy_types_1.mkExpr) `${cast.expr}::TIMESTAMPTZ`;
|
|
272
|
+
return (0, malloy_types_1.mkExpr) `CAST((${tstz}) AT TIME ZONE '${tz}' AS DATE)`;
|
|
273
|
+
}
|
|
274
|
+
else if (op === 'date::timestamp' && tz) {
|
|
275
|
+
return (0, malloy_types_1.mkExpr) `CAST((${cast.expr})::TIMESTAMP AT TIME ZONE '${tz}' AS TIMESTAMP)`;
|
|
276
|
+
}
|
|
244
277
|
if (cast.dstType !== cast.srcType) {
|
|
245
|
-
|
|
246
|
-
return (0, malloy_types_1.mkExpr) `cast(${cast.expr} as ${castTo})`;
|
|
278
|
+
return (0, malloy_types_1.mkExpr) `CAST(${cast.expr} AS ${castTo})`;
|
|
247
279
|
}
|
|
248
280
|
return cast.expr;
|
|
249
281
|
}
|
|
250
282
|
sqlRegexpMatch(expr, regexp) {
|
|
251
283
|
return (0, malloy_types_1.mkExpr) `(${expr} ~ ${regexp})`;
|
|
252
284
|
}
|
|
253
|
-
sqlLiteralTime(timeString, type,
|
|
285
|
+
sqlLiteralTime(qi, timeString, type, timezone) {
|
|
254
286
|
if (type === 'date') {
|
|
255
|
-
return `DATE
|
|
287
|
+
return `DATE '${timeString}'`;
|
|
256
288
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
else {
|
|
261
|
-
throw new Error(`Unknown Literal time format ${type}`);
|
|
289
|
+
const tz = timezone || (0, dialect_1.qtz)(qi);
|
|
290
|
+
if (tz) {
|
|
291
|
+
return `TIMESTAMPTZ '${timeString} ${tz}'::TIMESTAMP`;
|
|
262
292
|
}
|
|
293
|
+
return `TIMESTAMP '${timeString}'`;
|
|
263
294
|
}
|
|
264
295
|
getFunctionInfo(functionName) {
|
|
265
296
|
return this.functionInfo[functionName];
|
|
@@ -270,29 +301,10 @@ class PostgresDialect extends dialect_1.Dialect {
|
|
|
270
301
|
if (inSeconds[units]) {
|
|
271
302
|
lVal = (0, malloy_types_1.mkExpr) `EXTRACT(EPOCH FROM ${lVal})`;
|
|
272
303
|
rVal = (0, malloy_types_1.mkExpr) `EXTRACT(EPOCH FROM ${rVal})`;
|
|
273
|
-
const duration = (0, malloy_types_1.mkExpr)
|
|
304
|
+
const duration = (0, malloy_types_1.mkExpr) `${rVal}-${lVal}`;
|
|
274
305
|
return units === 'second'
|
|
275
|
-
? duration
|
|
276
|
-
: (0, malloy_types_1.mkExpr) `
|
|
277
|
-
}
|
|
278
|
-
if (units === 'day') {
|
|
279
|
-
return (0, malloy_types_1.mkExpr) `${rVal}::date - ${lVal}::date`;
|
|
280
|
-
}
|
|
281
|
-
const yearDiff = (0, malloy_types_1.mkExpr) `(DATE_PART('year', ${rVal}) - DATE_PART('year', ${lVal}))`;
|
|
282
|
-
if (units === 'year') {
|
|
283
|
-
return yearDiff;
|
|
284
|
-
}
|
|
285
|
-
if (units === 'week') {
|
|
286
|
-
const dayDiffForWeekStart = (0, malloy_types_1.mkExpr) `(DATE_TRUNC('week', ${rVal} + '1 day'::interval)::date - DATE_TRUNC('week', ${lVal} + '1 day'::interval)::date)`;
|
|
287
|
-
return (0, malloy_types_1.mkExpr) `${dayDiffForWeekStart} / 7`;
|
|
288
|
-
}
|
|
289
|
-
if (units === 'month') {
|
|
290
|
-
const monthDiff = (0, malloy_types_1.mkExpr) `DATE_PART('month', ${rVal}) - DATE_PART('month', ${lVal})`;
|
|
291
|
-
return (0, malloy_types_1.mkExpr) `${yearDiff} * 12 + ${monthDiff}`;
|
|
292
|
-
}
|
|
293
|
-
if (units === 'quarter') {
|
|
294
|
-
const qDiff = (0, malloy_types_1.mkExpr) `DATE_PART('quarter', ${rVal}) - DATE_PART('quarter', ${lVal})`;
|
|
295
|
-
return (0, malloy_types_1.mkExpr) `${yearDiff} * 4 + ${qDiff}`;
|
|
306
|
+
? (0, malloy_types_1.mkExpr) `FLOOR(${duration})`
|
|
307
|
+
: (0, malloy_types_1.mkExpr) `FLOOR((${duration})/${inSeconds[units].toString()}.0)`;
|
|
296
308
|
}
|
|
297
309
|
throw new Error(`Unknown or unhandled postgres time unit: ${units}`);
|
|
298
310
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Expr, ExtractUnit, Sampling, TimeValue, TimestampUnit, TypecastFragment } from '../model/malloy_types';
|
|
2
|
-
import { Dialect, DialectFieldList, FunctionInfo } from './dialect';
|
|
2
|
+
import { Dialect, DialectFieldList, FunctionInfo, QueryInfo } from './dialect';
|
|
3
3
|
export declare class StandardSQLDialect extends Dialect {
|
|
4
4
|
name: string;
|
|
5
5
|
defaultNumberType: string;
|
|
@@ -36,13 +36,13 @@ export declare class StandardSQLDialect extends Dialect {
|
|
|
36
36
|
keywords: string[];
|
|
37
37
|
sqlMaybeQuoteIdentifier(identifier: string): string;
|
|
38
38
|
sqlNow(): Expr;
|
|
39
|
-
sqlTrunc(sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
40
|
-
sqlExtract(expr: TimeValue, units: ExtractUnit): Expr;
|
|
39
|
+
sqlTrunc(qi: QueryInfo, sqlTime: TimeValue, units: TimestampUnit): Expr;
|
|
40
|
+
sqlExtract(qi: QueryInfo, expr: TimeValue, units: ExtractUnit): Expr;
|
|
41
41
|
sqlAlterTime(op: '+' | '-', expr: TimeValue, n: Expr, timeframe: TimestampUnit): Expr;
|
|
42
42
|
ignoreInProject(fieldName: string): boolean;
|
|
43
|
-
sqlCast(cast: TypecastFragment): Expr;
|
|
43
|
+
sqlCast(qi: QueryInfo, cast: TypecastFragment): Expr;
|
|
44
44
|
sqlRegexpMatch(expr: Expr, regexp: string): Expr;
|
|
45
|
-
sqlLiteralTime(timeString: string, type: 'date' | 'timestamp', timezone: string): string;
|
|
45
|
+
sqlLiteralTime(qi: QueryInfo, timeString: string, type: 'date' | 'timestamp', timezone: string | undefined): string;
|
|
46
46
|
sqlMeasureTime(from: TimeValue, to: TimeValue, units: string): Expr;
|
|
47
47
|
sqlSampleTable(tableSQL: string, sample: Sampling | undefined): string;
|
|
48
48
|
sqlLiteralString(literal: string): string;
|