@malloydata/malloy 0.0.311 → 0.0.313
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/mysql/mysql.js +3 -3
- package/dist/dialect/pg_impl.js +6 -1
- package/dist/dialect/trino/trino.d.ts +2 -1
- package/dist/dialect/trino/trino.js +30 -0
- package/dist/malloy.d.ts +1 -0
- package/dist/malloy.js +8 -0
- package/dist/model/expression_compiler.js +3 -3
- package/dist/model/field_instance.d.ts +3 -0
- package/dist/model/field_instance.js +6 -0
- package/dist/model/filter_compilers.d.ts +5 -4
- package/dist/model/filter_compilers.js +26 -15
- package/dist/model/malloy_types.d.ts +3 -0
- package/dist/model/malloy_types.js +7 -0
- package/dist/model/query_query.js +15 -2
- package/dist/to_stable.js +13 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -231,10 +231,10 @@ class MySQLDialect extends dialect_1.Dialect {
|
|
|
231
231
|
}
|
|
232
232
|
sqlSumDistinct(key, value, funcName) {
|
|
233
233
|
const sqlDistinctKey = `CONCAT(${key}, '')`;
|
|
234
|
-
const upperPart = `CAST(CONV(SUBSTRING(MD5(${sqlDistinctKey}), 1, 16), 16, 10) AS DECIMAL(
|
|
235
|
-
const lowerPart = `CAST(CONV(SUBSTRING(MD5(${sqlDistinctKey}), 16, 8), 16, 10) AS DECIMAL(
|
|
234
|
+
const upperPart = `CAST(CONV(SUBSTRING(MD5(${sqlDistinctKey}), 1, 16), 16, 10) AS DECIMAL(55, 10)) * 4294967296`;
|
|
235
|
+
const lowerPart = `CAST(CONV(SUBSTRING(MD5(${sqlDistinctKey}), 16, 8), 16, 10) AS DECIMAL(55, 10))`;
|
|
236
236
|
const hashkey = `(${upperPart} + ${lowerPart})`;
|
|
237
|
-
const v = `COALESCE(${value},0)`;
|
|
237
|
+
const v = `CAST(COALESCE(${value},0) as DECIMAL(55, 10))`;
|
|
238
238
|
const sqlSum = `(SUM(DISTINCT ${hashkey} + ${v}) - SUM(DISTINCT ${hashkey}))`;
|
|
239
239
|
if (funcName === 'SUM') {
|
|
240
240
|
return sqlSum;
|
package/dist/dialect/pg_impl.js
CHANGED
|
@@ -26,13 +26,18 @@ class PostgresBase extends dialect_1.Dialect {
|
|
|
26
26
|
if (malloy_types_1.TD.isTimestamp(df.e.typeDef)) {
|
|
27
27
|
const tz = (0, dialect_1.qtz)(qi);
|
|
28
28
|
if (tz) {
|
|
29
|
+
// get a civil version of the time in the query time zone
|
|
29
30
|
const civilSource = `(${truncThis}::TIMESTAMPTZ AT TIME ZONE '${tz}')`;
|
|
31
|
+
// do truncation in that time space
|
|
30
32
|
let civilTrunc = `DATE_TRUNC('${df.units}', ${civilSource})`;
|
|
31
33
|
if (week) {
|
|
32
34
|
civilTrunc = `(${civilTrunc} - INTERVAL '1' DAY)`;
|
|
33
35
|
}
|
|
34
|
-
//
|
|
36
|
+
// make a tstz from the civil time ... "AT TIME ZONE" of
|
|
37
|
+
// a TIMESTAMP will produce a TIMESTAMPTZ in that zone
|
|
38
|
+
// where the civi appeareance is the same as the TIMESTAMP
|
|
35
39
|
const truncTsTz = `${civilTrunc} AT TIME ZONE '${tz}'`;
|
|
40
|
+
// Now just make a system TIMESTAMP from that
|
|
36
41
|
return `(${truncTsTz})::TIMESTAMP`;
|
|
37
42
|
}
|
|
38
43
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Expr, Sampling, AtomicTypeDef, TimeDeltaExpr, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeLiteralNode, TimeExtractExpr, BasicAtomicTypeDef, RecordLiteralNode } from '../../model/malloy_types';
|
|
1
|
+
import type { Expr, Sampling, AtomicTypeDef, TimeDeltaExpr, TypecastExpr, RegexMatchExpr, MeasureTimeExpr, TimeLiteralNode, TimeExtractExpr, TimeTruncExpr, BasicAtomicTypeDef, RecordLiteralNode } from '../../model/malloy_types';
|
|
2
2
|
import type { DialectFunctionOverloadDef } from '../functions';
|
|
3
3
|
import type { DialectFieldList, OrderByClauseType, QueryInfo } from '../dialect';
|
|
4
4
|
import { PostgresBase } from '../pg_impl';
|
|
@@ -72,6 +72,7 @@ export declare class TrinoDialect extends PostgresBase {
|
|
|
72
72
|
sqlStringAggDistinct(distinctKey: string, valueSQL: string, separatorSQL: string): string;
|
|
73
73
|
validateTypeName(sqlType: string): boolean;
|
|
74
74
|
sqlLiteralTime(qi: QueryInfo, lit: TimeLiteralNode): string;
|
|
75
|
+
sqlTruncExpr(qi: QueryInfo, df: TimeTruncExpr): string;
|
|
75
76
|
sqlTimeExtractExpr(qi: QueryInfo, from: TimeExtractExpr): string;
|
|
76
77
|
sqlLiteralRecord(lit: RecordLiteralNode): string;
|
|
77
78
|
}
|
|
@@ -498,6 +498,36 @@ ${(0, utils_1.indent)(sql)}
|
|
|
498
498
|
}
|
|
499
499
|
return `TIMESTAMP '${lit.literal}'`;
|
|
500
500
|
}
|
|
501
|
+
sqlTruncExpr(qi, df) {
|
|
502
|
+
// adjusting for monday/sunday weeks
|
|
503
|
+
const week = df.units === 'week';
|
|
504
|
+
const truncThis = week ? `${df.e.sql} + INTERVAL '1' DAY` : df.e.sql;
|
|
505
|
+
// Only do timezone conversion for timestamps, not dates
|
|
506
|
+
if (malloy_types_1.TD.isTimestamp(df.e.typeDef)) {
|
|
507
|
+
const tz = qtz(qi);
|
|
508
|
+
if (tz) {
|
|
509
|
+
// get a civil version of the time in the query time zone
|
|
510
|
+
const civilSource = `(CAST(${truncThis} AS TIMESTAMP WITH TIME ZONE) AT TIME ZONE '${tz}')`;
|
|
511
|
+
// do truncation in that time space
|
|
512
|
+
let civilTrunc = `DATE_TRUNC('${df.units}', ${civilSource})`;
|
|
513
|
+
if (week) {
|
|
514
|
+
civilTrunc = `(${civilTrunc} - INTERVAL '1' DAY)`;
|
|
515
|
+
}
|
|
516
|
+
// make a tstz from the civil time ... "AT TIME ZONE" of
|
|
517
|
+
// a TIMESTAMP will produce a TIMESTAMP WITH TIME ZONE in that zone
|
|
518
|
+
// where the civil appearance is the same as the TIMESTAMP
|
|
519
|
+
const truncTsTz = `${civilTrunc} AT TIME ZONE '${tz}'`;
|
|
520
|
+
// Now just make a system TIMESTAMP from that
|
|
521
|
+
return `CAST(${truncTsTz} AS TIMESTAMP)`;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// For dates (civil time) or timestamps without query timezone
|
|
525
|
+
let result = `DATE_TRUNC('${df.units}', ${truncThis})`;
|
|
526
|
+
if (week) {
|
|
527
|
+
result = `(${result} - INTERVAL '1' DAY)`;
|
|
528
|
+
}
|
|
529
|
+
return result;
|
|
530
|
+
}
|
|
501
531
|
sqlTimeExtractExpr(qi, from) {
|
|
502
532
|
const pgUnits = pg_impl_1.timeExtractMap[from.units] || from.units;
|
|
503
533
|
let extractFrom = from.e.sql || '';
|
package/dist/malloy.d.ts
CHANGED
|
@@ -757,6 +757,7 @@ export declare class ExploreField extends Explore {
|
|
|
757
757
|
isAtomicField(): this is AtomicField;
|
|
758
758
|
get parentExplore(): Explore;
|
|
759
759
|
get sourceClasses(): string[];
|
|
760
|
+
get queryTimezone(): string | undefined;
|
|
760
761
|
}
|
|
761
762
|
type Connectionable = {
|
|
762
763
|
connection: Connection;
|
package/dist/malloy.js
CHANGED
|
@@ -1715,6 +1715,14 @@ class ExploreField extends Explore {
|
|
|
1715
1715
|
const sourceField = this.structDef.name || this.structDef.as;
|
|
1716
1716
|
return sourceField ? [sourceField] : [];
|
|
1717
1717
|
}
|
|
1718
|
+
get queryTimezone() {
|
|
1719
|
+
// For ExploreField, check the structDef directly first
|
|
1720
|
+
if ((0, model_1.isRecordOrRepeatedRecord)(this._structDef)) {
|
|
1721
|
+
return this._structDef.queryTimezone;
|
|
1722
|
+
}
|
|
1723
|
+
// Fall back to the parent implementation
|
|
1724
|
+
return super.queryTimezone;
|
|
1725
|
+
}
|
|
1718
1726
|
}
|
|
1719
1727
|
exports.ExploreField = ExploreField;
|
|
1720
1728
|
/**
|
|
@@ -212,14 +212,14 @@ function exprToSQL(resultSet, context, exprToTranslate, state = new utils_1.Gene
|
|
|
212
212
|
case 'compositeField':
|
|
213
213
|
return '{COMPOSITE_FIELD}';
|
|
214
214
|
case 'filterMatch':
|
|
215
|
-
return generateAppliedFilter(context, expr);
|
|
215
|
+
return generateAppliedFilter(context, expr, qi);
|
|
216
216
|
case 'filterLiteral':
|
|
217
217
|
return 'INTERNAL ERROR FILTER EXPRESSION VALUE SHOULD NOT BE USED';
|
|
218
218
|
default:
|
|
219
219
|
throw new Error(`Internal Error: Unknown expression node '${expr.node}' ${JSON.stringify(expr, undefined, 2)}`);
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
|
-
function generateAppliedFilter(context, filterMatchExpr) {
|
|
222
|
+
function generateAppliedFilter(context, filterMatchExpr, qi) {
|
|
223
223
|
var _a;
|
|
224
224
|
let filterExpr = filterMatchExpr.kids.filterExpr;
|
|
225
225
|
while (filterExpr.node === '()') {
|
|
@@ -261,7 +261,7 @@ function generateAppliedFilter(context, filterMatchExpr) {
|
|
|
261
261
|
if (fParse.log.length > 0) {
|
|
262
262
|
throw new Error(`Filter expression parse error: ${fParse.log[0]}`);
|
|
263
263
|
}
|
|
264
|
-
return filter_compilers_1.FilterCompilers.compile(filterMatchExpr.dataType, fParse.parsed, filterMatchExpr.kids.expr.sql || '', context.dialect);
|
|
264
|
+
return filter_compilers_1.FilterCompilers.compile(filterMatchExpr.dataType, fParse.parsed, filterMatchExpr.kids.expr.sql || '', context.dialect, qi);
|
|
265
265
|
}
|
|
266
266
|
// Helper functions for generateFunctionCallExpression
|
|
267
267
|
function getParameterMap(overload, numArgs) {
|
|
@@ -55,6 +55,9 @@ export declare class FieldInstanceResult implements FieldInstance {
|
|
|
55
55
|
* Information about the query containing this result set. Invented
|
|
56
56
|
* to pass on timezone information, but maybe more things will
|
|
57
57
|
* eventually go in here.
|
|
58
|
+
*
|
|
59
|
+
* For nested queries, this walks up the FieldInstanceResult parent chain
|
|
60
|
+
* to find the most specific (innermost) query timezone that applies.
|
|
58
61
|
* @returns QueryInfo
|
|
59
62
|
*/
|
|
60
63
|
getQueryInfo(): QueryInfo;
|
|
@@ -117,6 +117,9 @@ class FieldInstanceResult {
|
|
|
117
117
|
* Information about the query containing this result set. Invented
|
|
118
118
|
* to pass on timezone information, but maybe more things will
|
|
119
119
|
* eventually go in here.
|
|
120
|
+
*
|
|
121
|
+
* For nested queries, this walks up the FieldInstanceResult parent chain
|
|
122
|
+
* to find the most specific (innermost) query timezone that applies.
|
|
120
123
|
* @returns QueryInfo
|
|
121
124
|
*/
|
|
122
125
|
getQueryInfo() {
|
|
@@ -127,6 +130,9 @@ class FieldInstanceResult {
|
|
|
127
130
|
return { queryTimezone };
|
|
128
131
|
}
|
|
129
132
|
}
|
|
133
|
+
if (this.parent) {
|
|
134
|
+
return this.parent.getQueryInfo();
|
|
135
|
+
}
|
|
130
136
|
return {};
|
|
131
137
|
}
|
|
132
138
|
addField(as, field, usage, drillExpression) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { BooleanFilter, FilterExpression, NumberFilter, StringFilter, TemporalFilter } from '@malloydata/malloy-filter';
|
|
2
|
-
import type { Dialect } from '../dialect';
|
|
2
|
+
import type { Dialect, QueryInfo } from '../dialect';
|
|
3
3
|
export declare const FilterCompilers: {
|
|
4
|
-
compile(t: string, c: FilterExpression | null, x: string, d: Dialect): string;
|
|
4
|
+
compile(t: string, c: FilterExpression | null, x: string, d: Dialect, qi?: QueryInfo): string;
|
|
5
5
|
numberCompile(nc: NumberFilter, x: string, d: Dialect): string;
|
|
6
6
|
booleanCompile(bc: BooleanFilter, x: string, _d: Dialect): string;
|
|
7
7
|
stringCompile(sc: StringFilter, x: string, d: Dialect): string;
|
|
8
|
-
temporalCompile(tc: TemporalFilter, x: string, d: Dialect, t: "date" | "timestamp"): string;
|
|
8
|
+
temporalCompile(tc: TemporalFilter, x: string, d: Dialect, t: "date" | "timestamp", qi?: QueryInfo): string;
|
|
9
9
|
};
|
|
10
10
|
/**
|
|
11
11
|
* I felt like there was enough "helpful functions needed to make everything
|
|
@@ -17,7 +17,8 @@ export declare class TemporalFilterCompiler {
|
|
|
17
17
|
readonly expr: string;
|
|
18
18
|
readonly timetype: 'timestamp' | 'date';
|
|
19
19
|
readonly d: Dialect;
|
|
20
|
-
|
|
20
|
+
readonly qi: QueryInfo;
|
|
21
|
+
constructor(expr: string, dialect: Dialect, timetype?: 'timestamp' | 'date', queryInfo?: QueryInfo);
|
|
21
22
|
time(timeSQL: string): string;
|
|
22
23
|
compile(tc: TemporalFilter): string;
|
|
23
24
|
private expandLiteral;
|
|
@@ -37,7 +37,7 @@ function unlike(disLiked, x) {
|
|
|
37
37
|
* XXXXFilterCompiler.compile() will move to XXXFilterExpression.compile()
|
|
38
38
|
*/
|
|
39
39
|
exports.FilterCompilers = {
|
|
40
|
-
compile(t, c, x, d) {
|
|
40
|
+
compile(t, c, x, d, qi = {}) {
|
|
41
41
|
if (c === null) {
|
|
42
42
|
return 'true';
|
|
43
43
|
}
|
|
@@ -51,7 +51,7 @@ exports.FilterCompilers = {
|
|
|
51
51
|
return exports.FilterCompilers.booleanCompile(c, x, d);
|
|
52
52
|
}
|
|
53
53
|
else if ((t === 'date' || t === 'timestamp') && (0, malloy_filter_1.isTemporalFilter)(c)) {
|
|
54
|
-
return exports.FilterCompilers.temporalCompile(c, x, d, t);
|
|
54
|
+
return exports.FilterCompilers.temporalCompile(c, x, d, t, qi);
|
|
55
55
|
}
|
|
56
56
|
throw new Error('INTERNAL ERROR: No filter compiler for ' + t);
|
|
57
57
|
},
|
|
@@ -251,8 +251,8 @@ exports.FilterCompilers = {
|
|
|
251
251
|
}
|
|
252
252
|
},
|
|
253
253
|
// mtoy todo figure out what to do about dates
|
|
254
|
-
temporalCompile(tc, x, d, t) {
|
|
255
|
-
const c = new TemporalFilterCompiler(x, d, t);
|
|
254
|
+
temporalCompile(tc, x, d, t, qi = {}) {
|
|
255
|
+
const c = new TemporalFilterCompiler(x, d, t, qi);
|
|
256
256
|
return c.compile(tc);
|
|
257
257
|
},
|
|
258
258
|
};
|
|
@@ -269,10 +269,11 @@ const fTimestamp = `${fMinute}:ss`;
|
|
|
269
269
|
* a dialect as an argument?
|
|
270
270
|
*/
|
|
271
271
|
class TemporalFilterCompiler {
|
|
272
|
-
constructor(expr, dialect, timetype = 'timestamp') {
|
|
272
|
+
constructor(expr, dialect, timetype = 'timestamp', queryInfo = {}) {
|
|
273
273
|
this.expr = expr;
|
|
274
274
|
this.timetype = timetype;
|
|
275
275
|
this.d = dialect;
|
|
276
|
+
this.qi = queryInfo;
|
|
276
277
|
}
|
|
277
278
|
time(timeSQL) {
|
|
278
279
|
if (this.timetype === 'timestamp') {
|
|
@@ -430,7 +431,10 @@ class TemporalFilterCompiler {
|
|
|
430
431
|
typeDef: { type: 'timestamp' },
|
|
431
432
|
literal,
|
|
432
433
|
};
|
|
433
|
-
|
|
434
|
+
if (this.qi.queryTimezone) {
|
|
435
|
+
literalNode.timezone = this.qi.queryTimezone;
|
|
436
|
+
}
|
|
437
|
+
return { ...literalNode, sql: this.d.sqlLiteralTime(this.qi, literalNode) };
|
|
434
438
|
}
|
|
435
439
|
nowExpr() {
|
|
436
440
|
return {
|
|
@@ -460,7 +464,7 @@ class TemporalFilterCompiler {
|
|
|
460
464
|
e: (0, malloy_types_1.mkTemporal)(e, 'timestamp'),
|
|
461
465
|
units: 'day_of_week',
|
|
462
466
|
};
|
|
463
|
-
return { ...t, sql: this.d.sqlTimeExtractExpr(
|
|
467
|
+
return { ...t, sql: this.d.sqlTimeExtractExpr(this.qi, t) };
|
|
464
468
|
}
|
|
465
469
|
nowDot(units) {
|
|
466
470
|
const nowTruncExpr = {
|
|
@@ -468,7 +472,7 @@ class TemporalFilterCompiler {
|
|
|
468
472
|
e: this.nowExpr(),
|
|
469
473
|
units,
|
|
470
474
|
};
|
|
471
|
-
return { ...nowTruncExpr, sql: this.d.sqlTruncExpr(
|
|
475
|
+
return { ...nowTruncExpr, sql: this.d.sqlTruncExpr(this.qi, nowTruncExpr) };
|
|
472
476
|
}
|
|
473
477
|
thisUnit(units) {
|
|
474
478
|
const thisUnit = this.nowDot(units);
|
|
@@ -562,17 +566,24 @@ class TemporalFilterCompiler {
|
|
|
562
566
|
const destDayZeroBased = destDay - 1;
|
|
563
567
|
// dow is 1-7, convert to 0-6 for the arithmetic
|
|
564
568
|
const dowZeroBased = `(${dow.sql}-1)`;
|
|
565
|
-
let
|
|
569
|
+
let beginOffset;
|
|
570
|
+
let endOffset;
|
|
571
|
+
// "dayname" and "last dayname" both refer to the most recent,
|
|
572
|
+
// already complete day of that name. So if today is Sunday,
|
|
573
|
+
// "next sunday" is "today", and "last sunday" is 7 days ago.
|
|
566
574
|
if (direction === 'next') {
|
|
567
|
-
// Days forward: ((destDay -
|
|
568
|
-
|
|
575
|
+
// Days forward: ((destDay - currentDay + 6) % 7) + 1
|
|
576
|
+
beginOffset = `${this.mod7(`${destDayZeroBased}-${dowZeroBased}+6`)}+1`;
|
|
577
|
+
endOffset = `${this.mod7(`${destDayZeroBased}-${dowZeroBased}+6`)}+2`;
|
|
569
578
|
}
|
|
570
579
|
else {
|
|
571
|
-
// Days back: ((
|
|
572
|
-
|
|
580
|
+
// Days back: ((currentDay - destDay + 6) % 7) + 1
|
|
581
|
+
beginOffset = `${this.mod7(`${dowZeroBased}-${destDayZeroBased}+6`)}+1`;
|
|
582
|
+
// End offset is one day less (closer to today)
|
|
583
|
+
endOffset = `${this.mod7(`${dowZeroBased}-${destDayZeroBased}+6`)}`;
|
|
573
584
|
}
|
|
574
|
-
const begin = this.delta(todayBegin, direction === 'next' ? '+' : '-',
|
|
575
|
-
const end = this.delta(
|
|
585
|
+
const begin = this.delta(todayBegin, direction === 'next' ? '+' : '-', beginOffset, 'day');
|
|
586
|
+
const end = this.delta(todayBegin, direction === 'next' ? '+' : '-', endOffset, 'day');
|
|
576
587
|
return { begin, end: end.sql };
|
|
577
588
|
}
|
|
578
589
|
}
|
|
@@ -441,6 +441,7 @@ export interface RecordTypeDef {
|
|
|
441
441
|
export interface RecordDef extends RecordTypeDef, StructDefBase, JoinBase, FieldBase {
|
|
442
442
|
type: 'record';
|
|
443
443
|
join: 'one';
|
|
444
|
+
queryTimezone?: string;
|
|
444
445
|
}
|
|
445
446
|
export interface RecordElementTypeDef {
|
|
446
447
|
type: 'record_element';
|
|
@@ -453,11 +454,13 @@ export interface RepeatedRecordTypeDef {
|
|
|
453
454
|
export interface RepeatedRecordDef extends RepeatedRecordTypeDef, StructDefBase, JoinBase, FieldBase {
|
|
454
455
|
type: 'array';
|
|
455
456
|
join: 'many';
|
|
457
|
+
queryTimezone?: string;
|
|
456
458
|
}
|
|
457
459
|
export type ArrayTypeDef = BasicArrayTypeDef | RepeatedRecordTypeDef;
|
|
458
460
|
export type ArrayDef = BasicArrayDef | RepeatedRecordDef;
|
|
459
461
|
export declare function isRepeatedRecordFunctionParam(paramT: FunctionParameterTypeDef): paramT is RepeatedRecordFunctionParameterTypeDef;
|
|
460
462
|
export declare function isRepeatedRecord(fd: FieldDef | QueryFieldDef | StructDef | AtomicTypeDef): fd is RepeatedRecordTypeDef;
|
|
463
|
+
export declare function isRecordOrRepeatedRecord(fd: FieldDef | StructDef): fd is RecordDef | RepeatedRecordDef;
|
|
461
464
|
export declare function isBasicArray(td: AtomicTypeDef | FieldDef | QueryFieldDef | StructDef): td is BasicArrayTypeDef;
|
|
462
465
|
export interface ErrorTypeDef {
|
|
463
466
|
type: 'error';
|
|
@@ -52,6 +52,7 @@ exports.mkFieldDef = mkFieldDef;
|
|
|
52
52
|
exports.mkArrayDef = mkArrayDef;
|
|
53
53
|
exports.isRepeatedRecordFunctionParam = isRepeatedRecordFunctionParam;
|
|
54
54
|
exports.isRepeatedRecord = isRepeatedRecord;
|
|
55
|
+
exports.isRecordOrRepeatedRecord = isRecordOrRepeatedRecord;
|
|
55
56
|
exports.isBasicArray = isBasicArray;
|
|
56
57
|
exports.isMatrixOperation = isMatrixOperation;
|
|
57
58
|
exports.isJoinable = isJoinable;
|
|
@@ -303,6 +304,12 @@ function isRepeatedRecordFunctionParam(paramT) {
|
|
|
303
304
|
function isRepeatedRecord(fd) {
|
|
304
305
|
return fd.type === 'array' && fd.elementTypeDef.type === 'record_element';
|
|
305
306
|
}
|
|
307
|
+
function isRecordOrRepeatedRecord(fd) {
|
|
308
|
+
return (fd.type === 'record' ||
|
|
309
|
+
(fd.type === 'array' &&
|
|
310
|
+
'elementTypeDef' in fd &&
|
|
311
|
+
fd.elementTypeDef.type === 'record_element'));
|
|
312
|
+
}
|
|
306
313
|
function isBasicArray(td) {
|
|
307
314
|
return td.type === 'array' && td.elementTypeDef.type !== 'record_element';
|
|
308
315
|
}
|
|
@@ -388,24 +388,37 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
388
388
|
const resultMetadata = this.getResultMetadata(fi);
|
|
389
389
|
if (fi instanceof field_instance_1.FieldInstanceResult) {
|
|
390
390
|
const { structDef, repeatedResultType } = this.generateTurtlePipelineSQL(fi, new stage_writer_1.StageWriter(true, undefined), '<nosource>');
|
|
391
|
+
// Get the timezone from the nested query
|
|
392
|
+
const nestedQueryInfo = fi.getQueryInfo();
|
|
393
|
+
const queryTimezone = nestedQueryInfo.queryTimezone;
|
|
391
394
|
if (repeatedResultType === 'nested') {
|
|
392
395
|
const multiLineNest = {
|
|
393
|
-
...structDef,
|
|
394
396
|
type: 'array',
|
|
395
397
|
elementTypeDef: { type: 'record_element' },
|
|
396
398
|
join: 'many',
|
|
397
399
|
name,
|
|
400
|
+
fields: structDef.fields,
|
|
401
|
+
...(structDef.annotation && { annotation: structDef.annotation }),
|
|
402
|
+
...(structDef.modelAnnotation && {
|
|
403
|
+
modelAnnotation: structDef.modelAnnotation,
|
|
404
|
+
}),
|
|
398
405
|
resultMetadata,
|
|
406
|
+
...(queryTimezone && { queryTimezone }),
|
|
399
407
|
};
|
|
400
408
|
fields.push(multiLineNest);
|
|
401
409
|
}
|
|
402
410
|
else {
|
|
403
411
|
const oneLineNest = {
|
|
404
|
-
...structDef,
|
|
405
412
|
type: 'record',
|
|
406
413
|
join: 'one',
|
|
407
414
|
name,
|
|
415
|
+
fields: structDef.fields,
|
|
416
|
+
...(structDef.annotation && { annotation: structDef.annotation }),
|
|
417
|
+
...(structDef.modelAnnotation && {
|
|
418
|
+
modelAnnotation: structDef.modelAnnotation,
|
|
419
|
+
}),
|
|
408
420
|
resultMetadata,
|
|
421
|
+
...(queryTimezone && { queryTimezone }),
|
|
409
422
|
};
|
|
410
423
|
fields.push(oneLineNest);
|
|
411
424
|
}
|
package/dist/to_stable.js
CHANGED
|
@@ -213,9 +213,17 @@ function convertFieldInfos(source, fields) {
|
|
|
213
213
|
const resultMetadataAnnotation = field.resultMetadata
|
|
214
214
|
? getResultMetadataAnnotation(field, field.resultMetadata)
|
|
215
215
|
: undefined;
|
|
216
|
+
// Check if this field has queryTimezone information (for RecordDef/RepeatedRecordDef)
|
|
217
|
+
let timezoneAnnotation;
|
|
218
|
+
if ((0, model_1.isRecordOrRepeatedRecord)(field) && field.queryTimezone) {
|
|
219
|
+
const timezoneTag = malloy_tag_1.Tag.withPrefix('#(malloy) ');
|
|
220
|
+
timezoneTag.set(['query_timezone'], field.queryTimezone);
|
|
221
|
+
timezoneAnnotation = { value: timezoneTag.toString() };
|
|
222
|
+
}
|
|
216
223
|
const fieldAnnotations = [
|
|
217
224
|
...(annotations !== null && annotations !== void 0 ? annotations : []),
|
|
218
225
|
...(resultMetadataAnnotation ? [resultMetadataAnnotation] : []),
|
|
226
|
+
...(timezoneAnnotation ? [timezoneAnnotation] : []),
|
|
219
227
|
];
|
|
220
228
|
const fieldInfo = {
|
|
221
229
|
kind: aggregate ? 'measure' : 'dimension',
|
|
@@ -343,6 +351,11 @@ function getResultStructMetadataAnnotation(field, resultMetadata) {
|
|
|
343
351
|
}
|
|
344
352
|
hasAny = true;
|
|
345
353
|
}
|
|
354
|
+
// Include queryTimezone if present on the field
|
|
355
|
+
if (field.queryTimezone) {
|
|
356
|
+
tag.set(['query_timezone'], field.queryTimezone);
|
|
357
|
+
hasAny = true;
|
|
358
|
+
}
|
|
346
359
|
return hasAny
|
|
347
360
|
? {
|
|
348
361
|
value: tag.toString(),
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.313";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.313';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.313",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@malloydata/malloy-filter": "0.0.
|
|
45
|
-
"@malloydata/malloy-interfaces": "0.0.
|
|
46
|
-
"@malloydata/malloy-tag": "0.0.
|
|
44
|
+
"@malloydata/malloy-filter": "0.0.313",
|
|
45
|
+
"@malloydata/malloy-interfaces": "0.0.313",
|
|
46
|
+
"@malloydata/malloy-tag": "0.0.313",
|
|
47
47
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
48
48
|
"assert": "^2.0.0",
|
|
49
49
|
"jaro-winkler": "^0.2.8",
|