@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
|
@@ -63,6 +63,7 @@ import { DefExplorePrimaryKeyContext } from "./MalloyParser";
|
|
|
63
63
|
import { DefExploreRenameContext } from "./MalloyParser";
|
|
64
64
|
import { DefExploreEditFieldContext } from "./MalloyParser";
|
|
65
65
|
import { DefExploreQueryContext } from "./MalloyParser";
|
|
66
|
+
import { DefExploreTimezoneContext } from "./MalloyParser";
|
|
66
67
|
import { FilterByShortcutContext } from "./MalloyParser";
|
|
67
68
|
import { FilterByWhereContext } from "./MalloyParser";
|
|
68
69
|
import { JoinWithContext } from "./MalloyParser";
|
|
@@ -141,6 +142,7 @@ import { IndexElementContext } from "./MalloyParser";
|
|
|
141
142
|
import { IndexFieldsContext } from "./MalloyParser";
|
|
142
143
|
import { IndexStatementContext } from "./MalloyParser";
|
|
143
144
|
import { SampleStatementContext } from "./MalloyParser";
|
|
145
|
+
import { TimezoneStatementContext } from "./MalloyParser";
|
|
144
146
|
import { SampleSpecContext } from "./MalloyParser";
|
|
145
147
|
import { AggregateContext } from "./MalloyParser";
|
|
146
148
|
import { MalloyTypeContext } from "./MalloyParser";
|
|
@@ -944,6 +946,18 @@ export interface MalloyParserListener extends ParseTreeListener {
|
|
|
944
946
|
* @param ctx the parse tree
|
|
945
947
|
*/
|
|
946
948
|
exitDefExploreQuery?: (ctx: DefExploreQueryContext) => void;
|
|
949
|
+
/**
|
|
950
|
+
* Enter a parse tree produced by the `defExploreTimezone`
|
|
951
|
+
* labeled alternative in `MalloyParser.exploreStatement`.
|
|
952
|
+
* @param ctx the parse tree
|
|
953
|
+
*/
|
|
954
|
+
enterDefExploreTimezone?: (ctx: DefExploreTimezoneContext) => void;
|
|
955
|
+
/**
|
|
956
|
+
* Exit a parse tree produced by the `defExploreTimezone`
|
|
957
|
+
* labeled alternative in `MalloyParser.exploreStatement`.
|
|
958
|
+
* @param ctx the parse tree
|
|
959
|
+
*/
|
|
960
|
+
exitDefExploreTimezone?: (ctx: DefExploreTimezoneContext) => void;
|
|
947
961
|
/**
|
|
948
962
|
* Enter a parse tree produced by the `filterByShortcut`
|
|
949
963
|
* labeled alternative in `MalloyParser.filteredBy`.
|
|
@@ -1732,6 +1746,16 @@ export interface MalloyParserListener extends ParseTreeListener {
|
|
|
1732
1746
|
* @param ctx the parse tree
|
|
1733
1747
|
*/
|
|
1734
1748
|
exitSampleStatement?: (ctx: SampleStatementContext) => void;
|
|
1749
|
+
/**
|
|
1750
|
+
* Enter a parse tree produced by `MalloyParser.timezoneStatement`.
|
|
1751
|
+
* @param ctx the parse tree
|
|
1752
|
+
*/
|
|
1753
|
+
enterTimezoneStatement?: (ctx: TimezoneStatementContext) => void;
|
|
1754
|
+
/**
|
|
1755
|
+
* Exit a parse tree produced by `MalloyParser.timezoneStatement`.
|
|
1756
|
+
* @param ctx the parse tree
|
|
1757
|
+
*/
|
|
1758
|
+
exitTimezoneStatement?: (ctx: TimezoneStatementContext) => void;
|
|
1735
1759
|
/**
|
|
1736
1760
|
* Enter a parse tree produced by `MalloyParser.sampleSpec`.
|
|
1737
1761
|
* @param ctx the parse tree
|
|
@@ -63,6 +63,7 @@ import { DefExplorePrimaryKeyContext } from "./MalloyParser";
|
|
|
63
63
|
import { DefExploreRenameContext } from "./MalloyParser";
|
|
64
64
|
import { DefExploreEditFieldContext } from "./MalloyParser";
|
|
65
65
|
import { DefExploreQueryContext } from "./MalloyParser";
|
|
66
|
+
import { DefExploreTimezoneContext } from "./MalloyParser";
|
|
66
67
|
import { FilterByShortcutContext } from "./MalloyParser";
|
|
67
68
|
import { FilterByWhereContext } from "./MalloyParser";
|
|
68
69
|
import { JoinWithContext } from "./MalloyParser";
|
|
@@ -141,6 +142,7 @@ import { IndexElementContext } from "./MalloyParser";
|
|
|
141
142
|
import { IndexFieldsContext } from "./MalloyParser";
|
|
142
143
|
import { IndexStatementContext } from "./MalloyParser";
|
|
143
144
|
import { SampleStatementContext } from "./MalloyParser";
|
|
145
|
+
import { TimezoneStatementContext } from "./MalloyParser";
|
|
144
146
|
import { SampleSpecContext } from "./MalloyParser";
|
|
145
147
|
import { AggregateContext } from "./MalloyParser";
|
|
146
148
|
import { MalloyTypeContext } from "./MalloyParser";
|
|
@@ -627,6 +629,13 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
|
|
|
627
629
|
* @return the visitor result
|
|
628
630
|
*/
|
|
629
631
|
visitDefExploreQuery?: (ctx: DefExploreQueryContext) => Result;
|
|
632
|
+
/**
|
|
633
|
+
* Visit a parse tree produced by the `defExploreTimezone`
|
|
634
|
+
* labeled alternative in `MalloyParser.exploreStatement`.
|
|
635
|
+
* @param ctx the parse tree
|
|
636
|
+
* @return the visitor result
|
|
637
|
+
*/
|
|
638
|
+
visitDefExploreTimezone?: (ctx: DefExploreTimezoneContext) => Result;
|
|
630
639
|
/**
|
|
631
640
|
* Visit a parse tree produced by the `filterByShortcut`
|
|
632
641
|
* labeled alternative in `MalloyParser.filteredBy`.
|
|
@@ -1099,6 +1108,12 @@ export interface MalloyParserVisitor<Result> extends ParseTreeVisitor<Result> {
|
|
|
1099
1108
|
* @return the visitor result
|
|
1100
1109
|
*/
|
|
1101
1110
|
visitSampleStatement?: (ctx: SampleStatementContext) => Result;
|
|
1111
|
+
/**
|
|
1112
|
+
* Visit a parse tree produced by `MalloyParser.timezoneStatement`.
|
|
1113
|
+
* @param ctx the parse tree
|
|
1114
|
+
* @return the visitor result
|
|
1115
|
+
*/
|
|
1116
|
+
visitTimezoneStatement?: (ctx: TimezoneStatementContext) => Result;
|
|
1102
1117
|
/**
|
|
1103
1118
|
* Visit a parse tree produced by `MalloyParser.sampleSpec`.
|
|
1104
1119
|
* @param ctx the parse tree
|
|
@@ -88,6 +88,8 @@ export declare class MalloyToAST extends AbstractParseTreeVisitor<ast.MalloyElem
|
|
|
88
88
|
visitDefExplorePrimaryKey(pcx: parse.DefExplorePrimaryKeyContext): ast.PrimaryKey;
|
|
89
89
|
visitFieldNameList(pcx: parse.FieldNameListContext): ast.FieldReferences;
|
|
90
90
|
visitDefExploreEditField(pcx: parse.DefExploreEditFieldContext): ast.FieldListEdit;
|
|
91
|
+
visitDefExploreTimezone(cx: parse.DefExploreTimezoneContext): ast.TimezoneStatement;
|
|
92
|
+
visitTimezoneStatement(cx: parse.TimezoneStatementContext): ast.TimezoneStatement;
|
|
91
93
|
visitQueryProperties(pcx: parse.QueryPropertiesContext): ast.QOPDesc;
|
|
92
94
|
visitFieldPath(pcx: parse.FieldPathContext): ast.FieldReference;
|
|
93
95
|
visitQueryFieldDef(pcx: parse.QueryFieldDefContext): ast.QueryItem;
|
|
@@ -468,6 +468,12 @@ class MalloyToAST extends AbstractParseTreeVisitor_1.AbstractParseTreeVisitor {
|
|
|
468
468
|
const action = pcx.ACCEPT() ? 'accept' : 'except';
|
|
469
469
|
return new ast.FieldListEdit(action, this.visitFieldNameList(pcx.fieldNameList()));
|
|
470
470
|
}
|
|
471
|
+
visitDefExploreTimezone(cx) {
|
|
472
|
+
return this.visitTimezoneStatement(cx.timezoneStatement());
|
|
473
|
+
}
|
|
474
|
+
visitTimezoneStatement(cx) {
|
|
475
|
+
return this.astAt(new ast.TimezoneStatement(this.stripQuotes(cx.STRING_LITERAL().text)), cx);
|
|
476
|
+
}
|
|
471
477
|
visitQueryProperties(pcx) {
|
|
472
478
|
const qProps = pcx
|
|
473
479
|
.queryStatement()
|
|
@@ -116,6 +116,15 @@ expect.extend({
|
|
|
116
116
|
x.translate();
|
|
117
117
|
return checkForNeededs(x);
|
|
118
118
|
},
|
|
119
|
+
expressionCompiled: function (src) {
|
|
120
|
+
const x = new test_translator_1.BetaExpression(src);
|
|
121
|
+
x.compile();
|
|
122
|
+
const errorCheck = checkForErrors(x);
|
|
123
|
+
if (!errorCheck.pass) {
|
|
124
|
+
return errorCheck;
|
|
125
|
+
}
|
|
126
|
+
return checkForNeededs(x);
|
|
127
|
+
},
|
|
119
128
|
toBeErrorless: function (trans) {
|
|
120
129
|
return checkForErrors(trans);
|
|
121
130
|
},
|
|
@@ -135,7 +144,7 @@ expect.extend({
|
|
|
135
144
|
},
|
|
136
145
|
compileToFailWith: function (s, ...msgs) {
|
|
137
146
|
var _a;
|
|
138
|
-
let emsg = 'Compile Error expectation not met\nExpected
|
|
147
|
+
let emsg = 'Compile Error expectation not met\nExpected message';
|
|
139
148
|
let mSrc;
|
|
140
149
|
const qmsgs = msgs.map(s => `error '${s}'`);
|
|
141
150
|
if (msgs.length === 1) {
|
|
@@ -161,6 +170,7 @@ expect.extend({
|
|
|
161
170
|
m = new test_translator_1.TestTranslator(src);
|
|
162
171
|
}
|
|
163
172
|
emsg += `\nSource:\n${src}`;
|
|
173
|
+
m.compile();
|
|
164
174
|
const t = m.translate();
|
|
165
175
|
if (t.translated) {
|
|
166
176
|
return { pass: false, message: () => emsg };
|
|
@@ -533,7 +533,6 @@ describe('literals', () => {
|
|
|
533
533
|
['@1960-Q2', 'date', 'quarter', { literal: '1960-04-01' }],
|
|
534
534
|
['@1960-06', 'date', 'month', { literal: '1960-06-01' }],
|
|
535
535
|
['@1960-06-26-WK', 'date', 'week', { literal: '1960-06-26' }],
|
|
536
|
-
['@1960-06-30-WK', 'date', 'week', { literal: '1960-06-26' }],
|
|
537
536
|
['@1960-06-30', 'date', 'day', { literal: '1960-06-30' }],
|
|
538
537
|
['@1960-06-30 10', 'timestamp', 'hour', { literal: '1960-06-30 10:00:00' }],
|
|
539
538
|
[
|
|
@@ -560,24 +559,6 @@ describe('literals', () => {
|
|
|
560
559
|
undefined,
|
|
561
560
|
{ literal: '1960-06-30 10:30:00' },
|
|
562
561
|
],
|
|
563
|
-
[
|
|
564
|
-
'@1960-06-30 10:30:00+0',
|
|
565
|
-
'timestamp',
|
|
566
|
-
undefined,
|
|
567
|
-
{ literal: '1960-06-30 10:30:00', timezone: '+0' },
|
|
568
|
-
],
|
|
569
|
-
[
|
|
570
|
-
'@1960-06-30 10:30:00-7',
|
|
571
|
-
'timestamp',
|
|
572
|
-
undefined,
|
|
573
|
-
{ literal: '1960-06-30 10:30:00', timezone: '-7' },
|
|
574
|
-
],
|
|
575
|
-
[
|
|
576
|
-
'@1960-06-30 10:30:00-00:15',
|
|
577
|
-
'timestamp',
|
|
578
|
-
undefined,
|
|
579
|
-
{ literal: '1960-06-30 10:30:00', timezone: '-00:15' },
|
|
580
|
-
],
|
|
581
562
|
[
|
|
582
563
|
'@1960-06-30 10:30:00[America/Los_Angeles]',
|
|
583
564
|
'timestamp',
|
|
@@ -585,7 +566,6 @@ describe('literals', () => {
|
|
|
585
566
|
{
|
|
586
567
|
literal: '1960-06-30 10:30:00',
|
|
587
568
|
timezone: 'America/Los_Angeles',
|
|
588
|
-
tzIsLocale: true,
|
|
589
569
|
},
|
|
590
570
|
],
|
|
591
571
|
];
|
|
@@ -609,7 +589,7 @@ describe('literals', () => {
|
|
|
609
589
|
['@1960', '1960-01-01 00:00:00'],
|
|
610
590
|
['@1960-Q2', '1960-04-01 00:00:00'],
|
|
611
591
|
['@1960-06', '1960-06-01 00:00:00'],
|
|
612
|
-
['@1960-06-
|
|
592
|
+
['@1960-06-26-Wk', '1960-06-26 00:00:00'],
|
|
613
593
|
['@1960-06-30', '1960-06-30 00:00:00'],
|
|
614
594
|
['@1960-06-30 00:00', undefined],
|
|
615
595
|
];
|
|
@@ -648,16 +628,16 @@ describe('expressions', () => {
|
|
|
648
628
|
'quarter',
|
|
649
629
|
'year',
|
|
650
630
|
];
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
test(`timestamp truncate ${unit}`, exprOK(`ats.${unit}`));
|
|
654
|
-
}
|
|
631
|
+
test.each(timeframes.map(x => [x]))('truncate %s', unit => {
|
|
632
|
+
expect(new test_translator_1.BetaExpression(`ats.${unit}`)).modelParsed();
|
|
655
633
|
});
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
634
|
+
// mtoy todo units missing: implement, or document
|
|
635
|
+
const diffable = ['second', 'minute', 'hour', 'day'];
|
|
636
|
+
test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
|
|
637
|
+
expect(new test_translator_1.BetaExpression(`${unit}(@2021 to ats)`)).modelParsed();
|
|
638
|
+
});
|
|
639
|
+
test.each(diffable.map(x => [x]))('timestamp difference - %s', unit => {
|
|
640
|
+
expect(new test_translator_1.BetaExpression(`${unit}(ats to @2030)`)).modelParsed();
|
|
661
641
|
});
|
|
662
642
|
});
|
|
663
643
|
test('field name', exprOK('astr'));
|
|
@@ -681,6 +661,25 @@ describe('expressions', () => {
|
|
|
681
661
|
test('not', exprOK('not true'));
|
|
682
662
|
test('and', exprOK('true and false'));
|
|
683
663
|
test('or', exprOK('true or false'));
|
|
664
|
+
test('disallow date OP number', () => {
|
|
665
|
+
expect(new test_translator_1.BetaExpression('@2001 = 7')).compileToFailWith('Cannot compare a date to a number');
|
|
666
|
+
});
|
|
667
|
+
test('disallow date OP timestamp', () => {
|
|
668
|
+
expect(new test_translator_1.BetaExpression('ad = ats')).compileToFailWith('Cannot compare a date to a timestamp');
|
|
669
|
+
});
|
|
670
|
+
test('disallow interval from date to timestamp', () => {
|
|
671
|
+
expect(new test_translator_1.BetaExpression('days(ad to ats)')).compileToFailWith('Cannot measure from date to timestamp');
|
|
672
|
+
});
|
|
673
|
+
test('comparison promotes date literal to timestamp', () => {
|
|
674
|
+
expect('@2001 = ats').expressionCompiled();
|
|
675
|
+
});
|
|
676
|
+
test('can apply range to date', () => {
|
|
677
|
+
expect('ad ? @2001 for 1 day').expressionCompiled();
|
|
678
|
+
});
|
|
679
|
+
const noOffset = ['second', 'minute', 'hour'];
|
|
680
|
+
test.each(noOffset.map(x => [x]))('disallow date delta %s', unit => {
|
|
681
|
+
expect(new test_translator_1.BetaExpression(`ad + 10 ${unit}s`)).compileToFailWith(`Cannot offset date by ${unit}`);
|
|
682
|
+
});
|
|
684
683
|
});
|
|
685
684
|
test('filtered measure', exprOK("acount {? astr = 'why?' }"));
|
|
686
685
|
describe('aggregate forms', () => {
|
|
@@ -877,13 +876,6 @@ describe('error handling', () => {
|
|
|
877
876
|
test('query without fields', badModel('query: a -> { top: 5 }', "Can't determine query type (group_by/aggregate/nest,project,index)"));
|
|
878
877
|
test("refine can't change query type", badModel('query: ab -> aturtle { project: astr }', 'project: not legal in grouping query'));
|
|
879
878
|
test('undefined field ref in query', badModel('query: ab -> { aggregate: xyzzy }', "'xyzzy' is not defined"));
|
|
880
|
-
// test("queries with anonymous expressions", () => {
|
|
881
|
-
// const m = new TestTranslator("query: a->{\n group_by: a+1\n}");
|
|
882
|
-
// expect(m).not.modelParsed();
|
|
883
|
-
// const errList = m.errors().errors;
|
|
884
|
-
// const firstError = errList[0];
|
|
885
|
-
// expect(firstError.message).toBe("Expressions in queries must have names");
|
|
886
|
-
// });
|
|
887
879
|
test('query on source with errors', () => {
|
|
888
880
|
expect((0, test_translator_1.markSource) `
|
|
889
881
|
explore: na is a { join_one: ${'n'} on astr }
|
package/dist/malloy.js
CHANGED
|
@@ -25,6 +25,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
25
25
|
exports.CSVWriter = exports.JSONWriter = exports.DataWriter = exports.DataRecord = exports.DataArray = exports.Result = exports.ExploreMaterializer = exports.SQLBlockMaterializer = exports.PreparedResultMaterializer = exports.QueryMaterializer = exports.ModelMaterializer = exports.SingleConnectionRuntime = exports.ConnectionRuntime = exports.Runtime = exports.ExploreField = exports.JoinRelationship = exports.QueryField = exports.Query = exports.StringField = exports.UnsupportedField = exports.JSONField = exports.BooleanField = exports.NumberField = exports.TimestampField = exports.DateField = exports.TimestampTimeframe = exports.DateTimeframe = exports.AtomicField = exports.AtomicFieldType = exports.Explore = exports.SourceRelationship = exports.FixedConnectionMap = exports.InMemoryURLReader = exports.EmptyURLReader = exports.PreparedResult = exports.DocumentCompletion = exports.DocumentSymbol = exports.DocumentPosition = exports.DocumentRange = exports.DocumentHighlight = exports.Parse = exports.parseTableURI = exports.PreparedQuery = exports.Model = exports.MalloyError = exports.Malloy = void 0;
|
|
26
26
|
const lang_1 = require("./lang");
|
|
27
27
|
const model_1 = require("./model");
|
|
28
|
+
const luxon_1 = require("luxon");
|
|
28
29
|
class Malloy {
|
|
29
30
|
// TODO load from file built during release
|
|
30
31
|
static get version() {
|
|
@@ -2194,25 +2195,41 @@ class DataNumber extends ScalarData {
|
|
|
2194
2195
|
return this._field;
|
|
2195
2196
|
}
|
|
2196
2197
|
}
|
|
2198
|
+
function valueToDate(value) {
|
|
2199
|
+
// TODO properly map the data from BQ/Postgres types
|
|
2200
|
+
if (value instanceof Date) {
|
|
2201
|
+
return value;
|
|
2202
|
+
}
|
|
2203
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2204
|
+
const valAsAny = value;
|
|
2205
|
+
if (valAsAny.constructor.name === 'Date') {
|
|
2206
|
+
// For some reason duckdb TSTZ values come back as objects which do not
|
|
2207
|
+
// pass "instance of" but do seem date like.
|
|
2208
|
+
return new Date(value);
|
|
2209
|
+
}
|
|
2210
|
+
else if (typeof value === 'number') {
|
|
2211
|
+
return new Date(value);
|
|
2212
|
+
}
|
|
2213
|
+
else if (typeof value !== 'string') {
|
|
2214
|
+
return new Date(value.value);
|
|
2215
|
+
}
|
|
2216
|
+
else {
|
|
2217
|
+
// Postgres timestamps end up here, and ideally we would know the system
|
|
2218
|
+
// timezone of the postgres instance to correctly create a Date() object
|
|
2219
|
+
// which represents the same instant in time, but we don't have the data
|
|
2220
|
+
// flow to implement that. This may be a problem at some future day,
|
|
2221
|
+
// so here is a comment, for that day.
|
|
2222
|
+
const parsed = luxon_1.DateTime.fromISO(value, { zone: 'UTC' });
|
|
2223
|
+
return parsed.toJSDate();
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2197
2226
|
class DataTimestamp extends ScalarData {
|
|
2198
2227
|
constructor(value, field) {
|
|
2199
2228
|
super(value, field);
|
|
2200
2229
|
this._field = field;
|
|
2201
2230
|
}
|
|
2202
2231
|
get value() {
|
|
2203
|
-
|
|
2204
|
-
if (this._value instanceof Date) {
|
|
2205
|
-
return this._value;
|
|
2206
|
-
}
|
|
2207
|
-
else if (typeof this._value === 'number') {
|
|
2208
|
-
return new Date(this._value);
|
|
2209
|
-
}
|
|
2210
|
-
else if (typeof this._value !== 'string') {
|
|
2211
|
-
return new Date(this._value.value);
|
|
2212
|
-
}
|
|
2213
|
-
else {
|
|
2214
|
-
return new Date(this._value);
|
|
2215
|
-
}
|
|
2232
|
+
return valueToDate(this._value);
|
|
2216
2233
|
}
|
|
2217
2234
|
get field() {
|
|
2218
2235
|
return this._field;
|
|
@@ -2224,16 +2241,7 @@ class DataDate extends ScalarData {
|
|
|
2224
2241
|
this._field = field;
|
|
2225
2242
|
}
|
|
2226
2243
|
get value() {
|
|
2227
|
-
|
|
2228
|
-
if (this._value instanceof Date) {
|
|
2229
|
-
return this._value;
|
|
2230
|
-
}
|
|
2231
|
-
else if (typeof this._value !== 'string') {
|
|
2232
|
-
return new Date(this._value.value);
|
|
2233
|
-
}
|
|
2234
|
-
else {
|
|
2235
|
-
return new Date(this._value);
|
|
2236
|
-
}
|
|
2244
|
+
return valueToDate(this._value);
|
|
2237
2245
|
}
|
|
2238
2246
|
get field() {
|
|
2239
2247
|
return this._field;
|
|
@@ -2,6 +2,7 @@ import { Dialect, DialectFieldList } from '../dialect';
|
|
|
2
2
|
import { AggregateFragment, AnalyticFragment, CompiledQuery, DialectFragment, Expr, FieldDef, FieldFragment, FieldRef, Filtered, FilterExpression, FilterFragment, JoinRelationship, ModelDef, NamedQuery, OrderBy, Parameter, ParameterFragment, PipeSegment, Query, QueryFieldDef, QuerySegment, ResultMetadataDef, ResultStructMetadataDef, SearchIndexResult, StructDef, StructRef, TurtleDef, UngroupFragment } from './malloy_types';
|
|
3
3
|
import { Connection } from '../runtime_types';
|
|
4
4
|
import { AndChain } from './utils';
|
|
5
|
+
import { QueryInfo } from '../dialect/dialect';
|
|
5
6
|
interface TurtleDefPlus extends TurtleDef, Filtered {
|
|
6
7
|
}
|
|
7
8
|
/** Parent from QueryStruct. */
|
|
@@ -134,6 +135,7 @@ declare class FieldInstanceResult implements FieldInstance {
|
|
|
134
135
|
ungroupedSets: Map<string, UngroupSet>;
|
|
135
136
|
resultUsesUngrouped: boolean;
|
|
136
137
|
constructor(turtleDef: TurtleDef, parent: FieldInstanceResult | undefined);
|
|
138
|
+
getQueryInfo(): QueryInfo;
|
|
137
139
|
addField(as: string, field: QueryField, usage: FieldUsage): void;
|
|
138
140
|
parentGroupSet(): number;
|
|
139
141
|
add(name: string, f: FieldInstance): void;
|
|
@@ -349,7 +349,7 @@ class QueryField extends QueryNode {
|
|
|
349
349
|
}
|
|
350
350
|
}
|
|
351
351
|
generateDialect(resultSet, context, expr, state) {
|
|
352
|
-
return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(expr), state);
|
|
352
|
+
return this.generateExpressionFromExpr(resultSet, context, context.dialect.dialectExpr(resultSet.getQueryInfo(), expr), state);
|
|
353
353
|
}
|
|
354
354
|
generateAnalyticFragment(resultStruct, context, expr, state) {
|
|
355
355
|
const fields = resultStruct.getUngroupPartitions(undefined);
|
|
@@ -559,7 +559,7 @@ class QueryFieldDate extends QueryAtomicField {
|
|
|
559
559
|
return super.generateExpression(resultSet);
|
|
560
560
|
}
|
|
561
561
|
else {
|
|
562
|
-
const truncated = this.parent.dialect.sqlTrunc({ value: this.getExpr(), valueType: 'date' }, fd.timeframe);
|
|
562
|
+
const truncated = this.parent.dialect.sqlTrunc(resultSet.getQueryInfo(), { value: this.getExpr(), valueType: 'date' }, fd.timeframe);
|
|
563
563
|
return this.generateExpressionFromExpr(resultSet, this.parent, truncated);
|
|
564
564
|
}
|
|
565
565
|
}
|
|
@@ -666,9 +666,18 @@ class FieldInstanceResult {
|
|
|
666
666
|
this.turtleDef = turtleDef;
|
|
667
667
|
this.firstSegment = turtleDef.pipeline[0];
|
|
668
668
|
}
|
|
669
|
+
getQueryInfo() {
|
|
670
|
+
if (!(0, malloy_types_1.isIndexSegment)(this.firstSegment)) {
|
|
671
|
+
const { queryTimezone } = this.firstSegment;
|
|
672
|
+
if (queryTimezone) {
|
|
673
|
+
return { queryTimezone };
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
return {};
|
|
677
|
+
}
|
|
669
678
|
addField(as, field, usage) {
|
|
670
|
-
|
|
671
|
-
if (
|
|
679
|
+
const fi = this.allFields.get(as);
|
|
680
|
+
if (fi) {
|
|
672
681
|
if (fi.type === 'query') {
|
|
673
682
|
throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
|
|
674
683
|
}
|
|
@@ -1085,7 +1094,6 @@ class QueryTurtle extends QueryField {
|
|
|
1085
1094
|
* half translated to the new world of types ..
|
|
1086
1095
|
*/
|
|
1087
1096
|
class Segment {
|
|
1088
|
-
// static nextStructDef(s: StructDef, q: AnonymousQueryDef): StructDef
|
|
1089
1097
|
static nextStructDef(structDef, segment) {
|
|
1090
1098
|
const qs = new QueryStruct(structDef, {
|
|
1091
1099
|
model: new QueryModel(undefined),
|
|
@@ -1118,6 +1126,7 @@ class QueryQuery extends QueryField {
|
|
|
1118
1126
|
let flatTurtleDef = parentStruct.flattenTurtleDef(fieldDef);
|
|
1119
1127
|
let parent = parentStruct;
|
|
1120
1128
|
const firstStage = flatTurtleDef.pipeline[0];
|
|
1129
|
+
const sourceDef = parentStruct.fieldDef;
|
|
1121
1130
|
// if we are generating code
|
|
1122
1131
|
// and have extended declaration, we need to make a new QueryStruct
|
|
1123
1132
|
// copy the definitions into a new structdef
|
|
@@ -1126,8 +1135,8 @@ class QueryQuery extends QueryField {
|
|
|
1126
1135
|
(0, malloy_types_1.isQuerySegment)(firstStage) &&
|
|
1127
1136
|
firstStage.extendSource !== undefined) {
|
|
1128
1137
|
parent = new QueryStruct({
|
|
1129
|
-
...
|
|
1130
|
-
fields: [...
|
|
1138
|
+
...sourceDef,
|
|
1139
|
+
fields: [...sourceDef.fields, ...firstStage.extendSource],
|
|
1131
1140
|
}, parent.parent ? { struct: parent } : { model: parent.model });
|
|
1132
1141
|
flatTurtleDef = {
|
|
1133
1142
|
...flatTurtleDef,
|
|
@@ -1140,6 +1149,11 @@ class QueryQuery extends QueryField {
|
|
|
1140
1149
|
],
|
|
1141
1150
|
};
|
|
1142
1151
|
}
|
|
1152
|
+
if (sourceDef.queryTimezone &&
|
|
1153
|
+
(0, malloy_types_1.isQuerySegment)(firstStage) &&
|
|
1154
|
+
firstStage.queryTimezone === undefined) {
|
|
1155
|
+
firstStage.queryTimezone = sourceDef.queryTimezone;
|
|
1156
|
+
}
|
|
1143
1157
|
switch (firstStage.type) {
|
|
1144
1158
|
case 'reduce':
|
|
1145
1159
|
return new QueryQueryReduce(flatTurtleDef, parent, stageWriter);
|
|
@@ -1585,39 +1599,22 @@ class QueryQuery extends QueryField {
|
|
|
1585
1599
|
break;
|
|
1586
1600
|
case 'timestamp': {
|
|
1587
1601
|
const timeframe = fi.f.fieldDef.timeframe;
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
case 'hour':
|
|
1605
|
-
fields.push({
|
|
1606
|
-
name,
|
|
1607
|
-
type: 'timestamp',
|
|
1608
|
-
timeframe,
|
|
1609
|
-
resultMetadata,
|
|
1610
|
-
location,
|
|
1611
|
-
});
|
|
1612
|
-
break;
|
|
1613
|
-
default:
|
|
1614
|
-
fields.push({
|
|
1615
|
-
name,
|
|
1616
|
-
type: 'timestamp',
|
|
1617
|
-
resultMetadata,
|
|
1618
|
-
location,
|
|
1619
|
-
});
|
|
1620
|
-
break;
|
|
1602
|
+
if (timeframe) {
|
|
1603
|
+
fields.push({
|
|
1604
|
+
name,
|
|
1605
|
+
type: 'timestamp',
|
|
1606
|
+
timeframe,
|
|
1607
|
+
resultMetadata,
|
|
1608
|
+
location,
|
|
1609
|
+
});
|
|
1610
|
+
}
|
|
1611
|
+
else {
|
|
1612
|
+
fields.push({
|
|
1613
|
+
name,
|
|
1614
|
+
type: 'timestamp',
|
|
1615
|
+
resultMetadata,
|
|
1616
|
+
location,
|
|
1617
|
+
});
|
|
1621
1618
|
}
|
|
1622
1619
|
break;
|
|
1623
1620
|
}
|
|
@@ -1649,7 +1646,7 @@ class QueryQuery extends QueryField {
|
|
|
1649
1646
|
}
|
|
1650
1647
|
}
|
|
1651
1648
|
}
|
|
1652
|
-
|
|
1649
|
+
const outputStruct = {
|
|
1653
1650
|
fields,
|
|
1654
1651
|
name: this.resultStage || 'result',
|
|
1655
1652
|
dialect: this.parent.dialect.name,
|
|
@@ -1662,6 +1659,10 @@ class QueryQuery extends QueryField {
|
|
|
1662
1659
|
resultMetadata: this.getResultMetadata(this.rootResult),
|
|
1663
1660
|
type: 'struct',
|
|
1664
1661
|
};
|
|
1662
|
+
if ((0, malloy_types_1.isQuerySegment)(this.firstSegment) && this.firstSegment.queryTimezone) {
|
|
1663
|
+
outputStruct.queryTimezone = this.firstSegment.queryTimezone;
|
|
1664
|
+
}
|
|
1665
|
+
return outputStruct;
|
|
1665
1666
|
}
|
|
1666
1667
|
generateSQLJoinBlock(stageWriter, ji) {
|
|
1667
1668
|
let s = '';
|
|
@@ -196,8 +196,7 @@ export interface TimeLiteralFragment extends DialectFragmentBase {
|
|
|
196
196
|
function: 'timeLiteral';
|
|
197
197
|
literal: string;
|
|
198
198
|
literalType: TimeFieldType;
|
|
199
|
-
timezone
|
|
200
|
-
tzIsLocale?: boolean;
|
|
199
|
+
timezone?: string;
|
|
201
200
|
}
|
|
202
201
|
export interface StringLiteralFragment extends DialectFragmentBase {
|
|
203
202
|
function: 'stringLiteral';
|
|
@@ -378,6 +377,7 @@ export interface QuerySegment extends Filtered {
|
|
|
378
377
|
limit?: number;
|
|
379
378
|
by?: By;
|
|
380
379
|
orderBy?: OrderBy[];
|
|
380
|
+
queryTimezone?: string;
|
|
381
381
|
}
|
|
382
382
|
export interface TurtleDef extends NamedObject, Pipeline {
|
|
383
383
|
type: 'turtle';
|
|
@@ -449,6 +449,7 @@ export interface StructDef extends NamedObject, ResultStructMetadata, Filtered {
|
|
|
449
449
|
fields: FieldDef[];
|
|
450
450
|
primaryKey?: PrimaryKeyRef;
|
|
451
451
|
parameters?: Record<string, Parameter>;
|
|
452
|
+
queryTimezone?: string;
|
|
452
453
|
dialect: string;
|
|
453
454
|
}
|
|
454
455
|
export interface SQLBlockStructDef extends StructDef {
|