@malloydata/malloy 0.0.24-dev230224184907 → 0.0.24-dev230228224434
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/connection_utils.js +10 -10
- package/dist/dialect/dialect.d.ts +2 -2
- package/dist/dialect/dialect.js +13 -13
- package/dist/dialect/dialect_map.d.ts +1 -1
- package/dist/dialect/duckdb.d.ts +3 -3
- package/dist/dialect/duckdb.js +44 -44
- package/dist/dialect/index.d.ts +6 -6
- package/dist/dialect/postgres.d.ts +3 -3
- package/dist/dialect/postgres.js +46 -46
- package/dist/dialect/standardsql.d.ts +4 -4
- package/dist/dialect/standardsql.js +51 -51
- package/dist/index.d.ts +10 -10
- package/dist/lang/ast/ast-utils.d.ts +1 -1
- package/dist/lang/ast/ast-utils.js +3 -3
- package/dist/lang/ast/elements/define-query.d.ts +3 -3
- package/dist/lang/ast/elements/define-query.js +6 -6
- package/dist/lang/ast/elements/define-source.d.ts +4 -4
- package/dist/lang/ast/elements/define-source.js +8 -8
- package/dist/lang/ast/elements/import-statement.d.ts +2 -2
- package/dist/lang/ast/elements/import-statement.js +7 -7
- package/dist/lang/ast/elements/pipeline-desc.d.ts +5 -5
- package/dist/lang/ast/elements/pipeline-desc.js +11 -11
- package/dist/lang/ast/elements/refined-source.d.ts +4 -4
- package/dist/lang/ast/elements/refined-source.js +6 -6
- package/dist/lang/ast/elements/source.d.ts +3 -3
- package/dist/lang/ast/elements/source.js +1 -1
- package/dist/lang/ast/error-factory.d.ts +1 -1
- package/dist/lang/ast/error-factory.js +15 -15
- package/dist/lang/ast/executors/index-executor.d.ts +7 -7
- package/dist/lang/ast/executors/index-executor.js +4 -4
- package/dist/lang/ast/executors/project-executor.d.ts +5 -5
- package/dist/lang/ast/executors/project-executor.js +2 -2
- package/dist/lang/ast/executors/reduce-executor.d.ts +8 -8
- package/dist/lang/ast/executors/reduce-executor.js +5 -5
- package/dist/lang/ast/expressions/apply.d.ts +2 -2
- package/dist/lang/ast/expressions/apply.js +1 -1
- package/dist/lang/ast/expressions/binary-boolean.d.ts +3 -3
- package/dist/lang/ast/expressions/binary-boolean.js +6 -6
- package/dist/lang/ast/expressions/binary-numeric.d.ts +3 -3
- package/dist/lang/ast/expressions/binary-numeric.js +2 -2
- package/dist/lang/ast/expressions/boolean.d.ts +4 -4
- package/dist/lang/ast/expressions/boolean.js +2 -2
- package/dist/lang/ast/expressions/constant-sub-expression.d.ts +4 -4
- package/dist/lang/ast/expressions/constant-sub-expression.js +12 -12
- package/dist/lang/ast/expressions/expr-add-sub.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-add-sub.js +1 -1
- package/dist/lang/ast/expressions/expr-aggregate-function.d.ts +5 -5
- package/dist/lang/ast/expressions/expr-aggregate-function.js +15 -15
- package/dist/lang/ast/expressions/expr-alternation-tree.d.ts +6 -6
- package/dist/lang/ast/expressions/expr-alternation-tree.js +6 -6
- package/dist/lang/ast/expressions/expr-asymmetric.d.ts +5 -5
- package/dist/lang/ast/expressions/expr-asymmetric.js +3 -3
- package/dist/lang/ast/expressions/expr-avg.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-avg.js +2 -2
- package/dist/lang/ast/expressions/expr-cast.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-cast.js +6 -6
- package/dist/lang/ast/expressions/expr-compare.d.ts +5 -5
- package/dist/lang/ast/expressions/expr-compare.js +9 -9
- package/dist/lang/ast/expressions/expr-count-distinct.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-count-distinct.js +1 -1
- package/dist/lang/ast/expressions/expr-count.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-count.js +10 -10
- package/dist/lang/ast/expressions/expr-filter.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-filter.js +13 -13
- package/dist/lang/ast/expressions/expr-func.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-func.js +8 -8
- package/dist/lang/ast/expressions/expr-granular-time.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-granular-time.js +17 -17
- package/dist/lang/ast/expressions/expr-id-reference.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-id-reference.js +11 -11
- package/dist/lang/ast/expressions/expr-logical-op.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-logical-op.js +1 -1
- package/dist/lang/ast/expressions/expr-max.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-max.js +1 -1
- package/dist/lang/ast/expressions/expr-min.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-min.js +1 -1
- package/dist/lang/ast/expressions/expr-minus.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-minus.js +5 -5
- package/dist/lang/ast/expressions/expr-mul-div.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-mul-div.js +1 -1
- package/dist/lang/ast/expressions/expr-not.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-not.js +3 -3
- package/dist/lang/ast/expressions/expr-now.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-now.js +8 -8
- package/dist/lang/ast/expressions/expr-null.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-null.js +4 -4
- package/dist/lang/ast/expressions/expr-number.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-number.js +2 -2
- package/dist/lang/ast/expressions/expr-parens.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-parens.js +3 -3
- package/dist/lang/ast/expressions/expr-regex.d.ts +2 -2
- package/dist/lang/ast/expressions/expr-regex.js +4 -4
- package/dist/lang/ast/expressions/expr-string.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-string.js +8 -8
- package/dist/lang/ast/expressions/expr-sum.d.ts +3 -3
- package/dist/lang/ast/expressions/expr-sum.js +2 -2
- package/dist/lang/ast/expressions/expr-time-extract.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-time-extract.js +30 -30
- package/dist/lang/ast/expressions/expr-time.d.ts +4 -4
- package/dist/lang/ast/expressions/expr-time.js +10 -10
- package/dist/lang/ast/expressions/expr-ungroup.d.ts +6 -6
- package/dist/lang/ast/expressions/expr-ungroup.js +13 -13
- package/dist/lang/ast/expressions/for-range.d.ts +4 -4
- package/dist/lang/ast/expressions/for-range.js +17 -17
- package/dist/lang/ast/expressions/granular-literal.d.ts +4 -4
- package/dist/lang/ast/expressions/granular-literal.js +34 -34
- package/dist/lang/ast/expressions/partial-compare.d.ts +4 -4
- package/dist/lang/ast/expressions/partial-compare.js +4 -4
- package/dist/lang/ast/expressions/pick-when.d.ts +4 -4
- package/dist/lang/ast/expressions/pick-when.js +32 -32
- package/dist/lang/ast/expressions/range.d.ts +3 -3
- package/dist/lang/ast/expressions/range.js +20 -20
- package/dist/lang/ast/expressions/time-frame.d.ts +2 -2
- package/dist/lang/ast/expressions/time-frame.js +3 -3
- package/dist/lang/ast/expressions/top-by.d.ts +4 -4
- package/dist/lang/ast/expressions/top-by.js +5 -5
- package/dist/lang/ast/expressions/unary.d.ts +1 -1
- package/dist/lang/ast/expressions/unary.js +1 -1
- package/dist/lang/ast/expressions/utils.d.ts +2 -2
- package/dist/lang/ast/expressions/utils.js +6 -6
- package/dist/lang/ast/field-space/column-space-field.d.ts +3 -3
- package/dist/lang/ast/field-space/dynamic-space.d.ts +7 -7
- package/dist/lang/ast/field-space/dynamic-space.js +2 -2
- package/dist/lang/ast/field-space/field-definition-value.d.ts +5 -5
- package/dist/lang/ast/field-space/index-field-space.d.ts +3 -3
- package/dist/lang/ast/field-space/index-field-space.js +4 -4
- package/dist/lang/ast/field-space/join-space-field.d.ts +3 -3
- package/dist/lang/ast/field-space/project-field-space.d.ts +2 -2
- package/dist/lang/ast/field-space/project-field-space.js +5 -5
- package/dist/lang/ast/field-space/query-field-struct.d.ts +3 -3
- package/dist/lang/ast/field-space/query-field-struct.js +1 -1
- package/dist/lang/ast/field-space/query-space-field.d.ts +4 -4
- package/dist/lang/ast/field-space/query-space-field.js +1 -1
- package/dist/lang/ast/field-space/query-spaces.d.ts +11 -11
- package/dist/lang/ast/field-space/query-spaces.js +8 -8
- package/dist/lang/ast/field-space/reference-field.d.ts +5 -5
- package/dist/lang/ast/field-space/reference-field.js +1 -1
- package/dist/lang/ast/field-space/refined-space.d.ts +4 -4
- package/dist/lang/ast/field-space/refined-space.js +2 -2
- package/dist/lang/ast/field-space/rename-space-field.d.ts +3 -3
- package/dist/lang/ast/field-space/rename-space-field.js +2 -2
- package/dist/lang/ast/field-space/static-space.d.ts +7 -7
- package/dist/lang/ast/field-space/static-space.js +12 -12
- package/dist/lang/ast/field-space/struct-space-field-base.d.ts +4 -4
- package/dist/lang/ast/field-space/struct-space-field-base.js +1 -1
- package/dist/lang/ast/field-space/wild-space-field.d.ts +4 -4
- package/dist/lang/ast/field-space/wild-space-field.js +1 -1
- package/dist/lang/ast/fragtype-utils.d.ts +1 -1
- package/dist/lang/ast/fragtype-utils.js +15 -15
- package/dist/lang/ast/index.d.ts +90 -90
- package/dist/lang/ast/parameters/constant-parameter.d.ts +3 -3
- package/dist/lang/ast/parameters/constant-parameter.js +10 -10
- package/dist/lang/ast/parameters/has-parameter.d.ts +3 -3
- package/dist/lang/ast/parameters/has-parameter.js +7 -7
- package/dist/lang/ast/query-elements/anonymous-query.d.ts +3 -3
- package/dist/lang/ast/query-elements/anonymous-query.js +2 -2
- package/dist/lang/ast/query-elements/existing-query.d.ts +4 -4
- package/dist/lang/ast/query-elements/existing-query.js +10 -10
- package/dist/lang/ast/query-elements/full-query.d.ts +4 -4
- package/dist/lang/ast/query-elements/full-query.js +10 -10
- package/dist/lang/ast/query-elements/query-head-struct.d.ts +2 -2
- package/dist/lang/ast/query-elements/query-head-struct.js +2 -2
- package/dist/lang/ast/query-items/field-declaration.d.ts +6 -6
- package/dist/lang/ast/query-items/field-declaration.js +14 -14
- package/dist/lang/ast/query-items/field-references.d.ts +6 -6
- package/dist/lang/ast/query-items/field-references.js +9 -9
- package/dist/lang/ast/query-properties/aggregate.d.ts +2 -2
- package/dist/lang/ast/query-properties/aggregate.js +1 -1
- package/dist/lang/ast/query-properties/declare-fields.d.ts +2 -2
- package/dist/lang/ast/query-properties/declare-fields.js +1 -1
- package/dist/lang/ast/query-properties/dimmensions.d.ts +2 -2
- package/dist/lang/ast/query-properties/dimmensions.js +1 -1
- package/dist/lang/ast/query-properties/filters.d.ts +4 -4
- package/dist/lang/ast/query-properties/filters.js +16 -16
- package/dist/lang/ast/query-properties/group-by.d.ts +2 -2
- package/dist/lang/ast/query-properties/group-by.js +1 -1
- package/dist/lang/ast/query-properties/indexing.d.ts +3 -3
- package/dist/lang/ast/query-properties/indexing.js +3 -3
- package/dist/lang/ast/query-properties/joins.d.ts +6 -6
- package/dist/lang/ast/query-properties/joins.js +27 -27
- package/dist/lang/ast/query-properties/limit.d.ts +1 -1
- package/dist/lang/ast/query-properties/limit.js +1 -1
- package/dist/lang/ast/query-properties/measures.d.ts +2 -2
- package/dist/lang/ast/query-properties/measures.js +1 -1
- package/dist/lang/ast/query-properties/nest-reference.d.ts +2 -2
- package/dist/lang/ast/query-properties/nest-reference.js +1 -1
- package/dist/lang/ast/query-properties/nest.d.ts +7 -7
- package/dist/lang/ast/query-properties/nest.js +7 -7
- package/dist/lang/ast/query-properties/nests.d.ts +2 -2
- package/dist/lang/ast/query-properties/nests.js +1 -1
- package/dist/lang/ast/query-properties/ordering.d.ts +3 -3
- package/dist/lang/ast/query-properties/ordering.js +6 -6
- package/dist/lang/ast/query-properties/project-statement.d.ts +2 -2
- package/dist/lang/ast/query-properties/project-statement.js +1 -1
- package/dist/lang/ast/query-properties/qop-desc.d.ts +7 -7
- package/dist/lang/ast/query-properties/qop-desc.js +20 -20
- package/dist/lang/ast/query-properties/sampling.d.ts +2 -2
- package/dist/lang/ast/query-properties/sampling.js +1 -1
- package/dist/lang/ast/query-properties/top.d.ts +4 -4
- package/dist/lang/ast/query-properties/top.js +5 -5
- package/dist/lang/ast/source-properties/field-list-edit.d.ts +4 -4
- package/dist/lang/ast/source-properties/field-list-edit.js +2 -2
- package/dist/lang/ast/source-properties/primary-key.d.ts +2 -2
- package/dist/lang/ast/source-properties/primary-key.js +2 -2
- package/dist/lang/ast/source-properties/renames.d.ts +2 -2
- package/dist/lang/ast/source-properties/renames.js +3 -3
- package/dist/lang/ast/source-properties/turtles.d.ts +2 -2
- package/dist/lang/ast/source-properties/turtles.js +1 -1
- package/dist/lang/ast/sources/named-source.d.ts +4 -4
- package/dist/lang/ast/sources/named-source.js +5 -5
- package/dist/lang/ast/sources/query-source.d.ts +3 -3
- package/dist/lang/ast/sources/query-source.js +3 -3
- package/dist/lang/ast/sources/sql-source.d.ts +2 -2
- package/dist/lang/ast/sources/sql-source.js +2 -2
- package/dist/lang/ast/sources/table-source.d.ts +2 -2
- package/dist/lang/ast/sources/table-source.js +7 -7
- package/dist/lang/ast/space-seed.d.ts +2 -2
- package/dist/lang/ast/space-seed.js +1 -1
- package/dist/lang/ast/sql-elements/sql-statement.d.ts +4 -4
- package/dist/lang/ast/sql-elements/sql-statement.js +9 -9
- package/dist/lang/ast/sql-elements/sql-string.d.ts +3 -3
- package/dist/lang/ast/sql-elements/sql-string.js +6 -6
- package/dist/lang/ast/struct-utils.d.ts +2 -2
- package/dist/lang/ast/struct-utils.js +3 -3
- package/dist/lang/ast/time-utils.d.ts +6 -6
- package/dist/lang/ast/time-utils.js +46 -46
- package/dist/lang/ast/types/document-compile-result.d.ts +2 -2
- package/dist/lang/ast/types/executor.d.ts +3 -3
- package/dist/lang/ast/types/explore-desc.d.ts +2 -2
- package/dist/lang/ast/types/explore-desc.js +1 -1
- package/dist/lang/ast/types/explore-field.d.ts +3 -3
- package/dist/lang/ast/types/expr-result.d.ts +2 -2
- package/dist/lang/ast/types/expr-value.d.ts +2 -2
- package/dist/lang/ast/types/expression-def.d.ts +4 -4
- package/dist/lang/ast/types/expression-def.js +86 -86
- package/dist/lang/ast/types/field-collection-member.d.ts +3 -3
- package/dist/lang/ast/types/field-decl.d.ts +5 -5
- package/dist/lang/ast/types/field-space.d.ts +5 -5
- package/dist/lang/ast/types/field-space.js +1 -1
- package/dist/lang/ast/types/granular-result.d.ts +3 -3
- package/dist/lang/ast/types/granular-result.js +1 -1
- package/dist/lang/ast/types/lookup-result.d.ts +1 -1
- package/dist/lang/ast/types/malloy-element.d.ts +6 -6
- package/dist/lang/ast/types/malloy-element.js +49 -49
- package/dist/lang/ast/types/model-entry.d.ts +1 -1
- package/dist/lang/ast/types/name-space.d.ts +1 -1
- package/dist/lang/ast/types/nested-query.d.ts +2 -2
- package/dist/lang/ast/types/op-desc.d.ts +2 -2
- package/dist/lang/ast/types/query-comp.d.ts +1 -1
- package/dist/lang/ast/types/query-element.d.ts +3 -3
- package/dist/lang/ast/types/query-item.d.ts +4 -4
- package/dist/lang/ast/types/query-property.d.ts +15 -15
- package/dist/lang/ast/types/source-property.d.ts +8 -8
- package/dist/lang/ast/types/space-entry.d.ts +2 -2
- package/dist/lang/ast/types/space-field.d.ts +4 -4
- package/dist/lang/ast/types/space-field.js +4 -4
- package/dist/lang/ast/types/space-param.d.ts +4 -4
- package/dist/lang/ast/types/space-param.js +4 -4
- package/dist/lang/ast/types/time-result.d.ts +2 -2
- package/dist/lang/ast/types/turtle-headed-pipe.d.ts +2 -2
- package/dist/lang/ast/types/turtle-headed-pipe.js +1 -1
- package/dist/lang/ast/types/type-desc.d.ts +3 -3
- package/dist/lang/field-utils.d.ts +1 -1
- package/dist/lang/field-utils.js +4 -4
- package/dist/lang/index.d.ts +10 -10
- package/dist/lang/malloy-to-ast.d.ts +8 -8
- package/dist/lang/malloy-to-ast.js +68 -68
- package/dist/lang/parse-error-handler.d.ts +3 -3
- package/dist/lang/parse-error-handler.js +4 -4
- package/dist/lang/parse-log.d.ts +2 -2
- package/dist/lang/parse-log.js +1 -1
- package/dist/lang/parse-malloy.d.ts +7 -7
- package/dist/lang/parse-malloy.js +81 -81
- package/dist/lang/parse-tree-walkers/document-completion-walker.d.ts +2 -2
- package/dist/lang/parse-tree-walkers/document-completion-walker.js +38 -38
- package/dist/lang/parse-tree-walkers/document-help-context-walker.d.ts +1 -1
- package/dist/lang/parse-tree-walkers/document-help-context-walker.js +14 -14
- package/dist/lang/parse-tree-walkers/document-highlight-walker.d.ts +3 -3
- package/dist/lang/parse-tree-walkers/document-highlight-walker.js +85 -85
- package/dist/lang/parse-tree-walkers/document-symbol-walker.d.ts +4 -4
- package/dist/lang/parse-tree-walkers/document-symbol-walker.js +40 -40
- package/dist/lang/parse-tree-walkers/explore-query-walker.d.ts +3 -3
- package/dist/lang/parse-tree-walkers/explore-query-walker.js +3 -3
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +4 -4
- package/dist/lang/parse-tree-walkers/find-external-references.js +2 -2
- package/dist/lang/reference-list.d.ts +1 -1
- package/dist/lang/test/document-help-context-walker.spec.js +9 -9
- package/dist/lang/test/document-symbol-walker.spec.js +32 -32
- package/dist/lang/test/field-symbols.spec.js +87 -87
- package/dist/lang/test/parse.spec.d.ts +1 -1
- package/dist/lang/test/parse.spec.js +768 -768
- package/dist/lang/test/test-translator.d.ts +6 -6
- package/dist/lang/test/test-translator.js +159 -159
- package/dist/lang/translate-response.d.ts +7 -7
- package/dist/lang/translate-response.js +1 -1
- package/dist/lang/zone.d.ts +5 -5
- package/dist/lang/zone.js +8 -8
- package/dist/malloy.d.ts +7 -7
- package/dist/malloy.js +192 -192
- package/dist/model/index.d.ts +2 -2
- package/dist/model/malloy_query.d.ts +12 -12
- package/dist/model/malloy_query.js +497 -502
- package/dist/model/malloy_types.d.ts +68 -68
- package/dist/model/malloy_types.js +102 -102
- package/dist/model/sql_block.d.ts +1 -1
- package/dist/model/sql_block.js +4 -4
- package/dist/model/utils.d.ts +1 -1
- package/dist/model/utils.js +10 -10
- package/dist/runtime_types.d.ts +2 -2
- package/package.json +4 -1
|
@@ -40,7 +40,7 @@ class StageWriter {
|
|
|
40
40
|
this.withs = [];
|
|
41
41
|
this.udfs = [];
|
|
42
42
|
this.pdts = [];
|
|
43
|
-
this.stagePrefix =
|
|
43
|
+
this.stagePrefix = '__stage';
|
|
44
44
|
this.parent = parent;
|
|
45
45
|
this.useCTE = useCTE;
|
|
46
46
|
}
|
|
@@ -69,7 +69,7 @@ class StageWriter {
|
|
|
69
69
|
// eslint-disable-next-line prefer-const
|
|
70
70
|
let { sql, lastStageName } = stageWriter.combineStages(true);
|
|
71
71
|
if (lastStageName === undefined) {
|
|
72
|
-
throw new Error(
|
|
72
|
+
throw new Error('Internal Error: no stage to combine');
|
|
73
73
|
}
|
|
74
74
|
sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, structDef);
|
|
75
75
|
const id = `${dialect.udfPrefix}${this.root().udfs.length}`;
|
|
@@ -79,7 +79,7 @@ class StageWriter {
|
|
|
79
79
|
}
|
|
80
80
|
addPDT(baseName, dialect) {
|
|
81
81
|
const sql = this.combineStages(false).sql + this.withs[this.withs.length - 1];
|
|
82
|
-
const tableName =
|
|
82
|
+
const tableName = 'scratch.' + baseName + (0, utils_1.generateHash)(sql);
|
|
83
83
|
this.root().pdts.push(dialect.sqlCreateTableAsSelect(tableName, sql));
|
|
84
84
|
return tableName;
|
|
85
85
|
}
|
|
@@ -87,11 +87,11 @@ class StageWriter {
|
|
|
87
87
|
// return SQL and the last stage name
|
|
88
88
|
combineStages(includeLastStage) {
|
|
89
89
|
if (!this.useCTE) {
|
|
90
|
-
return {
|
|
90
|
+
return { sql: this.withs[0], lastStageName: this.withs[0] };
|
|
91
91
|
}
|
|
92
92
|
let lastStageName = this.getName(0);
|
|
93
|
-
let prefix =
|
|
94
|
-
let w =
|
|
93
|
+
let prefix = 'WITH ';
|
|
94
|
+
let w = '';
|
|
95
95
|
for (let i = 0; i < this.withs.length - (includeLastStage ? 0 : 1); i++) {
|
|
96
96
|
const sql = this.withs[i];
|
|
97
97
|
lastStageName = this.getName(i);
|
|
@@ -99,18 +99,18 @@ class StageWriter {
|
|
|
99
99
|
throw new Error(`Expected sql WITH to be present for stage ${lastStageName}.`);
|
|
100
100
|
}
|
|
101
101
|
w += `${prefix}${lastStageName} AS (\n${(0, utils_1.indent)(sql)})\n`;
|
|
102
|
-
prefix =
|
|
102
|
+
prefix = ', ';
|
|
103
103
|
}
|
|
104
|
-
return {
|
|
104
|
+
return { sql: w, lastStageName };
|
|
105
105
|
}
|
|
106
106
|
/** emit the SQL for all the stages. */
|
|
107
107
|
generateSQLStages() {
|
|
108
108
|
const lastStageNum = this.withs.length - 1;
|
|
109
109
|
if (lastStageNum < 0) {
|
|
110
|
-
throw new Error(
|
|
110
|
+
throw new Error('No SQL generated');
|
|
111
111
|
}
|
|
112
|
-
const udfs = this.udfs.join(
|
|
113
|
-
const pdts = this.pdts.join(
|
|
112
|
+
const udfs = this.udfs.join('\n');
|
|
113
|
+
const pdts = this.pdts.join('\n');
|
|
114
114
|
const sql = this.combineStages(false).sql;
|
|
115
115
|
return udfs + pdts + sql + this.withs[lastStageNum];
|
|
116
116
|
}
|
|
@@ -180,12 +180,12 @@ class QueryField extends QueryNode {
|
|
|
180
180
|
getJoinableParent() {
|
|
181
181
|
// if it is inline it should always have a parent
|
|
182
182
|
const parent = this.parent;
|
|
183
|
-
if (parent.fieldDef.structRelationship.type ===
|
|
183
|
+
if (parent.fieldDef.structRelationship.type === 'inline') {
|
|
184
184
|
if (parent) {
|
|
185
185
|
return parent.getJoinableParent();
|
|
186
186
|
}
|
|
187
187
|
else {
|
|
188
|
-
throw new Error(
|
|
188
|
+
throw new Error('Internal Error: inline struct cannot be root');
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
return parent;
|
|
@@ -197,7 +197,7 @@ class QueryField extends QueryNode {
|
|
|
197
197
|
else {
|
|
198
198
|
const exp = groupSets.length === 1
|
|
199
199
|
? `=${groupSets[0]}`
|
|
200
|
-
: ` IN (${groupSets.join(
|
|
200
|
+
: ` IN (${groupSets.join(',')})`;
|
|
201
201
|
return `CASE WHEN group_set${exp} THEN\n ${s}\n END`;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
@@ -248,12 +248,12 @@ class QueryField extends QueryNode {
|
|
|
248
248
|
}
|
|
249
249
|
generateUngroupedFragment(resultSet, context, expr, state) {
|
|
250
250
|
if (state.totalGroupSet !== -1) {
|
|
251
|
-
throw new Error(
|
|
251
|
+
throw new Error('Already in ALL. Cannot nest within an all calcuation.');
|
|
252
252
|
}
|
|
253
253
|
let totalGroupSet;
|
|
254
254
|
let ungroupSet;
|
|
255
255
|
if (expr.fields && expr.fields.length > 0) {
|
|
256
|
-
const key = expr.fields.sort().join(
|
|
256
|
+
const key = expr.fields.sort().join('|') + expr.type;
|
|
257
257
|
ungroupSet = resultSet.ungroupedSets.get(key);
|
|
258
258
|
if (ungroupSet === undefined) {
|
|
259
259
|
throw new Error(`Internal Error, cannot find groupset with key ${key}`);
|
|
@@ -265,8 +265,8 @@ class QueryField extends QueryNode {
|
|
|
265
265
|
}
|
|
266
266
|
const s = this.generateExpressionFromExpr(resultSet, context, expr.e, state.withTotal(totalGroupSet));
|
|
267
267
|
const fields = resultSet.getUngroupPartitions(ungroupSet);
|
|
268
|
-
let partitionBy =
|
|
269
|
-
const fieldsString = fields.map(
|
|
268
|
+
let partitionBy = '';
|
|
269
|
+
const fieldsString = fields.map(f => f.getPartitionSQL()).join(', ');
|
|
270
270
|
if (fieldsString.length > 0) {
|
|
271
271
|
partitionBy = `PARTITION BY ${fieldsString}`;
|
|
272
272
|
}
|
|
@@ -303,9 +303,9 @@ class QueryField extends QueryNode {
|
|
|
303
303
|
}
|
|
304
304
|
generateSymmetricFragment(resultSet, context, expr, state) {
|
|
305
305
|
const dimSQL = this.generateDimFragment(resultSet, context, expr, state);
|
|
306
|
-
const f = expr.function ===
|
|
307
|
-
?
|
|
308
|
-
: expr.function +
|
|
306
|
+
const f = expr.function === 'count_distinct'
|
|
307
|
+
? 'count(distinct '
|
|
308
|
+
: expr.function + '(';
|
|
309
309
|
return `${f}${dimSQL})`;
|
|
310
310
|
}
|
|
311
311
|
generateAvgFragment(resultSet, context, expr, state) {
|
|
@@ -331,11 +331,11 @@ class QueryField extends QueryNode {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
generateCountFragment(resultSet, context, expr, state) {
|
|
334
|
-
let func =
|
|
335
|
-
let thing =
|
|
334
|
+
let func = 'COUNT(';
|
|
335
|
+
let thing = '1';
|
|
336
336
|
const distinctKeySQL = this.generateDistinctKeyIfNecessary(resultSet, context, expr.structPath);
|
|
337
337
|
if (distinctKeySQL) {
|
|
338
|
-
func =
|
|
338
|
+
func = 'COUNT(DISTINCT';
|
|
339
339
|
thing = distinctKeySQL;
|
|
340
340
|
}
|
|
341
341
|
// find the structDef and return the path to the field...
|
|
@@ -351,46 +351,46 @@ class QueryField extends QueryNode {
|
|
|
351
351
|
}
|
|
352
352
|
generateAnalyticFragment(resultStruct, context, expr, state) {
|
|
353
353
|
const fields = resultStruct.getUngroupPartitions(undefined);
|
|
354
|
-
let partitionBy =
|
|
355
|
-
const fieldsString = fields.map(
|
|
354
|
+
let partitionBy = '';
|
|
355
|
+
const fieldsString = fields.map(f => f.getPartitionSQL()).join(', ');
|
|
356
356
|
if (fieldsString.length > 0) {
|
|
357
357
|
partitionBy = `PARTITION BY ${fieldsString}`;
|
|
358
358
|
}
|
|
359
|
-
let orderBy =
|
|
359
|
+
let orderBy = '';
|
|
360
360
|
// calculate the ordering.
|
|
361
361
|
const obSQL = [];
|
|
362
362
|
let orderingField;
|
|
363
363
|
const orderByDef = resultStruct.firstSegment.orderBy ||
|
|
364
364
|
resultStruct.calculateDefaultOrderBy();
|
|
365
365
|
for (const ordering of orderByDef) {
|
|
366
|
-
if (typeof ordering.field ===
|
|
366
|
+
if (typeof ordering.field === 'string') {
|
|
367
367
|
orderingField = {
|
|
368
|
-
|
|
369
|
-
|
|
368
|
+
name: ordering.field,
|
|
369
|
+
fif: resultStruct.getField(ordering.field),
|
|
370
370
|
};
|
|
371
371
|
}
|
|
372
372
|
else {
|
|
373
373
|
orderingField = resultStruct.getFieldByNumber(ordering.field);
|
|
374
374
|
}
|
|
375
|
-
if (resultStruct.firstSegment.type ===
|
|
375
|
+
if (resultStruct.firstSegment.type === 'reduce') {
|
|
376
376
|
obSQL.push(` ${orderingField.fif.getSQL()}` +
|
|
377
377
|
// this.parent.dialect.sqlMaybeQuoteIdentifier(
|
|
378
378
|
// `${orderingField.name}__${resultStruct.groupSet}`
|
|
379
379
|
// ) +
|
|
380
|
-
` ${ordering.dir ||
|
|
380
|
+
` ${ordering.dir || 'ASC'}`);
|
|
381
381
|
}
|
|
382
|
-
else if (resultStruct.firstSegment.type ===
|
|
383
|
-
obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir ||
|
|
382
|
+
else if (resultStruct.firstSegment.type === 'project') {
|
|
383
|
+
obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir || 'ASC'}`);
|
|
384
384
|
}
|
|
385
385
|
}
|
|
386
386
|
const func = malloy_types_1.malloyFunctions[expr.function];
|
|
387
387
|
const paramSQL = [];
|
|
388
388
|
if (expr.parameters !== undefined) {
|
|
389
389
|
for (const e of expr.parameters) {
|
|
390
|
-
if (typeof e ===
|
|
390
|
+
if (typeof e === 'string') {
|
|
391
391
|
paramSQL.push(e); // need to map to dimensional expression.
|
|
392
392
|
}
|
|
393
|
-
else if (typeof e ===
|
|
393
|
+
else if (typeof e === 'number') {
|
|
394
394
|
paramSQL.push(e.toString());
|
|
395
395
|
}
|
|
396
396
|
else {
|
|
@@ -399,15 +399,15 @@ class QueryField extends QueryNode {
|
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
401
|
if (obSQL.length > 0) {
|
|
402
|
-
orderBy =
|
|
402
|
+
orderBy = ' ' + this.parent.dialect.sqlOrderBy(obSQL);
|
|
403
403
|
}
|
|
404
404
|
const sqlName = func.sqlName || expr.function;
|
|
405
|
-
return `${sqlName}(${paramSQL.join(
|
|
405
|
+
return `${sqlName}(${paramSQL.join(', ')}) OVER(${partitionBy} ${orderBy} )`;
|
|
406
406
|
}
|
|
407
407
|
generateExpressionFromExpr(resultSet, context, e, state = new GenerateState()) {
|
|
408
|
-
let s =
|
|
408
|
+
let s = '';
|
|
409
409
|
for (const expr of e) {
|
|
410
|
-
if (typeof expr ===
|
|
410
|
+
if (typeof expr === 'string') {
|
|
411
411
|
s += expr;
|
|
412
412
|
}
|
|
413
413
|
else if ((0, malloy_types_1.isFieldFragment)(expr)) {
|
|
@@ -427,16 +427,16 @@ class QueryField extends QueryNode {
|
|
|
427
427
|
}
|
|
428
428
|
else if ((0, malloy_types_1.isAggregateFragment)(expr)) {
|
|
429
429
|
let agg;
|
|
430
|
-
if (expr.function ===
|
|
430
|
+
if (expr.function === 'sum') {
|
|
431
431
|
agg = this.generateSumFragment(resultSet, context, expr, state);
|
|
432
432
|
}
|
|
433
|
-
else if (expr.function ===
|
|
433
|
+
else if (expr.function === 'avg') {
|
|
434
434
|
agg = this.generateAvgFragment(resultSet, context, expr, state);
|
|
435
435
|
}
|
|
436
|
-
else if (expr.function ===
|
|
436
|
+
else if (expr.function === 'count') {
|
|
437
437
|
agg = this.generateCountFragment(resultSet, context, expr, state);
|
|
438
438
|
}
|
|
439
|
-
else if ([
|
|
439
|
+
else if (['count_distinct', 'min', 'max'].includes(expr.function)) {
|
|
440
440
|
agg = this.generateSymmetricFragment(resultSet, context, expr, state);
|
|
441
441
|
}
|
|
442
442
|
else {
|
|
@@ -462,10 +462,10 @@ class QueryField extends QueryNode {
|
|
|
462
462
|
s += state.applyValue;
|
|
463
463
|
}
|
|
464
464
|
else {
|
|
465
|
-
throw new Error(
|
|
465
|
+
throw new Error('Internal Error: Partial application value referenced but not provided');
|
|
466
466
|
}
|
|
467
467
|
}
|
|
468
|
-
else if (expr.type
|
|
468
|
+
else if (expr.type === 'dialect') {
|
|
469
469
|
s += this.generateDialect(resultSet, context, expr, state);
|
|
470
470
|
}
|
|
471
471
|
else {
|
|
@@ -479,11 +479,11 @@ class QueryField extends QueryNode {
|
|
|
479
479
|
return this.fieldDef.e;
|
|
480
480
|
}
|
|
481
481
|
return [
|
|
482
|
-
this.parent.dialect.sqlFieldReference(this.parent.getSQLIdentifier(), this.fieldDef.name, this.fieldDef.type, this.parent.fieldDef.structSource.type ===
|
|
483
|
-
this.parent.fieldDef.structSource.type ===
|
|
484
|
-
(this.parent.fieldDef.structSource.type ===
|
|
485
|
-
this.parent.fieldDef.structSource.method ===
|
|
486
|
-
this.parent.fieldDef.structRelationship.isArray)
|
|
482
|
+
this.parent.dialect.sqlFieldReference(this.parent.getSQLIdentifier(), this.fieldDef.name, this.fieldDef.type, this.parent.fieldDef.structSource.type === 'nested' ||
|
|
483
|
+
this.parent.fieldDef.structSource.type === 'inline' ||
|
|
484
|
+
(this.parent.fieldDef.structSource.type === 'sql' &&
|
|
485
|
+
this.parent.fieldDef.structSource.method === 'nested'), this.parent.fieldDef.structRelationship.type === 'nested' &&
|
|
486
|
+
this.parent.fieldDef.structRelationship.isArray),
|
|
487
487
|
];
|
|
488
488
|
}
|
|
489
489
|
generateExpression(resultSet) {
|
|
@@ -536,17 +536,17 @@ class QueryFieldStruct extends QueryAtomicField {
|
|
|
536
536
|
getAsJoinedStructDef(foreignKeyName) {
|
|
537
537
|
return {
|
|
538
538
|
...this.parent.fieldDef,
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
539
|
+
structRelationship: {
|
|
540
|
+
type: 'one',
|
|
541
|
+
onExpression: [
|
|
542
542
|
{
|
|
543
|
-
|
|
544
|
-
|
|
543
|
+
type: 'field',
|
|
544
|
+
path: this.primaryKey,
|
|
545
545
|
},
|
|
546
|
-
|
|
547
|
-
{
|
|
548
|
-
]
|
|
549
|
-
}
|
|
546
|
+
'=',
|
|
547
|
+
{ type: 'field', path: foreignKeyName },
|
|
548
|
+
],
|
|
549
|
+
},
|
|
550
550
|
};
|
|
551
551
|
}
|
|
552
552
|
}
|
|
@@ -557,7 +557,7 @@ class QueryFieldDate extends QueryAtomicField {
|
|
|
557
557
|
return super.generateExpression(resultSet);
|
|
558
558
|
}
|
|
559
559
|
else {
|
|
560
|
-
const truncated = this.parent.dialect.sqlTrunc({
|
|
560
|
+
const truncated = this.parent.dialect.sqlTrunc({ value: this.getExpr(), valueType: 'date' }, fd.timeframe);
|
|
561
561
|
return this.generateExpressionFromExpr(resultSet, this.parent, truncated);
|
|
562
562
|
}
|
|
563
563
|
}
|
|
@@ -565,8 +565,8 @@ class QueryFieldDate extends QueryAtomicField {
|
|
|
565
565
|
getChildByName(name) {
|
|
566
566
|
const fieldDef = {
|
|
567
567
|
...this.fieldDef,
|
|
568
|
-
|
|
569
|
-
|
|
568
|
+
as: `${this.getIdentifier()}_${name}`,
|
|
569
|
+
timeframe: name,
|
|
570
570
|
};
|
|
571
571
|
return new QueryFieldDate(fieldDef, this.parent);
|
|
572
572
|
}
|
|
@@ -576,8 +576,8 @@ class QueryFieldTimestamp extends QueryAtomicField {
|
|
|
576
576
|
getChildByName(name) {
|
|
577
577
|
const fieldDef = {
|
|
578
578
|
...this.fieldDef,
|
|
579
|
-
|
|
580
|
-
|
|
579
|
+
as: `${this.getIdentifier()}_${name}`,
|
|
580
|
+
timeframe: name,
|
|
581
581
|
};
|
|
582
582
|
return new QueryFieldTimestamp(fieldDef, this.parent);
|
|
583
583
|
}
|
|
@@ -589,13 +589,13 @@ class QueryFieldDistinctKey extends QueryAtomicField {
|
|
|
589
589
|
const pk = this.parent.getPrimaryKeyField(this.fieldDef);
|
|
590
590
|
return pk.generateExpression(resultSet);
|
|
591
591
|
}
|
|
592
|
-
else if (this.parent.fieldDef.structSource.type ===
|
|
592
|
+
else if (this.parent.fieldDef.structSource.type === 'nested') {
|
|
593
593
|
const parentKey = (_a = this.parent.parent) === null || _a === void 0 ? void 0 : _a.getDistinctKey().generateExpression(resultSet);
|
|
594
|
-
return `CONCAT(${parentKey}, 'x', ${this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(),
|
|
594
|
+
return `CONCAT(${parentKey}, 'x', ${this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(), '__row_id', 'string', true, false)})`;
|
|
595
595
|
}
|
|
596
596
|
else {
|
|
597
597
|
// return this.parent.getIdentifier() + "." + "__distinct_key";
|
|
598
|
-
return this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(),
|
|
598
|
+
return this.parent.dialect.sqlFieldReference(this.parent.getIdentifier(), '__distinct_key', 'string', this.parent.fieldDef.structRelationship.type === 'nested', false);
|
|
599
599
|
}
|
|
600
600
|
}
|
|
601
601
|
includeInWildcard() {
|
|
@@ -622,7 +622,7 @@ function sqlSumDistinct(dialect, sqlExp, sqlDistintKey) {
|
|
|
622
622
|
}
|
|
623
623
|
class FieldInstanceField {
|
|
624
624
|
constructor(f, fieldUsage, parent) {
|
|
625
|
-
this.type =
|
|
625
|
+
this.type = 'field';
|
|
626
626
|
this.additionalGroupSets = [];
|
|
627
627
|
this.f = f;
|
|
628
628
|
this.fieldUsage = fieldUsage;
|
|
@@ -651,7 +651,7 @@ class FieldInstanceField {
|
|
|
651
651
|
}
|
|
652
652
|
class FieldInstanceResult {
|
|
653
653
|
constructor(turtleDef, parent) {
|
|
654
|
-
this.type =
|
|
654
|
+
this.type = 'query';
|
|
655
655
|
this.allFields = new Map();
|
|
656
656
|
this.groupSet = 0;
|
|
657
657
|
this.depth = 0;
|
|
@@ -667,12 +667,12 @@ class FieldInstanceResult {
|
|
|
667
667
|
addField(as, field, usage) {
|
|
668
668
|
let fi;
|
|
669
669
|
if ((fi = this.allFields.get(as))) {
|
|
670
|
-
if (fi.type ===
|
|
670
|
+
if (fi.type === 'query') {
|
|
671
671
|
throw new Error(`Redefinition of field ${field.fieldDef.name} as struct`);
|
|
672
672
|
}
|
|
673
673
|
const fif = fi;
|
|
674
|
-
if (fif.fieldUsage.type ===
|
|
675
|
-
if (usage.type !==
|
|
674
|
+
if (fif.fieldUsage.type === 'result') {
|
|
675
|
+
if (usage.type !== 'result') {
|
|
676
676
|
// its already in the result, we can just ignore it.
|
|
677
677
|
return;
|
|
678
678
|
}
|
|
@@ -711,9 +711,9 @@ class FieldInstanceResult {
|
|
|
711
711
|
getFieldByNumber(index) {
|
|
712
712
|
for (const [name, fi] of this.allFields) {
|
|
713
713
|
if (fi instanceof FieldInstanceField) {
|
|
714
|
-
if (fi.fieldUsage.type ===
|
|
714
|
+
if (fi.fieldUsage.type === 'result' &&
|
|
715
715
|
fi.fieldUsage.resultIndex === index) {
|
|
716
|
-
return { name,
|
|
716
|
+
return { name, fif: fi };
|
|
717
717
|
}
|
|
718
718
|
}
|
|
719
719
|
}
|
|
@@ -737,10 +737,10 @@ class FieldInstanceResult {
|
|
|
737
737
|
let isComplex = false;
|
|
738
738
|
let children = [this.groupSet];
|
|
739
739
|
for (const [_name, fi] of this.allFields) {
|
|
740
|
-
if (fi.type ===
|
|
740
|
+
if (fi.type === 'query') {
|
|
741
741
|
const fir = fi;
|
|
742
742
|
isComplex = true;
|
|
743
|
-
if (fir.firstSegment.type ===
|
|
743
|
+
if (fir.firstSegment.type === 'reduce') {
|
|
744
744
|
const r = fir.computeGroups(nextGroupSetNumber, depth + 1);
|
|
745
745
|
children = children.concat(r.children);
|
|
746
746
|
nextGroupSetNumber = r.nextGroupSetNumber;
|
|
@@ -779,14 +779,14 @@ class FieldInstanceResult {
|
|
|
779
779
|
// and emit the resulting structure as a RECORD instead of REPEATED
|
|
780
780
|
// if we have all numbers, we need to know because we'll have to conjur a record.
|
|
781
781
|
getRepeatedResultType() {
|
|
782
|
-
let ret =
|
|
782
|
+
let ret = 'inline_all_numbers';
|
|
783
783
|
for (const f of this.fields()) {
|
|
784
|
-
if (f.fieldUsage.type ===
|
|
784
|
+
if (f.fieldUsage.type === 'result') {
|
|
785
785
|
if (isScalarField(f.f)) {
|
|
786
|
-
return
|
|
786
|
+
return 'nested';
|
|
787
787
|
}
|
|
788
788
|
if (f.f instanceof QueryStruct) {
|
|
789
|
-
ret =
|
|
789
|
+
ret = 'inline';
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
792
|
}
|
|
@@ -819,19 +819,19 @@ class FieldInstanceResult {
|
|
|
819
819
|
let firstField;
|
|
820
820
|
for (const [_name, fi] of this.allFields) {
|
|
821
821
|
if (fi instanceof FieldInstanceField) {
|
|
822
|
-
if (fi.fieldUsage.type ===
|
|
822
|
+
if (fi.fieldUsage.type === 'result') {
|
|
823
823
|
firstField || (firstField = fi.fieldUsage.resultIndex);
|
|
824
|
-
if ([
|
|
825
|
-
return [{
|
|
824
|
+
if (['date', 'timestamp'].indexOf(fi.f.fieldDef.type) > -1) {
|
|
825
|
+
return [{ dir: 'desc', field: fi.fieldUsage.resultIndex }];
|
|
826
826
|
}
|
|
827
827
|
else if (isCalculatedField(fi.f)) {
|
|
828
|
-
return [{
|
|
828
|
+
return [{ dir: 'desc', field: fi.fieldUsage.resultIndex }];
|
|
829
829
|
}
|
|
830
830
|
}
|
|
831
831
|
}
|
|
832
832
|
}
|
|
833
833
|
if (firstField) {
|
|
834
|
-
return [{
|
|
834
|
+
return [{ dir: 'asc', field: firstField }];
|
|
835
835
|
}
|
|
836
836
|
return [];
|
|
837
837
|
}
|
|
@@ -863,7 +863,7 @@ class FieldInstanceResult {
|
|
|
863
863
|
joinStack.indexOf(name) === -1) {
|
|
864
864
|
query.addDependantExpr(this, qs.parent, sr.onExpression, [
|
|
865
865
|
...joinStack,
|
|
866
|
-
name
|
|
866
|
+
name,
|
|
867
867
|
]);
|
|
868
868
|
}
|
|
869
869
|
if (!(join = this.root().joins.get(name))) {
|
|
@@ -884,7 +884,7 @@ class FieldInstanceResult {
|
|
|
884
884
|
if (this.parent) {
|
|
885
885
|
return this.parent.root();
|
|
886
886
|
}
|
|
887
|
-
throw new Error(
|
|
887
|
+
throw new Error('Internal Error, Null parent FieldInstanceResult');
|
|
888
888
|
}
|
|
889
889
|
getUngroupPartitions(ungroupSet) {
|
|
890
890
|
let ret = [];
|
|
@@ -892,13 +892,13 @@ class FieldInstanceResult {
|
|
|
892
892
|
let excludeFields = [];
|
|
893
893
|
let inScopeFieldNames = [];
|
|
894
894
|
// all defaults to all fields at the current level.
|
|
895
|
-
if (ungroupSet === undefined || ungroupSet.type ===
|
|
895
|
+
if (ungroupSet === undefined || ungroupSet.type === 'all') {
|
|
896
896
|
// fields specified an an all, convert it to an exclude set.
|
|
897
897
|
const allFields = (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.fields) || [];
|
|
898
898
|
// convert an All into the equivalent exclude
|
|
899
|
-
excludeFields = this.fields(
|
|
900
|
-
fi.fieldUsage.type ===
|
|
901
|
-
allFields.indexOf(fi.f.getIdentifier()) === -1).map(
|
|
899
|
+
excludeFields = this.fields(fi => isScalarField(fi.f) &&
|
|
900
|
+
fi.fieldUsage.type === 'result' &&
|
|
901
|
+
allFields.indexOf(fi.f.getIdentifier()) === -1).map(fi => fi.f.getIdentifier());
|
|
902
902
|
}
|
|
903
903
|
else {
|
|
904
904
|
excludeFields = ungroupSet.fields;
|
|
@@ -906,13 +906,13 @@ class FieldInstanceResult {
|
|
|
906
906
|
let firstScope = true;
|
|
907
907
|
while (p !== undefined) {
|
|
908
908
|
// get a list of valid fieldnames for the current scope.
|
|
909
|
-
if (firstScope || (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type) ===
|
|
909
|
+
if (firstScope || (ungroupSet === null || ungroupSet === void 0 ? void 0 : ungroupSet.type) === 'exclude') {
|
|
910
910
|
inScopeFieldNames = inScopeFieldNames.concat(p
|
|
911
|
-
.fields(
|
|
912
|
-
.map(
|
|
911
|
+
.fields(fi => isScalarField(fi.f) && fi.fieldUsage.type === 'result')
|
|
912
|
+
.map(fi => fi.f.getIdentifier()));
|
|
913
913
|
}
|
|
914
|
-
ret = ret.concat(p.fields(
|
|
915
|
-
fi.fieldUsage.type ===
|
|
914
|
+
ret = ret.concat(p.fields(fi => isScalarField(fi.f) &&
|
|
915
|
+
fi.fieldUsage.type === 'result' &&
|
|
916
916
|
excludeFields.indexOf(fi.f.getIdentifier()) === -1));
|
|
917
917
|
p = p.parent;
|
|
918
918
|
firstScope = false;
|
|
@@ -954,12 +954,12 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
954
954
|
// groups that were used in ungroup calculations. We need to do this only
|
|
955
955
|
// once and in the very next stage.
|
|
956
956
|
eliminateComputeGroupsSQL() {
|
|
957
|
-
if (this.elimatedComputeGroups || this.computeOnlyGroups.length
|
|
958
|
-
return
|
|
957
|
+
if (this.elimatedComputeGroups || this.computeOnlyGroups.length === 0) {
|
|
958
|
+
return '';
|
|
959
959
|
}
|
|
960
960
|
else {
|
|
961
961
|
this.elimatedComputeGroups = true;
|
|
962
|
-
return `group_set NOT IN (${this.computeOnlyGroups.join(
|
|
962
|
+
return `group_set NOT IN (${this.computeOnlyGroups.join(',')})`;
|
|
963
963
|
}
|
|
964
964
|
}
|
|
965
965
|
// look at all the fields again in the structs in the query
|
|
@@ -968,14 +968,14 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
968
968
|
for (const [name, join] of this.joins) {
|
|
969
969
|
// first join is by default the
|
|
970
970
|
const relationship = join.parentRelationship();
|
|
971
|
-
if (relationship ===
|
|
971
|
+
if (relationship === 'many_to_many') {
|
|
972
972
|
// everything must be calculated with symmetric aggregates
|
|
973
|
-
leafiest =
|
|
973
|
+
leafiest = '0never';
|
|
974
974
|
}
|
|
975
975
|
else if (leafiest === undefined) {
|
|
976
976
|
leafiest = name;
|
|
977
977
|
}
|
|
978
|
-
else if (join.parentRelationship() ===
|
|
978
|
+
else if (join.parentRelationship() === 'one_to_many') {
|
|
979
979
|
// check up the parent relationship until you find
|
|
980
980
|
// the current leafiest node. If it isn't in the direct path
|
|
981
981
|
// we need symmetric aggregate for everything.
|
|
@@ -986,7 +986,7 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
986
986
|
}
|
|
987
987
|
else {
|
|
988
988
|
// we have more than on one_to_many join chain, all bets are off.
|
|
989
|
-
leafiest =
|
|
989
|
+
leafiest = '0never';
|
|
990
990
|
}
|
|
991
991
|
}
|
|
992
992
|
}
|
|
@@ -1005,7 +1005,7 @@ class FieldInstanceResultRoot extends FieldInstanceResult {
|
|
|
1005
1005
|
if (!j.queryStruct.primaryKey()) {
|
|
1006
1006
|
j.makeUniqueKey = true;
|
|
1007
1007
|
}
|
|
1008
|
-
if (j.queryStruct.fieldDef.structRelationship.type ===
|
|
1008
|
+
if (j.queryStruct.fieldDef.structRelationship.type === 'nested') {
|
|
1009
1009
|
j = j.parent;
|
|
1010
1010
|
}
|
|
1011
1011
|
else {
|
|
@@ -1034,9 +1034,9 @@ class JoinInstance {
|
|
|
1034
1034
|
this.joinFilterConditions = [];
|
|
1035
1035
|
for (const filter of this.queryStruct.fieldDef.filterList) {
|
|
1036
1036
|
const qf = new QueryFieldBoolean({
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1037
|
+
type: 'boolean',
|
|
1038
|
+
name: 'ignoreme',
|
|
1039
|
+
e: filter.expression,
|
|
1040
1040
|
}, this.queryStruct);
|
|
1041
1041
|
this.joinFilterConditions.push(qf);
|
|
1042
1042
|
}
|
|
@@ -1044,19 +1044,19 @@ class JoinInstance {
|
|
|
1044
1044
|
}
|
|
1045
1045
|
parentRelationship() {
|
|
1046
1046
|
if (this.queryStruct.parent === undefined) {
|
|
1047
|
-
return
|
|
1047
|
+
return 'root';
|
|
1048
1048
|
}
|
|
1049
1049
|
switch (this.queryStruct.fieldDef.structRelationship.type) {
|
|
1050
|
-
case
|
|
1051
|
-
return
|
|
1052
|
-
case
|
|
1053
|
-
return
|
|
1054
|
-
case
|
|
1055
|
-
return
|
|
1056
|
-
case
|
|
1057
|
-
return
|
|
1058
|
-
case
|
|
1059
|
-
return
|
|
1050
|
+
case 'one':
|
|
1051
|
+
return 'many_to_one';
|
|
1052
|
+
case 'cross':
|
|
1053
|
+
return 'many_to_many';
|
|
1054
|
+
case 'many':
|
|
1055
|
+
return 'one_to_many';
|
|
1056
|
+
case 'nested':
|
|
1057
|
+
return 'one_to_many';
|
|
1058
|
+
case 'inline':
|
|
1059
|
+
return 'one_to_one';
|
|
1060
1060
|
default:
|
|
1061
1061
|
throw new Error(`Internal error unknown relationship type to parent for ${this.queryStruct.fieldDef.name}`);
|
|
1062
1062
|
}
|
|
@@ -1066,9 +1066,9 @@ class JoinInstance {
|
|
|
1066
1066
|
const dialectFieldList = [];
|
|
1067
1067
|
for (const f of this.queryStruct.fieldDef.fields.filter(malloy_types_1.isPhysical)) {
|
|
1068
1068
|
dialectFieldList.push({
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1069
|
+
type: f.type,
|
|
1070
|
+
sqlExpression: (0, malloy_types_1.getIdentifier)(f),
|
|
1071
|
+
sqlOutputName: (0, malloy_types_1.getIdentifier)(f),
|
|
1072
1072
|
});
|
|
1073
1073
|
}
|
|
1074
1074
|
return dialectFieldList;
|
|
@@ -1086,12 +1086,12 @@ class Segment {
|
|
|
1086
1086
|
// static nextStructDef(s: StructDef, q: AnonymousQueryDef): StructDef
|
|
1087
1087
|
static nextStructDef(structDef, segment) {
|
|
1088
1088
|
const qs = new QueryStruct(structDef, {
|
|
1089
|
-
|
|
1089
|
+
model: new QueryModel(undefined),
|
|
1090
1090
|
});
|
|
1091
1091
|
const turtleDef = {
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1092
|
+
type: 'turtle',
|
|
1093
|
+
name: 'ignoreme',
|
|
1094
|
+
pipeline: [segment],
|
|
1095
1095
|
};
|
|
1096
1096
|
const queryQueryQuery = QueryQuery.makeQuery(turtleDef, qs, new StageWriter(true, undefined) // stage write indicates we want to get a result.
|
|
1097
1097
|
);
|
|
@@ -1125,28 +1125,25 @@ class QueryQuery extends QueryField {
|
|
|
1125
1125
|
firstStage.extendSource !== undefined) {
|
|
1126
1126
|
parent = new QueryStruct({
|
|
1127
1127
|
...parentStruct.fieldDef,
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
...firstStage.extendSource
|
|
1131
|
-
]
|
|
1132
|
-
}, parent.parent ? { "struct": parent } : { "model": parent.model });
|
|
1128
|
+
fields: [...parentStruct.fieldDef.fields, ...firstStage.extendSource],
|
|
1129
|
+
}, parent.parent ? { struct: parent } : { model: parent.model });
|
|
1133
1130
|
flatTurtleDef = {
|
|
1134
1131
|
...flatTurtleDef,
|
|
1135
|
-
|
|
1132
|
+
pipeline: [
|
|
1136
1133
|
{
|
|
1137
1134
|
...firstStage,
|
|
1138
|
-
|
|
1135
|
+
extendSource: undefined,
|
|
1139
1136
|
},
|
|
1140
|
-
...flatTurtleDef.pipeline.slice(1)
|
|
1141
|
-
]
|
|
1137
|
+
...flatTurtleDef.pipeline.slice(1),
|
|
1138
|
+
],
|
|
1142
1139
|
};
|
|
1143
1140
|
}
|
|
1144
1141
|
switch (firstStage.type) {
|
|
1145
|
-
case
|
|
1142
|
+
case 'reduce':
|
|
1146
1143
|
return new QueryQueryReduce(flatTurtleDef, parent, stageWriter);
|
|
1147
|
-
case
|
|
1144
|
+
case 'project':
|
|
1148
1145
|
return new QueryQueryProject(flatTurtleDef, parent, stageWriter);
|
|
1149
|
-
case
|
|
1146
|
+
case 'index':
|
|
1150
1147
|
return new QueryQueryIndex(flatTurtleDef, parent, stageWriter);
|
|
1151
1148
|
}
|
|
1152
1149
|
}
|
|
@@ -1154,11 +1151,11 @@ class QueryQuery extends QueryField {
|
|
|
1154
1151
|
switch (this.firstSegment.type) {
|
|
1155
1152
|
// case "index":
|
|
1156
1153
|
// return this.firstSegment.fields || [];
|
|
1157
|
-
case
|
|
1154
|
+
case 'reduce':
|
|
1158
1155
|
return this.firstSegment.fields;
|
|
1159
1156
|
// probably need some way of checking type class of field here...
|
|
1160
1157
|
// project should only contain scalars
|
|
1161
|
-
case
|
|
1158
|
+
case 'project':
|
|
1162
1159
|
return this.firstSegment.fields;
|
|
1163
1160
|
default:
|
|
1164
1161
|
throw new Error(`Query contains no fields ${JSON.stringify(this.fieldDef)}`);
|
|
@@ -1169,19 +1166,19 @@ class QueryQuery extends QueryField {
|
|
|
1169
1166
|
let as;
|
|
1170
1167
|
let field;
|
|
1171
1168
|
// if it is a string
|
|
1172
|
-
if (typeof f ===
|
|
1169
|
+
if (typeof f === 'string') {
|
|
1173
1170
|
field = this.parent.getQueryFieldByName(f);
|
|
1174
1171
|
}
|
|
1175
|
-
else if (
|
|
1172
|
+
else if ('type' in f) {
|
|
1176
1173
|
field = this.parent.makeQueryField(f);
|
|
1177
1174
|
}
|
|
1178
1175
|
// or FilteredAliasedName or a hacked timestamp field.
|
|
1179
|
-
else if (
|
|
1176
|
+
else if ('name' in f && 'as' in f) {
|
|
1180
1177
|
field = this.parent.getQueryFieldByName(f.name);
|
|
1181
1178
|
// QueryFieldStructs return new names...
|
|
1182
1179
|
as = field.fieldDef.as || f.as;
|
|
1183
1180
|
if (field instanceof QueryFieldStruct) {
|
|
1184
|
-
throw new Error(
|
|
1181
|
+
throw new Error('Syntax currently disallowed. Semantics up for discussion');
|
|
1185
1182
|
}
|
|
1186
1183
|
// Types of aliased fields.
|
|
1187
1184
|
// turtles
|
|
@@ -1249,17 +1246,17 @@ class QueryQuery extends QueryField {
|
|
|
1249
1246
|
expandWildCards(fields, filter = undefined) {
|
|
1250
1247
|
let ret = [];
|
|
1251
1248
|
for (const f of fields) {
|
|
1252
|
-
if (typeof f !==
|
|
1249
|
+
if (typeof f !== 'string') {
|
|
1253
1250
|
ret.push(f);
|
|
1254
1251
|
}
|
|
1255
1252
|
else {
|
|
1256
1253
|
const fieldName = f;
|
|
1257
|
-
const path = fieldName.split(
|
|
1258
|
-
if (!path[path.length - 1].startsWith(
|
|
1254
|
+
const path = fieldName.split('.');
|
|
1255
|
+
if (!path[path.length - 1].startsWith('*')) {
|
|
1259
1256
|
ret.push(f);
|
|
1260
1257
|
}
|
|
1261
1258
|
else {
|
|
1262
|
-
const expandChildren = path.pop() ===
|
|
1259
|
+
const expandChildren = path.pop() === '**';
|
|
1263
1260
|
let struct = this.parent;
|
|
1264
1261
|
let pathElementName;
|
|
1265
1262
|
while (path.length > 0 && (pathElementName = path.shift())) {
|
|
@@ -1290,7 +1287,7 @@ class QueryQuery extends QueryField {
|
|
|
1290
1287
|
struct = node;
|
|
1291
1288
|
}
|
|
1292
1289
|
else {
|
|
1293
|
-
throw new Error(
|
|
1290
|
+
throw new Error('Internal Error: Unknown object type');
|
|
1294
1291
|
}
|
|
1295
1292
|
resultStruct
|
|
1296
1293
|
.root()
|
|
@@ -1303,12 +1300,12 @@ class QueryQuery extends QueryField {
|
|
|
1303
1300
|
resultStruct.root().isComplexQuery = true;
|
|
1304
1301
|
resultStruct.root().queryUsesPartitioning = true;
|
|
1305
1302
|
if (expr.fields && expr.fields.length > 0) {
|
|
1306
|
-
const key = expr.fields.sort().join(
|
|
1303
|
+
const key = expr.fields.sort().join('|') + expr.type;
|
|
1307
1304
|
if (resultStruct.ungroupedSets.get(key) === undefined) {
|
|
1308
1305
|
resultStruct.ungroupedSets.set(key, {
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1306
|
+
type: expr.type,
|
|
1307
|
+
fields: expr.fields,
|
|
1308
|
+
groupSet: -1,
|
|
1312
1309
|
});
|
|
1313
1310
|
}
|
|
1314
1311
|
}
|
|
@@ -1334,28 +1331,28 @@ class QueryQuery extends QueryField {
|
|
|
1334
1331
|
else if ((0, malloy_types_1.isDialectFragment)(expr)) {
|
|
1335
1332
|
const expressions = [];
|
|
1336
1333
|
switch (expr.function) {
|
|
1337
|
-
case
|
|
1334
|
+
case 'now':
|
|
1338
1335
|
break;
|
|
1339
|
-
case
|
|
1336
|
+
case 'div':
|
|
1340
1337
|
expressions.push(expr.denominator);
|
|
1341
1338
|
expressions.push(expr.numerator);
|
|
1342
1339
|
break;
|
|
1343
|
-
case
|
|
1340
|
+
case 'timeLiteral':
|
|
1344
1341
|
break;
|
|
1345
|
-
case
|
|
1342
|
+
case 'stringLiteral':
|
|
1346
1343
|
break;
|
|
1347
|
-
case
|
|
1344
|
+
case 'timeDiff':
|
|
1348
1345
|
expressions.push(expr.left.value, expr.right.value);
|
|
1349
1346
|
break;
|
|
1350
|
-
case
|
|
1347
|
+
case 'delta':
|
|
1351
1348
|
expressions.push(expr.base.value, expr.delta);
|
|
1352
1349
|
break;
|
|
1353
|
-
case
|
|
1354
|
-
case
|
|
1350
|
+
case 'trunc':
|
|
1351
|
+
case 'extract':
|
|
1355
1352
|
expressions.push(expr.expr.value);
|
|
1356
1353
|
break;
|
|
1357
|
-
case
|
|
1358
|
-
case
|
|
1354
|
+
case 'regexpMatch':
|
|
1355
|
+
case 'cast':
|
|
1359
1356
|
expressions.push(expr.expr);
|
|
1360
1357
|
break;
|
|
1361
1358
|
default:
|
|
@@ -1392,7 +1389,7 @@ class QueryQuery extends QueryField {
|
|
|
1392
1389
|
for (const f of this.expandWildCards(resultStruct.firstSegment.fields)) {
|
|
1393
1390
|
const { as, field } = this.expandField(f);
|
|
1394
1391
|
if (field instanceof QueryTurtle || field instanceof QueryQuery) {
|
|
1395
|
-
if (this.firstSegment.type ===
|
|
1392
|
+
if (this.firstSegment.type === 'project') {
|
|
1396
1393
|
throw new Error(`Turtled Queries cannot be used in PROJECT - '${field.fieldDef.name}'`);
|
|
1397
1394
|
}
|
|
1398
1395
|
const fir = new FieldInstanceResult(field.fieldDef, resultStruct);
|
|
@@ -1402,11 +1399,11 @@ class QueryQuery extends QueryField {
|
|
|
1402
1399
|
else if (field instanceof QueryAtomicField) {
|
|
1403
1400
|
resultStruct.addField(as, field, {
|
|
1404
1401
|
resultIndex,
|
|
1405
|
-
|
|
1402
|
+
type: 'result',
|
|
1406
1403
|
});
|
|
1407
1404
|
this.addDependancies(resultStruct, field);
|
|
1408
1405
|
if (isCalculatedField(field)) {
|
|
1409
|
-
if (this.firstSegment.type ===
|
|
1406
|
+
if (this.firstSegment.type === 'project') {
|
|
1410
1407
|
throw new Error(`Aggregate Fields cannot be used in PROJECT - '${field.fieldDef.name}'`);
|
|
1411
1408
|
}
|
|
1412
1409
|
}
|
|
@@ -1446,7 +1443,7 @@ class QueryQuery extends QueryField {
|
|
|
1446
1443
|
}
|
|
1447
1444
|
for (const join of resultStruct.root().joins.values() || []) {
|
|
1448
1445
|
for (const qf of join.joinFilterConditions || []) {
|
|
1449
|
-
if (qf.fieldDef.type ===
|
|
1446
|
+
if (qf.fieldDef.type === 'boolean' && qf.fieldDef.e) {
|
|
1450
1447
|
this.addDependantExpr(resultStruct, qf.parent, qf.fieldDef.e, []);
|
|
1451
1448
|
}
|
|
1452
1449
|
}
|
|
@@ -1463,8 +1460,8 @@ class QueryQuery extends QueryField {
|
|
|
1463
1460
|
// in the correct catgory.
|
|
1464
1461
|
for (const cond of list || []) {
|
|
1465
1462
|
const context = this.parent;
|
|
1466
|
-
if ((which ===
|
|
1467
|
-
(which ===
|
|
1463
|
+
if ((which === 'having' && (0, malloy_types_1.expressionIsCalculation)(cond.expressionType)) ||
|
|
1464
|
+
(which === 'where' && cond.expressionType === 'scalar')) {
|
|
1468
1465
|
const sqlClause = this.generateExpressionFromExpr(resultStruct, context, cond.expression, undefined);
|
|
1469
1466
|
resultFilters.add(sqlClause);
|
|
1470
1467
|
}
|
|
@@ -1482,11 +1479,11 @@ class QueryQuery extends QueryField {
|
|
|
1482
1479
|
// get the source fieldname and filters associated with the field (so we can drill later)
|
|
1483
1480
|
getResultMetadata(fi) {
|
|
1484
1481
|
if (fi instanceof FieldInstanceField) {
|
|
1485
|
-
if (fi.fieldUsage.type ===
|
|
1482
|
+
if (fi.fieldUsage.type === 'result') {
|
|
1486
1483
|
const fieldDef = fi.f.fieldDef;
|
|
1487
1484
|
let filterList;
|
|
1488
1485
|
const sourceField = fi.f.parent.getFullOutputName() +
|
|
1489
|
-
(fieldDef.name || fieldDef.as ||
|
|
1486
|
+
(fieldDef.name || fieldDef.as || 'undefined');
|
|
1490
1487
|
const sourceExpression = fieldDef.code;
|
|
1491
1488
|
const sourceClasses = [sourceField];
|
|
1492
1489
|
if (isCalculatedField(fi.f)) {
|
|
@@ -1496,7 +1493,7 @@ class QueryQuery extends QueryField {
|
|
|
1496
1493
|
sourceExpression,
|
|
1497
1494
|
filterList,
|
|
1498
1495
|
sourceClasses,
|
|
1499
|
-
|
|
1496
|
+
fieldKind: 'measure',
|
|
1500
1497
|
};
|
|
1501
1498
|
}
|
|
1502
1499
|
if (isScalarField(fi.f)) {
|
|
@@ -1505,7 +1502,7 @@ class QueryQuery extends QueryField {
|
|
|
1505
1502
|
sourceExpression,
|
|
1506
1503
|
filterList,
|
|
1507
1504
|
sourceClasses,
|
|
1508
|
-
|
|
1505
|
+
fieldKind: 'dimension',
|
|
1509
1506
|
};
|
|
1510
1507
|
}
|
|
1511
1508
|
else {
|
|
@@ -1524,8 +1521,8 @@ class QueryQuery extends QueryField {
|
|
|
1524
1521
|
sourceField,
|
|
1525
1522
|
filterList,
|
|
1526
1523
|
sourceClasses,
|
|
1527
|
-
|
|
1528
|
-
limit
|
|
1524
|
+
fieldKind: 'struct',
|
|
1525
|
+
limit,
|
|
1529
1526
|
};
|
|
1530
1527
|
}
|
|
1531
1528
|
}
|
|
@@ -1540,22 +1537,22 @@ class QueryQuery extends QueryField {
|
|
|
1540
1537
|
for (const [name, fi] of resultStruct.allFields) {
|
|
1541
1538
|
const resultMetadata = this.getResultMetadata(fi);
|
|
1542
1539
|
if (fi instanceof FieldInstanceResult) {
|
|
1543
|
-
const { structDef } = this.generateTurtlePipelineSQL(fi, new StageWriter(true, undefined),
|
|
1540
|
+
const { structDef } = this.generateTurtlePipelineSQL(fi, new StageWriter(true, undefined), '<nosource>');
|
|
1544
1541
|
// LTNOTE: This is probably broken now. Need to look at the last stage
|
|
1545
1542
|
// to figure out the resulting nested/inline state...
|
|
1546
|
-
const resultType = fi.getRepeatedResultType() ===
|
|
1543
|
+
const resultType = fi.getRepeatedResultType() === 'nested' ? 'nested' : 'inline';
|
|
1547
1544
|
structDef.name = name;
|
|
1548
1545
|
structDef.structRelationship = {
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1546
|
+
field: name,
|
|
1547
|
+
type: resultType,
|
|
1548
|
+
isArray: false,
|
|
1552
1549
|
};
|
|
1553
|
-
structDef.structSource = {
|
|
1550
|
+
structDef.structSource = { type: resultType };
|
|
1554
1551
|
structDef.resultMetadata = resultMetadata;
|
|
1555
1552
|
fields.push(structDef);
|
|
1556
1553
|
}
|
|
1557
1554
|
else if (fi instanceof FieldInstanceField) {
|
|
1558
|
-
if (fi.fieldUsage.type ===
|
|
1555
|
+
if (fi.fieldUsage.type === 'result') {
|
|
1559
1556
|
if (fi.f instanceof QueryFieldStruct) {
|
|
1560
1557
|
fields.push(fi.f.getAsJoinedStructDef(name));
|
|
1561
1558
|
}
|
|
@@ -1573,74 +1570,74 @@ class QueryQuery extends QueryField {
|
|
|
1573
1570
|
const location = fi.f.fieldDef.location;
|
|
1574
1571
|
// build out the result fields...
|
|
1575
1572
|
switch (fi.f.fieldDef.type) {
|
|
1576
|
-
case
|
|
1577
|
-
case
|
|
1578
|
-
case
|
|
1573
|
+
case 'boolean':
|
|
1574
|
+
case 'json':
|
|
1575
|
+
case 'string':
|
|
1579
1576
|
fields.push({
|
|
1580
1577
|
name,
|
|
1581
|
-
|
|
1578
|
+
type: fi.f.fieldDef.type,
|
|
1582
1579
|
resultMetadata,
|
|
1583
|
-
location
|
|
1580
|
+
location,
|
|
1584
1581
|
});
|
|
1585
1582
|
break;
|
|
1586
|
-
case
|
|
1583
|
+
case 'timestamp': {
|
|
1587
1584
|
const timeframe = fi.f.fieldDef.timeframe;
|
|
1588
1585
|
switch (timeframe) {
|
|
1589
|
-
case
|
|
1590
|
-
case
|
|
1591
|
-
case
|
|
1592
|
-
case
|
|
1593
|
-
case
|
|
1586
|
+
case 'year':
|
|
1587
|
+
case 'month':
|
|
1588
|
+
case 'week':
|
|
1589
|
+
case 'quarter':
|
|
1590
|
+
case 'day':
|
|
1594
1591
|
fields.push({
|
|
1595
1592
|
name,
|
|
1596
|
-
|
|
1593
|
+
type: 'date',
|
|
1597
1594
|
timeframe,
|
|
1598
1595
|
resultMetadata,
|
|
1599
|
-
location
|
|
1596
|
+
location,
|
|
1600
1597
|
});
|
|
1601
1598
|
break;
|
|
1602
|
-
case
|
|
1603
|
-
case
|
|
1604
|
-
case
|
|
1599
|
+
case 'second':
|
|
1600
|
+
case 'minute':
|
|
1601
|
+
case 'hour':
|
|
1605
1602
|
fields.push({
|
|
1606
1603
|
name,
|
|
1607
|
-
|
|
1604
|
+
type: 'timestamp',
|
|
1608
1605
|
timeframe,
|
|
1609
1606
|
resultMetadata,
|
|
1610
|
-
location
|
|
1607
|
+
location,
|
|
1611
1608
|
});
|
|
1612
1609
|
break;
|
|
1613
1610
|
default:
|
|
1614
1611
|
fields.push({
|
|
1615
1612
|
name,
|
|
1616
|
-
|
|
1613
|
+
type: 'timestamp',
|
|
1617
1614
|
resultMetadata,
|
|
1618
|
-
location
|
|
1615
|
+
location,
|
|
1619
1616
|
});
|
|
1620
1617
|
break;
|
|
1621
1618
|
}
|
|
1622
1619
|
break;
|
|
1623
1620
|
}
|
|
1624
|
-
case
|
|
1621
|
+
case 'date': {
|
|
1625
1622
|
fields.push({
|
|
1626
1623
|
name,
|
|
1627
|
-
|
|
1628
|
-
|
|
1624
|
+
type: fi.f.fieldDef.type,
|
|
1625
|
+
timeframe: fi.f.fieldDef.timeframe,
|
|
1629
1626
|
resultMetadata,
|
|
1630
|
-
location
|
|
1627
|
+
location,
|
|
1631
1628
|
});
|
|
1632
1629
|
break;
|
|
1633
1630
|
}
|
|
1634
|
-
case
|
|
1631
|
+
case 'number':
|
|
1635
1632
|
fields.push({
|
|
1636
1633
|
name,
|
|
1637
|
-
|
|
1638
|
-
|
|
1634
|
+
numberType: fi.f.fieldDef.numberType,
|
|
1635
|
+
type: 'number',
|
|
1639
1636
|
resultMetadata,
|
|
1640
|
-
location
|
|
1637
|
+
location,
|
|
1641
1638
|
});
|
|
1642
1639
|
break;
|
|
1643
|
-
case
|
|
1640
|
+
case 'unsupported':
|
|
1644
1641
|
fields.push({ ...fi.f.fieldDef, resultMetadata, location });
|
|
1645
1642
|
break;
|
|
1646
1643
|
default:
|
|
@@ -1651,20 +1648,20 @@ class QueryQuery extends QueryField {
|
|
|
1651
1648
|
}
|
|
1652
1649
|
return {
|
|
1653
1650
|
fields,
|
|
1654
|
-
|
|
1655
|
-
|
|
1651
|
+
name: this.resultStage || 'result',
|
|
1652
|
+
dialect: this.parent.dialect.name,
|
|
1656
1653
|
primaryKey,
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1654
|
+
structRelationship: {
|
|
1655
|
+
type: 'basetable',
|
|
1656
|
+
connectionName: this.parent.connectionName,
|
|
1660
1657
|
},
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1658
|
+
structSource: { type: 'query_result' },
|
|
1659
|
+
resultMetadata: this.getResultMetadata(this.rootResult),
|
|
1660
|
+
type: 'struct',
|
|
1664
1661
|
};
|
|
1665
1662
|
}
|
|
1666
1663
|
generateSQLJoinBlock(stageWriter, ji) {
|
|
1667
|
-
let s =
|
|
1664
|
+
let s = '';
|
|
1668
1665
|
const qs = ji.queryStruct;
|
|
1669
1666
|
const structRelationship = qs.fieldDef.structRelationship;
|
|
1670
1667
|
let structSQL = qs.structSourceSQL(stageWriter);
|
|
@@ -1672,55 +1669,55 @@ class QueryQuery extends QueryField {
|
|
|
1672
1669
|
if (ji.makeUniqueKey) {
|
|
1673
1670
|
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as __distinct_key, * FROM ${structSQL})`;
|
|
1674
1671
|
}
|
|
1675
|
-
let onCondition =
|
|
1672
|
+
let onCondition = '';
|
|
1676
1673
|
if (qs.parent === undefined) {
|
|
1677
|
-
throw new Error(
|
|
1674
|
+
throw new Error('Expected joined struct to have a parent.');
|
|
1678
1675
|
}
|
|
1679
1676
|
if (structRelationship.onExpression) {
|
|
1680
1677
|
onCondition = new QueryFieldBoolean({
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1678
|
+
type: 'boolean',
|
|
1679
|
+
name: 'ignoreme',
|
|
1680
|
+
e: structRelationship.onExpression,
|
|
1684
1681
|
}, qs.parent).generateExpression(this.rootResult);
|
|
1685
1682
|
}
|
|
1686
1683
|
else {
|
|
1687
|
-
onCondition =
|
|
1684
|
+
onCondition = '1=1';
|
|
1688
1685
|
}
|
|
1689
|
-
let filters =
|
|
1686
|
+
let filters = '';
|
|
1690
1687
|
let conditions = undefined;
|
|
1691
1688
|
if (ji.joinFilterConditions) {
|
|
1692
|
-
conditions = ji.joinFilterConditions.map(
|
|
1689
|
+
conditions = ji.joinFilterConditions.map(qf => qf.generateExpression(this.rootResult));
|
|
1693
1690
|
}
|
|
1694
1691
|
if (ji.children.length === 0 || conditions === undefined) {
|
|
1695
1692
|
if (conditions !== undefined && conditions.length >= 1) {
|
|
1696
|
-
filters = ` AND (${conditions.join(
|
|
1693
|
+
filters = ` AND (${conditions.join(' AND ')})`;
|
|
1697
1694
|
}
|
|
1698
1695
|
s += `LEFT JOIN ${structSQL} AS ${ji.alias}\n ON ${onCondition}${filters}\n`;
|
|
1699
1696
|
}
|
|
1700
1697
|
else {
|
|
1701
1698
|
let select = `SELECT ${ji.alias}.*`;
|
|
1702
|
-
let joins =
|
|
1699
|
+
let joins = '';
|
|
1703
1700
|
for (const childJoin of ji.children) {
|
|
1704
1701
|
joins += this.generateSQLJoinBlock(stageWriter, childJoin);
|
|
1705
|
-
const physicalFields = (0, malloy_types_1.getPhysicalFields)(childJoin.queryStruct.fieldDef).map(
|
|
1702
|
+
const physicalFields = (0, malloy_types_1.getPhysicalFields)(childJoin.queryStruct.fieldDef).map(fieldDef => this.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name));
|
|
1706
1703
|
select += `, ${this.parent.dialect.sqlSelectAliasAsStruct(childJoin.alias, physicalFields)} AS ${childJoin.alias}`;
|
|
1707
1704
|
}
|
|
1708
|
-
select += `\nFROM ${structSQL} AS ${ji.alias}\n${joins}\nWHERE ${conditions === null || conditions === void 0 ? void 0 : conditions.join(
|
|
1705
|
+
select += `\nFROM ${structSQL} AS ${ji.alias}\n${joins}\nWHERE ${conditions === null || conditions === void 0 ? void 0 : conditions.join(' AND ')}\n`;
|
|
1709
1706
|
s += `LEFT JOIN (\n${(0, utils_1.indent)(select)}) AS ${ji.alias}\n ON ${onCondition}\n`;
|
|
1710
1707
|
return s;
|
|
1711
1708
|
}
|
|
1712
1709
|
}
|
|
1713
|
-
else if (structRelationship.type ===
|
|
1710
|
+
else if (structRelationship.type === 'nested') {
|
|
1714
1711
|
if (qs.parent === undefined || ji.parent === undefined) {
|
|
1715
|
-
throw new Error(
|
|
1712
|
+
throw new Error('Internal Error, nested structure with no parent.');
|
|
1716
1713
|
}
|
|
1717
|
-
const fieldExpression = this.parent.dialect.sqlFieldReference(qs.parent.getSQLIdentifier(), structRelationship.field,
|
|
1714
|
+
const fieldExpression = this.parent.dialect.sqlFieldReference(qs.parent.getSQLIdentifier(), structRelationship.field, 'struct', qs.parent.fieldDef.structRelationship.type === 'nested', this.parent.fieldDef.structRelationship.type === 'nested' &&
|
|
1718
1715
|
this.parent.fieldDef.structRelationship.isArray);
|
|
1719
1716
|
// we need to generate primary key. If parent has a primary key combine
|
|
1720
1717
|
s += `${this.parent.dialect.sqlUnnestAlias(fieldExpression, ji.alias, ji.getDialectFieldList(), ji.makeUniqueKey, structRelationship.isArray)}\n`;
|
|
1721
1718
|
}
|
|
1722
|
-
else if (structRelationship.type ===
|
|
1723
|
-
throw new Error(
|
|
1719
|
+
else if (structRelationship.type === 'inline') {
|
|
1720
|
+
throw new Error('Internal Error: inline structs should never appear in join trees');
|
|
1724
1721
|
}
|
|
1725
1722
|
else {
|
|
1726
1723
|
throw new Error(`Join type not implemented ${JSON.stringify(qs.fieldDef.structRelationship)}`);
|
|
@@ -1731,7 +1728,7 @@ class QueryQuery extends QueryField {
|
|
|
1731
1728
|
return s;
|
|
1732
1729
|
}
|
|
1733
1730
|
generateSQLJoins(stageWriter) {
|
|
1734
|
-
let s =
|
|
1731
|
+
let s = '';
|
|
1735
1732
|
// get the first value from the map (weird, I know)
|
|
1736
1733
|
const [[, ji]] = this.rootResult.joins;
|
|
1737
1734
|
const qs = ji.queryStruct;
|
|
@@ -1744,14 +1741,14 @@ class QueryQuery extends QueryField {
|
|
|
1744
1741
|
}
|
|
1745
1742
|
}
|
|
1746
1743
|
const structRelationship = qs.fieldDef.structRelationship;
|
|
1747
|
-
if (structRelationship.type ===
|
|
1744
|
+
if (structRelationship.type === 'basetable') {
|
|
1748
1745
|
if (ji.makeUniqueKey) {
|
|
1749
1746
|
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as __distinct_key, * FROM ${structSQL} as x)`;
|
|
1750
1747
|
}
|
|
1751
1748
|
s += `FROM ${structSQL} as ${this.parent.getIdentifier()}\n`;
|
|
1752
1749
|
}
|
|
1753
1750
|
else {
|
|
1754
|
-
throw new Error(
|
|
1751
|
+
throw new Error('Internal Error, queries must start from a basetable');
|
|
1755
1752
|
}
|
|
1756
1753
|
for (const childJoin of ji.children) {
|
|
1757
1754
|
s += this.generateSQLJoinBlock(stageWriter, childJoin);
|
|
@@ -1759,60 +1756,60 @@ class QueryQuery extends QueryField {
|
|
|
1759
1756
|
return s;
|
|
1760
1757
|
}
|
|
1761
1758
|
genereateSQLOrderBy(queryDef, resultStruct) {
|
|
1762
|
-
let s =
|
|
1763
|
-
if (this.firstSegment.type ===
|
|
1764
|
-
return
|
|
1759
|
+
let s = '';
|
|
1760
|
+
if (this.firstSegment.type === 'project' && !queryDef.orderBy) {
|
|
1761
|
+
return ''; // No default ordering for project.
|
|
1765
1762
|
}
|
|
1766
1763
|
const orderBy = queryDef.orderBy || resultStruct.calculateDefaultOrderBy();
|
|
1767
1764
|
const o = [];
|
|
1768
1765
|
for (const f of orderBy) {
|
|
1769
|
-
if (typeof f.field ===
|
|
1766
|
+
if (typeof f.field === 'string') {
|
|
1770
1767
|
// convert name to an index
|
|
1771
1768
|
const fi = resultStruct.getField(f.field);
|
|
1772
|
-
if (fi && fi.fieldUsage.type ===
|
|
1773
|
-
o.push(`${fi.fieldUsage.resultIndex} ${f.dir ||
|
|
1769
|
+
if (fi && fi.fieldUsage.type === 'result') {
|
|
1770
|
+
o.push(`${fi.fieldUsage.resultIndex} ${f.dir || 'ASC'}`);
|
|
1774
1771
|
}
|
|
1775
1772
|
else {
|
|
1776
1773
|
throw new Error(`Unknown field in ORDER BY ${f.field}`);
|
|
1777
1774
|
}
|
|
1778
1775
|
}
|
|
1779
1776
|
else {
|
|
1780
|
-
o.push(`${f.field} ${f.dir ||
|
|
1777
|
+
o.push(`${f.field} ${f.dir || 'ASC'}`);
|
|
1781
1778
|
}
|
|
1782
1779
|
}
|
|
1783
1780
|
if (o.length > 0) {
|
|
1784
|
-
s = this.parent.dialect.sqlOrderBy(o) +
|
|
1781
|
+
s = this.parent.dialect.sqlOrderBy(o) + '\n';
|
|
1785
1782
|
}
|
|
1786
1783
|
return s;
|
|
1787
1784
|
}
|
|
1788
1785
|
generateSimpleSQL(stageWriter) {
|
|
1789
|
-
let s =
|
|
1790
|
-
s +=
|
|
1786
|
+
let s = '';
|
|
1787
|
+
s += 'SELECT \n';
|
|
1791
1788
|
const fields = [];
|
|
1792
1789
|
for (const [name, field] of this.rootResult.allFields) {
|
|
1793
1790
|
const fi = field;
|
|
1794
1791
|
const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
|
|
1795
|
-
if (fi.fieldUsage.type ===
|
|
1792
|
+
if (fi.fieldUsage.type === 'result') {
|
|
1796
1793
|
fields.push(` ${fi.f.generateExpression(this.rootResult)} as ${sqlName}`);
|
|
1797
1794
|
}
|
|
1798
1795
|
}
|
|
1799
|
-
s += (0, utils_1.indent)(fields.join(
|
|
1796
|
+
s += (0, utils_1.indent)(fields.join(',\n')) + '\n';
|
|
1800
1797
|
s += this.generateSQLJoins(stageWriter);
|
|
1801
|
-
s += this.generateSQLFilters(this.rootResult,
|
|
1798
|
+
s += this.generateSQLFilters(this.rootResult, 'where').sql('where');
|
|
1802
1799
|
// group by
|
|
1803
|
-
if (this.firstSegment.type ===
|
|
1800
|
+
if (this.firstSegment.type === 'reduce') {
|
|
1804
1801
|
const n = [];
|
|
1805
1802
|
for (const field of this.rootResult.fields()) {
|
|
1806
1803
|
const fi = field;
|
|
1807
|
-
if (fi.fieldUsage.type ===
|
|
1804
|
+
if (fi.fieldUsage.type === 'result' && isScalarField(fi.f)) {
|
|
1808
1805
|
n.push(fi.fieldUsage.resultIndex.toString());
|
|
1809
1806
|
}
|
|
1810
1807
|
}
|
|
1811
1808
|
if (n.length > 0) {
|
|
1812
|
-
s += `GROUP BY ${n.join(
|
|
1809
|
+
s += `GROUP BY ${n.join(',')}\n`;
|
|
1813
1810
|
}
|
|
1814
1811
|
}
|
|
1815
|
-
s += this.generateSQLFilters(this.rootResult,
|
|
1812
|
+
s += this.generateSQLFilters(this.rootResult, 'having').sql('having');
|
|
1816
1813
|
// order by
|
|
1817
1814
|
s += this.genereateSQLOrderBy(this.firstSegment, this.rootResult);
|
|
1818
1815
|
// limit
|
|
@@ -1829,9 +1826,9 @@ class QueryQuery extends QueryField {
|
|
|
1829
1826
|
return lastStageName;
|
|
1830
1827
|
}
|
|
1831
1828
|
const pipelinesSQL = outputPipelinedSQL
|
|
1832
|
-
.map(
|
|
1829
|
+
.map(o => `${o.pipelineSQL} as ${o.sqlFieldName}
|
|
1833
1830
|
`)
|
|
1834
|
-
.join(
|
|
1831
|
+
.join(',\n');
|
|
1835
1832
|
return stageWriter.addStage(`SELECT * replace (${pipelinesSQL}) FROM ${lastStageName}
|
|
1836
1833
|
`);
|
|
1837
1834
|
}
|
|
@@ -1839,15 +1836,15 @@ class QueryQuery extends QueryField {
|
|
|
1839
1836
|
for (const [name, fi] of resultSet.allFields) {
|
|
1840
1837
|
const outputName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
|
|
1841
1838
|
if (fi instanceof FieldInstanceField) {
|
|
1842
|
-
if (fi.fieldUsage.type ===
|
|
1839
|
+
if (fi.fieldUsage.type === 'result') {
|
|
1843
1840
|
const exp = fi.getSQL();
|
|
1844
1841
|
if (isScalarField(fi.f)) {
|
|
1845
|
-
if (this.parent.dialect.name ===
|
|
1842
|
+
if (this.parent.dialect.name === 'standardsql' &&
|
|
1846
1843
|
this.rootResult.queryUsesPartitioning) {
|
|
1847
1844
|
// BigQuery can't partition aggregate function except when the field has no
|
|
1848
1845
|
// expression. Additionally it can't partition by floats. We stuff expressions
|
|
1849
1846
|
// and numbers as strings into a lateral join when the query has ungrouped expressions
|
|
1850
|
-
if (fi.f.fieldDef.type ===
|
|
1847
|
+
if (fi.f.fieldDef.type === 'number') {
|
|
1851
1848
|
// make an extra dimension as a string
|
|
1852
1849
|
output.sql.push(`${exp} as ${outputName}`);
|
|
1853
1850
|
const outputFieldName = `__lateral_join_bag.${outputName}_string`;
|
|
@@ -1876,10 +1873,10 @@ class QueryQuery extends QueryField {
|
|
|
1876
1873
|
}
|
|
1877
1874
|
}
|
|
1878
1875
|
else if (fi instanceof FieldInstanceResult) {
|
|
1879
|
-
if (fi.firstSegment.type ===
|
|
1876
|
+
if (fi.firstSegment.type === 'reduce') {
|
|
1880
1877
|
this.generateStage0Fields(fi, output, stageWriter);
|
|
1881
1878
|
}
|
|
1882
|
-
else if (fi.firstSegment.type ===
|
|
1879
|
+
else if (fi.firstSegment.type === 'project') {
|
|
1883
1880
|
const s = this.generateTurtleSQL(fi, stageWriter, outputName, output.outputPipelinedSQL);
|
|
1884
1881
|
output.sql.push(`${s} as ${outputName}`);
|
|
1885
1882
|
output.fieldIndex++;
|
|
@@ -1890,7 +1887,7 @@ class QueryQuery extends QueryField {
|
|
|
1890
1887
|
// leaf turtles can have their having clauses in the main query
|
|
1891
1888
|
// turtles with leaves need to promote their state to their
|
|
1892
1889
|
// children.
|
|
1893
|
-
const having = this.generateSQLFilters(resultSet,
|
|
1890
|
+
const having = this.generateSQLFilters(resultSet, 'having');
|
|
1894
1891
|
if (!having.empty()) {
|
|
1895
1892
|
// if we have no children, the having can run at the root level
|
|
1896
1893
|
if (resultSet.childGroups.length === 1) {
|
|
@@ -1908,11 +1905,11 @@ class QueryQuery extends QueryField {
|
|
|
1908
1905
|
generateSQLWhereChildren(resultStruct) {
|
|
1909
1906
|
const wheres = new utils_1.AndChain();
|
|
1910
1907
|
for (const [, field] of resultStruct.allFields) {
|
|
1911
|
-
if (field.type ===
|
|
1908
|
+
if (field.type === 'query') {
|
|
1912
1909
|
const fir = field;
|
|
1913
|
-
const turtleWhere = this.generateSQLFilters(fir,
|
|
1910
|
+
const turtleWhere = this.generateSQLFilters(fir, 'where');
|
|
1914
1911
|
if (turtleWhere.present()) {
|
|
1915
|
-
const groupSets = fir.childGroups.join(
|
|
1912
|
+
const groupSets = fir.childGroups.join(',');
|
|
1916
1913
|
wheres.add(`(group_set NOT IN (${groupSets})` +
|
|
1917
1914
|
` OR (group_set IN (${groupSets}) AND ${turtleWhere.sql()}))`);
|
|
1918
1915
|
}
|
|
@@ -1922,9 +1919,9 @@ class QueryQuery extends QueryField {
|
|
|
1922
1919
|
return wheres;
|
|
1923
1920
|
}
|
|
1924
1921
|
generateSQLWhereTurtled() {
|
|
1925
|
-
const wheres = this.generateSQLFilters(this.rootResult,
|
|
1922
|
+
const wheres = this.generateSQLFilters(this.rootResult, 'where');
|
|
1926
1923
|
wheres.addChain(this.generateSQLWhereChildren(this.rootResult));
|
|
1927
|
-
return wheres.sql(
|
|
1924
|
+
return wheres.sql('where');
|
|
1928
1925
|
}
|
|
1929
1926
|
// iterate over the nested queries looking for Havings (and someday limits).
|
|
1930
1927
|
// if you find any, generate a new stage(s) to perform these functions.
|
|
@@ -1937,51 +1934,51 @@ class QueryQuery extends QueryField {
|
|
|
1937
1934
|
const dimensions = [];
|
|
1938
1935
|
let r = result;
|
|
1939
1936
|
while (r) {
|
|
1940
|
-
for (const name of r.fieldNames(
|
|
1937
|
+
for (const name of r.fieldNames(fi => isScalarField(fi.f))) {
|
|
1941
1938
|
dimensions.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${r.groupSet}`));
|
|
1942
1939
|
}
|
|
1943
1940
|
r = r.parent;
|
|
1944
1941
|
}
|
|
1945
|
-
fields.push(`MAX(CASE WHEN group_set IN (${result.childGroups.join(
|
|
1946
|
-
.map(
|
|
1947
|
-
.join(
|
|
1942
|
+
fields.push(`MAX(CASE WHEN group_set IN (${result.childGroups.join(',')}) THEN __delete__${result.groupSet} END) OVER(partition by ${dimensions
|
|
1943
|
+
.map(x => `CAST(${x} AS ${this.parent.dialect.stringTypeName}) `)
|
|
1944
|
+
.join(',')}) as __shaving__${result.groupSet}`);
|
|
1948
1945
|
}
|
|
1949
1946
|
}
|
|
1950
1947
|
if (resultsWithHaving.length > 0) {
|
|
1951
|
-
lastStageName = stageWriter.addStage(`SELECT\n *,\n ${fields.join(
|
|
1948
|
+
lastStageName = stageWriter.addStage(`SELECT\n *,\n ${fields.join(',\n ')} \nFROM ${lastStageName}`);
|
|
1952
1949
|
const havings = new utils_1.AndChain();
|
|
1953
1950
|
for (const result of resultsWithHaving) {
|
|
1954
|
-
havings.add(`group_set IN (${result.childGroups.join(
|
|
1951
|
+
havings.add(`group_set IN (${result.childGroups.join(',')}) AND __shaving__${result.groupSet}=1`);
|
|
1955
1952
|
}
|
|
1956
1953
|
lastStageName = stageWriter.addStage(`SELECT *\nFROM ${lastStageName}\nWHERE NOT (${havings.sqlOr()})`);
|
|
1957
1954
|
}
|
|
1958
1955
|
return lastStageName;
|
|
1959
1956
|
}
|
|
1960
1957
|
generateSQLStage0(stageWriter) {
|
|
1961
|
-
let s =
|
|
1958
|
+
let s = 'SELECT\n';
|
|
1962
1959
|
let from = this.generateSQLJoins(stageWriter);
|
|
1963
1960
|
const wheres = this.generateSQLWhereTurtled();
|
|
1964
1961
|
const f = {
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1962
|
+
dimensionIndexes: [1],
|
|
1963
|
+
fieldIndex: 2,
|
|
1964
|
+
sql: ['group_set'],
|
|
1965
|
+
lateralJoinSQLExpressions: [],
|
|
1966
|
+
groupsAggregated: [],
|
|
1967
|
+
outputPipelinedSQL: [],
|
|
1971
1968
|
};
|
|
1972
1969
|
this.generateStage0Fields(this.rootResult, f, stageWriter);
|
|
1973
|
-
if (this.firstSegment.type ===
|
|
1974
|
-
throw new Error(
|
|
1970
|
+
if (this.firstSegment.type === 'project') {
|
|
1971
|
+
throw new Error('PROJECT cannot be used on queries with turtles');
|
|
1975
1972
|
}
|
|
1976
|
-
const groupBy =
|
|
1977
|
-
from += this.parent.dialect.sqlGroupSetTable(this.maxGroupSet) +
|
|
1978
|
-
s += (0, utils_1.indent)(f.sql.join(
|
|
1973
|
+
const groupBy = 'GROUP BY ' + f.dimensionIndexes.join(',') + '\n';
|
|
1974
|
+
from += this.parent.dialect.sqlGroupSetTable(this.maxGroupSet) + '\n';
|
|
1975
|
+
s += (0, utils_1.indent)(f.sql.join(',\n')) + '\n';
|
|
1979
1976
|
// this should only happen on standard SQL, BigQuery can't partition by expressions and
|
|
1980
1977
|
// aggregates.
|
|
1981
1978
|
if (f.lateralJoinSQLExpressions.length > 0) {
|
|
1982
|
-
from += `LEFT JOIN UNNEST([STRUCT(${f.lateralJoinSQLExpressions.join(
|
|
1979
|
+
from += `LEFT JOIN UNNEST([STRUCT(${f.lateralJoinSQLExpressions.join(',\n')})]) as __lateral_join_bag\n`;
|
|
1983
1980
|
}
|
|
1984
|
-
s += from + wheres + groupBy + this.rootResult.havings.sql(
|
|
1981
|
+
s += from + wheres + groupBy + this.rootResult.havings.sql('having');
|
|
1985
1982
|
// generate the stage
|
|
1986
1983
|
const resultStage = stageWriter.addStage(s);
|
|
1987
1984
|
// generate stages for havings and limits
|
|
@@ -1994,7 +1991,7 @@ class QueryQuery extends QueryField {
|
|
|
1994
1991
|
for (const [name, fi] of resultSet.allFields) {
|
|
1995
1992
|
const sqlFieldName = this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultSet.groupSet}`);
|
|
1996
1993
|
if (fi instanceof FieldInstanceField) {
|
|
1997
|
-
if (fi.fieldUsage.type ===
|
|
1994
|
+
if (fi.fieldUsage.type === 'result') {
|
|
1998
1995
|
if (isScalarField(fi.f)) {
|
|
1999
1996
|
const exp = this.caseGroup(resultSet.groupSet > 0 ? resultSet.childGroups : [], sqlFieldName);
|
|
2000
1997
|
output.sql.push(`${exp} as ${sqlFieldName}`);
|
|
@@ -2014,8 +2011,8 @@ class QueryQuery extends QueryField {
|
|
|
2014
2011
|
else if (fi.depth === depth) {
|
|
2015
2012
|
const s = this.generateTurtleSQL(fi, stageWriter, sqlFieldName, output.outputPipelinedSQL);
|
|
2016
2013
|
output.groupsAggregated.push({
|
|
2017
|
-
|
|
2018
|
-
|
|
2014
|
+
fromGroup: fi.groupSet,
|
|
2015
|
+
toGroup: resultSet.groupSet,
|
|
2019
2016
|
});
|
|
2020
2017
|
groupsToMap.push(fi.groupSet);
|
|
2021
2018
|
output.sql.push(`${s} as ${sqlFieldName}`);
|
|
@@ -2027,39 +2024,39 @@ class QueryQuery extends QueryField {
|
|
|
2027
2024
|
}
|
|
2028
2025
|
}
|
|
2029
2026
|
if (output.groupsAggregated.length > 0) {
|
|
2030
|
-
output.sql[0] =
|
|
2027
|
+
output.sql[0] = 'CASE ';
|
|
2031
2028
|
for (const m of output.groupsAggregated) {
|
|
2032
2029
|
output.sql[0] += `WHEN group_set=${m.fromGroup} THEN ${m.toGroup} `;
|
|
2033
2030
|
}
|
|
2034
|
-
output.sql[0] +=
|
|
2031
|
+
output.sql[0] += ' ELSE group_set END as group_set';
|
|
2035
2032
|
}
|
|
2036
2033
|
}
|
|
2037
2034
|
generateSQLDepthN(depth, stageWriter, stageName) {
|
|
2038
|
-
let s =
|
|
2035
|
+
let s = 'SELECT \n';
|
|
2039
2036
|
const f = {
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2037
|
+
dimensionIndexes: [1],
|
|
2038
|
+
fieldIndex: 2,
|
|
2039
|
+
sql: ['group_set'],
|
|
2040
|
+
lateralJoinSQLExpressions: [],
|
|
2041
|
+
groupsAggregated: [],
|
|
2042
|
+
outputPipelinedSQL: [],
|
|
2046
2043
|
};
|
|
2047
2044
|
this.generateDepthNFields(depth, this.rootResult, f, stageWriter);
|
|
2048
|
-
s += (0, utils_1.indent)(f.sql.join(
|
|
2045
|
+
s += (0, utils_1.indent)(f.sql.join(',\n')) + '\n';
|
|
2049
2046
|
s += `FROM ${stageName}\n`;
|
|
2050
2047
|
const where = this.rootResult.eliminateComputeGroupsSQL();
|
|
2051
2048
|
if (where.length > 0) {
|
|
2052
2049
|
s += `WHERE ${where}\n`;
|
|
2053
2050
|
}
|
|
2054
2051
|
if (f.dimensionIndexes.length > 0) {
|
|
2055
|
-
s += `GROUP BY ${f.dimensionIndexes.join(
|
|
2052
|
+
s += `GROUP BY ${f.dimensionIndexes.join(',')}\n`;
|
|
2056
2053
|
}
|
|
2057
2054
|
this.resultStage = stageWriter.addStage(s);
|
|
2058
2055
|
this.resultStage = this.generatePipelinedStages(f.outputPipelinedSQL, this.resultStage, stageWriter);
|
|
2059
2056
|
return this.resultStage;
|
|
2060
2057
|
}
|
|
2061
2058
|
genereateSQLCombineTurtles(stageWriter, stage0Name) {
|
|
2062
|
-
let s =
|
|
2059
|
+
let s = 'SELECT\n';
|
|
2063
2060
|
const fieldsSQL = [];
|
|
2064
2061
|
let fieldIndex = 1;
|
|
2065
2062
|
const outputPipelinedSQL = [];
|
|
@@ -2067,7 +2064,7 @@ class QueryQuery extends QueryField {
|
|
|
2067
2064
|
for (const [name, fi] of this.rootResult.allFields) {
|
|
2068
2065
|
const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
|
|
2069
2066
|
if (fi instanceof FieldInstanceField) {
|
|
2070
|
-
if (fi.fieldUsage.type ===
|
|
2067
|
+
if (fi.fieldUsage.type === 'result') {
|
|
2071
2068
|
if (isScalarField(fi.f)) {
|
|
2072
2069
|
fieldsSQL.push(this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
|
|
2073
2070
|
dimensionIndexes.push(fieldIndex++);
|
|
@@ -2079,23 +2076,23 @@ class QueryQuery extends QueryField {
|
|
|
2079
2076
|
}
|
|
2080
2077
|
}
|
|
2081
2078
|
else if (fi instanceof FieldInstanceResult) {
|
|
2082
|
-
if (fi.firstSegment.type ===
|
|
2079
|
+
if (fi.firstSegment.type === 'reduce') {
|
|
2083
2080
|
fieldsSQL.push(`${this.generateTurtleSQL(fi, stageWriter, sqlName, outputPipelinedSQL)} as ${sqlName}`);
|
|
2084
2081
|
fieldIndex++;
|
|
2085
2082
|
}
|
|
2086
|
-
else if (fi.firstSegment.type ===
|
|
2083
|
+
else if (fi.firstSegment.type === 'project') {
|
|
2087
2084
|
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(name, this.rootResult.groupSet, sqlName));
|
|
2088
2085
|
fieldIndex++;
|
|
2089
2086
|
}
|
|
2090
2087
|
}
|
|
2091
2088
|
}
|
|
2092
|
-
s += (0, utils_1.indent)(fieldsSQL.join(
|
|
2089
|
+
s += (0, utils_1.indent)(fieldsSQL.join(',\n')) + `\nFROM ${stage0Name}\n`;
|
|
2093
2090
|
const where = this.rootResult.eliminateComputeGroupsSQL();
|
|
2094
2091
|
if (where.length > 0) {
|
|
2095
2092
|
s += `WHERE ${where}\n`;
|
|
2096
2093
|
}
|
|
2097
2094
|
if (dimensionIndexes.length > 0) {
|
|
2098
|
-
s += `GROUP BY ${dimensionIndexes.join(
|
|
2095
|
+
s += `GROUP BY ${dimensionIndexes.join(',')}\n`;
|
|
2099
2096
|
}
|
|
2100
2097
|
// order by
|
|
2101
2098
|
s += this.genereateSQLOrderBy(this.firstSegment, this.rootResult);
|
|
@@ -2110,7 +2107,7 @@ class QueryQuery extends QueryField {
|
|
|
2110
2107
|
generateTurtleSQL(resultStruct, stageWriter, sqlFieldName, outputPipelinedSQL) {
|
|
2111
2108
|
// let fieldsSQL: string[] = [];
|
|
2112
2109
|
const dialectFieldList = [];
|
|
2113
|
-
let orderBy =
|
|
2110
|
+
let orderBy = '';
|
|
2114
2111
|
const limit = resultStruct.firstSegment.limit;
|
|
2115
2112
|
// calculate the ordering.
|
|
2116
2113
|
const obSQL = [];
|
|
@@ -2118,61 +2115,61 @@ class QueryQuery extends QueryField {
|
|
|
2118
2115
|
const orderByDef = resultStruct.firstSegment.orderBy ||
|
|
2119
2116
|
resultStruct.calculateDefaultOrderBy();
|
|
2120
2117
|
for (const ordering of orderByDef) {
|
|
2121
|
-
if (typeof ordering.field ===
|
|
2118
|
+
if (typeof ordering.field === 'string') {
|
|
2122
2119
|
orderingField = {
|
|
2123
|
-
|
|
2124
|
-
|
|
2120
|
+
name: ordering.field,
|
|
2121
|
+
fif: resultStruct.getField(ordering.field),
|
|
2125
2122
|
};
|
|
2126
2123
|
}
|
|
2127
2124
|
else {
|
|
2128
2125
|
orderingField = resultStruct.getFieldByNumber(ordering.field);
|
|
2129
2126
|
}
|
|
2130
|
-
if (resultStruct.firstSegment.type ===
|
|
2131
|
-
obSQL.push(
|
|
2127
|
+
if (resultStruct.firstSegment.type === 'reduce') {
|
|
2128
|
+
obSQL.push(' ' +
|
|
2132
2129
|
this.parent.dialect.sqlMaybeQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`) +
|
|
2133
|
-
` ${ordering.dir ||
|
|
2130
|
+
` ${ordering.dir || 'ASC'}`);
|
|
2134
2131
|
}
|
|
2135
|
-
else if (resultStruct.firstSegment.type ===
|
|
2136
|
-
obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir ||
|
|
2132
|
+
else if (resultStruct.firstSegment.type === 'project') {
|
|
2133
|
+
obSQL.push(` ${orderingField.fif.f.generateExpression(resultStruct)} ${ordering.dir || 'ASC'}`);
|
|
2137
2134
|
}
|
|
2138
2135
|
}
|
|
2139
2136
|
if (obSQL.length > 0) {
|
|
2140
|
-
orderBy =
|
|
2137
|
+
orderBy = ' ' + this.parent.dialect.sqlOrderBy(obSQL);
|
|
2141
2138
|
}
|
|
2142
2139
|
for (const [name, field] of resultStruct.allFields) {
|
|
2143
2140
|
const sqlName = this.parent.dialect.sqlMaybeQuoteIdentifier(name);
|
|
2144
2141
|
//
|
|
2145
|
-
if (resultStruct.firstSegment.type ===
|
|
2142
|
+
if (resultStruct.firstSegment.type === 'reduce' &&
|
|
2146
2143
|
(field instanceof FieldInstanceResult ||
|
|
2147
2144
|
(field instanceof FieldInstanceField &&
|
|
2148
|
-
field.fieldUsage.type ===
|
|
2145
|
+
field.fieldUsage.type === 'result'))) {
|
|
2149
2146
|
// fieldsSQL.push(`${name}__${resultStruct.groupSet} as ${sqlName}`);
|
|
2150
2147
|
// outputFieldNames.push(name);
|
|
2151
2148
|
dialectFieldList.push({
|
|
2152
|
-
|
|
2149
|
+
type: field instanceof FieldInstanceField
|
|
2153
2150
|
? field.f.fieldDef.type
|
|
2154
|
-
:
|
|
2155
|
-
|
|
2156
|
-
|
|
2151
|
+
: 'struct',
|
|
2152
|
+
sqlExpression: this.parent.dialect.sqlMaybeQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
2153
|
+
sqlOutputName: sqlName,
|
|
2157
2154
|
});
|
|
2158
2155
|
}
|
|
2159
|
-
else if (resultStruct.firstSegment.type ===
|
|
2156
|
+
else if (resultStruct.firstSegment.type === 'project' &&
|
|
2160
2157
|
field instanceof FieldInstanceField &&
|
|
2161
|
-
field.fieldUsage.type ===
|
|
2158
|
+
field.fieldUsage.type === 'result') {
|
|
2162
2159
|
// fieldsSQL.push(
|
|
2163
2160
|
// `${field.f.generateExpression(resultStruct)} as ${sqlName}`
|
|
2164
2161
|
// );
|
|
2165
2162
|
dialectFieldList.push({
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2163
|
+
type: field.type,
|
|
2164
|
+
sqlExpression: field.f.generateExpression(resultStruct),
|
|
2165
|
+
sqlOutputName: sqlName,
|
|
2169
2166
|
});
|
|
2170
2167
|
}
|
|
2171
2168
|
}
|
|
2172
2169
|
let resultType;
|
|
2173
2170
|
let ret;
|
|
2174
|
-
if ((resultType = resultStruct.getRepeatedResultType()) !==
|
|
2175
|
-
if (resultType ===
|
|
2171
|
+
if ((resultType = resultStruct.getRepeatedResultType()) !== 'nested') {
|
|
2172
|
+
if (resultType === 'inline_all_numbers') {
|
|
2176
2173
|
ret = this.parent.dialect.sqlCoaleseMeasuresInline(resultStruct.groupSet, dialectFieldList);
|
|
2177
2174
|
}
|
|
2178
2175
|
else {
|
|
@@ -2194,7 +2191,7 @@ class QueryQuery extends QueryField {
|
|
|
2194
2191
|
else {
|
|
2195
2192
|
outputPipelinedSQL.push({
|
|
2196
2193
|
sqlFieldName,
|
|
2197
|
-
|
|
2194
|
+
pipelineSQL: `(${sql})`,
|
|
2198
2195
|
});
|
|
2199
2196
|
}
|
|
2200
2197
|
}
|
|
@@ -2212,14 +2209,14 @@ class QueryQuery extends QueryField {
|
|
|
2212
2209
|
const pipeline = [...fi.turtleDef.pipeline];
|
|
2213
2210
|
pipeline.shift();
|
|
2214
2211
|
const newTurtle = {
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
pipeline
|
|
2212
|
+
type: 'turtle',
|
|
2213
|
+
name: 'starthere',
|
|
2214
|
+
pipeline,
|
|
2218
2215
|
};
|
|
2219
|
-
structDef.name = this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType ===
|
|
2220
|
-
structDef.structSource = {
|
|
2216
|
+
structDef.name = this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === 'inline_all_numbers', sourceSQLExpression);
|
|
2217
|
+
structDef.structSource = { type: 'sql', method: 'nested' };
|
|
2221
2218
|
const qs = new QueryStruct(structDef, {
|
|
2222
|
-
|
|
2219
|
+
model: this.parent.getModel(),
|
|
2223
2220
|
});
|
|
2224
2221
|
const q = QueryQuery.makeQuery(newTurtle, qs, stageWriter);
|
|
2225
2222
|
pipeOut = q.generateSQLFromPipeline(stageWriter);
|
|
@@ -2228,7 +2225,7 @@ class QueryQuery extends QueryField {
|
|
|
2228
2225
|
}
|
|
2229
2226
|
return {
|
|
2230
2227
|
structDef,
|
|
2231
|
-
pipeOut
|
|
2228
|
+
pipeOut,
|
|
2232
2229
|
};
|
|
2233
2230
|
}
|
|
2234
2231
|
generateComplexSQL(stageWriter) {
|
|
@@ -2259,7 +2256,7 @@ class QueryQuery extends QueryField {
|
|
|
2259
2256
|
}
|
|
2260
2257
|
toMalloy() {
|
|
2261
2258
|
let ret = `EXPLORE ${(0, malloy_types_1.getIdentifier)(this.parent.fieldDef)} | `;
|
|
2262
|
-
ret += this.fieldDef.type.toUpperCase() +
|
|
2259
|
+
ret += this.fieldDef.type.toUpperCase() + ' ';
|
|
2263
2260
|
return ret;
|
|
2264
2261
|
}
|
|
2265
2262
|
generateSQLFromPipeline(stageWriter) {
|
|
@@ -2271,20 +2268,20 @@ class QueryQuery extends QueryField {
|
|
|
2271
2268
|
const pipeline = [...this.fieldDef.pipeline];
|
|
2272
2269
|
let structDef = {
|
|
2273
2270
|
...outputStruct,
|
|
2274
|
-
|
|
2271
|
+
structSource: { type: 'sql', method: 'lastStage' },
|
|
2275
2272
|
};
|
|
2276
2273
|
pipeline.shift();
|
|
2277
2274
|
for (const transform of pipeline) {
|
|
2278
2275
|
const s = new QueryStruct(structDef, {
|
|
2279
|
-
|
|
2276
|
+
model: this.parent.getModel(),
|
|
2280
2277
|
});
|
|
2281
|
-
const q = QueryQuery.makeQuery({
|
|
2278
|
+
const q = QueryQuery.makeQuery({ type: 'turtle', name: 'ignoreme', pipeline: [transform] }, s, stageWriter);
|
|
2282
2279
|
q.prepare(stageWriter);
|
|
2283
2280
|
lastStageName = q.generateSQL(stageWriter);
|
|
2284
2281
|
outputStruct = q.getResultStructDef();
|
|
2285
2282
|
structDef = {
|
|
2286
2283
|
...outputStruct,
|
|
2287
|
-
|
|
2284
|
+
structSource: { type: 'sql', method: 'lastStage' },
|
|
2288
2285
|
};
|
|
2289
2286
|
}
|
|
2290
2287
|
}
|
|
@@ -2306,7 +2303,7 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
2306
2303
|
// get a field ref and expand it.
|
|
2307
2304
|
expandField(f) {
|
|
2308
2305
|
const field = this.parent.getFieldByName(f);
|
|
2309
|
-
return {
|
|
2306
|
+
return { as: f, field };
|
|
2310
2307
|
}
|
|
2311
2308
|
expandFields(resultStruct) {
|
|
2312
2309
|
let resultIndex = 1;
|
|
@@ -2317,7 +2314,7 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
2317
2314
|
const { as, field } = this.expandField(f);
|
|
2318
2315
|
resultStruct.addField(as, field, {
|
|
2319
2316
|
resultIndex,
|
|
2320
|
-
|
|
2317
|
+
type: 'result',
|
|
2321
2318
|
});
|
|
2322
2319
|
if (field instanceof QueryAtomicField) {
|
|
2323
2320
|
this.addDependancies(resultStruct, field);
|
|
@@ -2329,19 +2326,19 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
2329
2326
|
const f = this.parent.getFieldByName(measure);
|
|
2330
2327
|
resultStruct.addField(measure, f, {
|
|
2331
2328
|
resultIndex,
|
|
2332
|
-
|
|
2329
|
+
type: 'result',
|
|
2333
2330
|
});
|
|
2334
2331
|
this.addDependancies(resultStruct, f);
|
|
2335
2332
|
}
|
|
2336
2333
|
this.expandFilters(resultStruct);
|
|
2337
2334
|
}
|
|
2338
2335
|
generateSQL(stageWriter) {
|
|
2339
|
-
let measureSQL =
|
|
2336
|
+
let measureSQL = 'COUNT(*)';
|
|
2340
2337
|
const dialect = this.parent.dialect;
|
|
2341
|
-
const fieldNameColumn = dialect.sqlMaybeQuoteIdentifier(
|
|
2342
|
-
const fieldValueColumn = dialect.sqlMaybeQuoteIdentifier(
|
|
2343
|
-
const fieldTypeColumn = dialect.sqlMaybeQuoteIdentifier(
|
|
2344
|
-
const fieldRangeColumn = dialect.sqlMaybeQuoteIdentifier(
|
|
2338
|
+
const fieldNameColumn = dialect.sqlMaybeQuoteIdentifier('fieldName');
|
|
2339
|
+
const fieldValueColumn = dialect.sqlMaybeQuoteIdentifier('fieldValue');
|
|
2340
|
+
const fieldTypeColumn = dialect.sqlMaybeQuoteIdentifier('fieldType');
|
|
2341
|
+
const fieldRangeColumn = dialect.sqlMaybeQuoteIdentifier('fieldRange');
|
|
2345
2342
|
const measureName = this.firstSegment.weightMeasure;
|
|
2346
2343
|
if (measureName) {
|
|
2347
2344
|
measureSQL = this.rootResult
|
|
@@ -2351,37 +2348,37 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
2351
2348
|
const fields = [];
|
|
2352
2349
|
for (const [name, field] of this.rootResult.allFields) {
|
|
2353
2350
|
const fi = field;
|
|
2354
|
-
if (fi.fieldUsage.type ===
|
|
2351
|
+
if (fi.fieldUsage.type === 'result' && isScalarField(fi.f)) {
|
|
2355
2352
|
const expression = fi.f.generateExpression(this.rootResult);
|
|
2356
|
-
fields.push({ name,
|
|
2353
|
+
fields.push({ name, type: fi.f.fieldDef.type, expression });
|
|
2357
2354
|
}
|
|
2358
2355
|
}
|
|
2359
|
-
let s =
|
|
2360
|
-
s +=
|
|
2356
|
+
let s = 'SELECT\n group_set,\n';
|
|
2357
|
+
s += ' CASE group_set\n';
|
|
2361
2358
|
for (let i = 0; i < fields.length; i++) {
|
|
2362
2359
|
s += ` WHEN ${i} THEN '${fields[i].name}'\n`;
|
|
2363
2360
|
}
|
|
2364
2361
|
s += ` END as ${fieldNameColumn},`;
|
|
2365
|
-
s +=
|
|
2362
|
+
s += ' CASE group_set\n';
|
|
2366
2363
|
for (let i = 0; i < fields.length; i++) {
|
|
2367
2364
|
s += ` WHEN ${i} THEN '${fields[i].type}'\n`;
|
|
2368
2365
|
}
|
|
2369
2366
|
s += ` END as ${fieldTypeColumn},`;
|
|
2370
|
-
s +=
|
|
2367
|
+
s += ' CASE group_set WHEN 99999 THEN NULL\n';
|
|
2371
2368
|
for (let i = 0; i < fields.length; i++) {
|
|
2372
|
-
if (fields[i].type ===
|
|
2369
|
+
if (fields[i].type === 'string') {
|
|
2373
2370
|
s += ` WHEN ${i} THEN ${fields[i].expression}\n`;
|
|
2374
2371
|
}
|
|
2375
2372
|
}
|
|
2376
2373
|
s += ` END as ${fieldValueColumn},\n`;
|
|
2377
2374
|
s += ` ${measureSQL} as weight,\n`;
|
|
2378
2375
|
// just in case we don't have any field types, force the case statement to have at least one value.
|
|
2379
|
-
s +=
|
|
2376
|
+
s += " CASE group_set\n WHEN 99999 THEN ''";
|
|
2380
2377
|
for (let i = 0; i < fields.length; i++) {
|
|
2381
|
-
if (fields[i].type ===
|
|
2378
|
+
if (fields[i].type === 'number') {
|
|
2382
2379
|
s += ` WHEN ${i} THEN CAST(MIN(${fields[i].expression}) AS ${dialect.stringTypeName}) || ' to ' || CAST(MAX(${fields[i].expression}) AS ${dialect.stringTypeName})\n`;
|
|
2383
2380
|
}
|
|
2384
|
-
if (fields[i].type ===
|
|
2381
|
+
if (fields[i].type === 'timestamp' || fields[i].type === 'date') {
|
|
2385
2382
|
s += ` WHEN ${i} THEN MIN(${dialect.sqlDateToString(fields[i].expression)}) || ' to ' || MAX(${dialect.sqlDateToString(fields[i].expression)})\n`;
|
|
2386
2383
|
}
|
|
2387
2384
|
}
|
|
@@ -2394,9 +2391,9 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
2394
2391
|
// ELSE NULL
|
|
2395
2392
|
// END as field_range\n`;
|
|
2396
2393
|
s += this.generateSQLJoins(stageWriter);
|
|
2397
|
-
s += dialect.sqlGroupSetTable(fields.length) +
|
|
2398
|
-
s += this.generateSQLFilters(this.rootResult,
|
|
2399
|
-
s +=
|
|
2394
|
+
s += dialect.sqlGroupSetTable(fields.length) + '\n';
|
|
2395
|
+
s += this.generateSQLFilters(this.rootResult, 'where').sql('where');
|
|
2396
|
+
s += 'GROUP BY 1,2,3,4\n';
|
|
2400
2397
|
// limit
|
|
2401
2398
|
if (this.firstSegment.limit) {
|
|
2402
2399
|
s += `LIMIT ${this.firstSegment.limit}\n`;
|
|
@@ -2425,8 +2422,8 @@ class QueryQueryIndex extends QueryQuery {
|
|
|
2425
2422
|
findFanPrefexes(qs) {
|
|
2426
2423
|
for (const [_name, f] of qs.nameMap) {
|
|
2427
2424
|
if (f instanceof QueryStruct &&
|
|
2428
|
-
(f.fieldDef.structRelationship.type ===
|
|
2429
|
-
f.fieldDef.structRelationship.type ===
|
|
2425
|
+
(f.fieldDef.structRelationship.type === 'many' ||
|
|
2426
|
+
f.fieldDef.structRelationship.type === 'nested') &&
|
|
2430
2427
|
f.fieldDef.fields.length > 1 && // leave arrays in parent.
|
|
2431
2428
|
this.parent.dialect.dontUnionIndex === false) {
|
|
2432
2429
|
const key = f.getFullOutputName();
|
|
@@ -2439,9 +2436,9 @@ class QueryQueryIndex extends QueryQuery {
|
|
|
2439
2436
|
// if no fields were specified, look in the parent struct for strings.
|
|
2440
2437
|
let fieldNames = this.firstSegment.fields || [];
|
|
2441
2438
|
if (fieldNames.length === 0) {
|
|
2442
|
-
fieldNames.push(
|
|
2439
|
+
fieldNames.push('**');
|
|
2443
2440
|
}
|
|
2444
|
-
fieldNames = this.expandWildCards(fieldNames,
|
|
2441
|
+
fieldNames = this.expandWildCards(fieldNames, qf => ['string', 'number', 'timestamp', 'date'].indexOf(qf.fieldDef.type) !==
|
|
2445
2442
|
-1);
|
|
2446
2443
|
return fieldNames;
|
|
2447
2444
|
}
|
|
@@ -2495,12 +2492,12 @@ class QueryQueryIndex extends QueryQuery {
|
|
|
2495
2492
|
for (const fields of stages) {
|
|
2496
2493
|
const q = new QueryQueryIndexStage({
|
|
2497
2494
|
...this.fieldDef,
|
|
2498
|
-
|
|
2495
|
+
pipeline: [
|
|
2499
2496
|
{
|
|
2500
2497
|
...this.fieldDef.pipeline[0],
|
|
2501
|
-
|
|
2502
|
-
}
|
|
2503
|
-
]
|
|
2498
|
+
fields: fields,
|
|
2499
|
+
},
|
|
2500
|
+
],
|
|
2504
2501
|
}, this.parent, stageWriter);
|
|
2505
2502
|
q.prepare(stageWriter);
|
|
2506
2503
|
const lastStageName = q.generateSQL(stageWriter);
|
|
@@ -2510,29 +2507,27 @@ class QueryQueryIndex extends QueryQuery {
|
|
|
2510
2507
|
this.resultStage = outputStageNames[0];
|
|
2511
2508
|
}
|
|
2512
2509
|
else {
|
|
2513
|
-
this.resultStage = stageWriter.addStage(outputStageNames
|
|
2514
|
-
.map((n) => `SELECT * FROM ${n}\n`)
|
|
2515
|
-
.join(" UNION ALL \n"));
|
|
2510
|
+
this.resultStage = stageWriter.addStage(outputStageNames.map(n => `SELECT * FROM ${n}\n`).join(' UNION ALL \n'));
|
|
2516
2511
|
}
|
|
2517
2512
|
return this.resultStage;
|
|
2518
2513
|
}
|
|
2519
2514
|
/** All Indexes have the same output schema */
|
|
2520
2515
|
getResultStructDef() {
|
|
2521
2516
|
return {
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
{
|
|
2527
|
-
{
|
|
2528
|
-
{
|
|
2529
|
-
{
|
|
2517
|
+
type: 'struct',
|
|
2518
|
+
name: this.resultStage || 'result',
|
|
2519
|
+
dialect: this.parent.fieldDef.dialect,
|
|
2520
|
+
fields: [
|
|
2521
|
+
{ type: 'string', name: 'fieldName' },
|
|
2522
|
+
{ type: 'string', name: 'fieldValue' },
|
|
2523
|
+
{ type: 'string', name: 'fieldType' },
|
|
2524
|
+
{ type: 'number', name: 'weight', numberType: 'integer' },
|
|
2530
2525
|
],
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2526
|
+
structRelationship: {
|
|
2527
|
+
type: 'basetable',
|
|
2528
|
+
connectionName: this.parent.connectionName,
|
|
2534
2529
|
},
|
|
2535
|
-
|
|
2530
|
+
structSource: { type: 'query_result' },
|
|
2536
2531
|
};
|
|
2537
2532
|
}
|
|
2538
2533
|
}
|
|
@@ -2542,14 +2537,14 @@ class QueryStruct extends QueryNode {
|
|
|
2542
2537
|
super(fieldDef);
|
|
2543
2538
|
this.nameMap = new Map();
|
|
2544
2539
|
this.setParent(parent);
|
|
2545
|
-
if (
|
|
2540
|
+
if ('model' in parent) {
|
|
2546
2541
|
this.model = parent.model;
|
|
2547
2542
|
this.pathAliasMap = new Map();
|
|
2548
|
-
if (fieldDef.structRelationship.type ===
|
|
2543
|
+
if (fieldDef.structRelationship.type === 'basetable') {
|
|
2549
2544
|
this.connectionName = fieldDef.structRelationship.connectionName;
|
|
2550
2545
|
}
|
|
2551
2546
|
else {
|
|
2552
|
-
throw new Error(
|
|
2547
|
+
throw new Error('All root StructDefs should be a baseTable');
|
|
2553
2548
|
}
|
|
2554
2549
|
}
|
|
2555
2550
|
else {
|
|
@@ -2569,14 +2564,14 @@ class QueryStruct extends QueryNode {
|
|
|
2569
2564
|
for (const field of fields) {
|
|
2570
2565
|
const as = (0, malloy_types_1.getIdentifier)(field);
|
|
2571
2566
|
switch (field.type) {
|
|
2572
|
-
case
|
|
2567
|
+
case 'struct': {
|
|
2573
2568
|
this.addFieldToNameMap(as, new QueryStruct(field, {
|
|
2574
|
-
|
|
2569
|
+
struct: this,
|
|
2575
2570
|
}));
|
|
2576
2571
|
break;
|
|
2577
2572
|
}
|
|
2578
2573
|
// case "reduce" || "project" || "index": {
|
|
2579
|
-
case
|
|
2574
|
+
case 'turtle': {
|
|
2580
2575
|
// not sure why we need to cast here...
|
|
2581
2576
|
this.addFieldToNameMap(as, QueryQuery.makeQuery(field, this));
|
|
2582
2577
|
break;
|
|
@@ -2587,8 +2582,8 @@ class QueryStruct extends QueryNode {
|
|
|
2587
2582
|
}
|
|
2588
2583
|
}
|
|
2589
2584
|
// if we don't have distinct key yet for this struct, add it.
|
|
2590
|
-
if (!this.nameMap.has(
|
|
2591
|
-
this.addFieldToNameMap(
|
|
2585
|
+
if (!this.nameMap.has('__distinct_key')) {
|
|
2586
|
+
this.addFieldToNameMap('__distinct_key', new QueryFieldDistinctKey({ type: 'string', name: '__distinct_key' }, this));
|
|
2592
2587
|
}
|
|
2593
2588
|
}
|
|
2594
2589
|
// generate unique string for the alias.
|
|
@@ -2612,7 +2607,7 @@ class QueryStruct extends QueryNode {
|
|
|
2612
2607
|
return name;
|
|
2613
2608
|
}
|
|
2614
2609
|
else {
|
|
2615
|
-
throw new Error(
|
|
2610
|
+
throw new Error('Internal Error: cannot create unique alias name');
|
|
2616
2611
|
}
|
|
2617
2612
|
// get the malloy name for this struct (will include a trailing dot)
|
|
2618
2613
|
// return this.getFullOutputName().replace(/\.$/, "").replace(/\./g, "_o_");
|
|
@@ -2629,28 +2624,28 @@ class QueryStruct extends QueryNode {
|
|
|
2629
2624
|
}
|
|
2630
2625
|
const pkField = this.getPrimaryKeyField(this.fieldDef);
|
|
2631
2626
|
const pkType = pkField.fieldDef.type;
|
|
2632
|
-
if (pkType !==
|
|
2627
|
+
if (pkType !== 'string' && pkType !== 'number') {
|
|
2633
2628
|
throw new Error(`Unknown Primary key data type for ${pkField.fieldDef.name}`);
|
|
2634
2629
|
}
|
|
2635
2630
|
const aliasName = (0, malloy_types_1.getIdentifier)(this.fieldDef);
|
|
2636
2631
|
const pkName = this.fieldDef.primaryKey;
|
|
2637
2632
|
const fieldDef = {
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2633
|
+
type: pkType,
|
|
2634
|
+
name: `${aliasName}_id`,
|
|
2635
|
+
e: [
|
|
2641
2636
|
{
|
|
2642
|
-
|
|
2637
|
+
type: 'field',
|
|
2643
2638
|
// path: pkField.getFullOutputName(),
|
|
2644
|
-
|
|
2645
|
-
}
|
|
2646
|
-
]
|
|
2639
|
+
path: pkField.getIdentifier(),
|
|
2640
|
+
},
|
|
2641
|
+
],
|
|
2647
2642
|
};
|
|
2648
2643
|
return new QueryFieldStruct(fieldDef, this, `${aliasName}.${pkName}`);
|
|
2649
2644
|
}
|
|
2650
2645
|
getSQLIdentifier() {
|
|
2651
2646
|
if (this.unnestWithNumbers() && this.parent !== undefined) {
|
|
2652
2647
|
const x = this.parent.getSQLIdentifier() +
|
|
2653
|
-
|
|
2648
|
+
'.' +
|
|
2654
2649
|
(0, malloy_types_1.getIdentifier)(this.fieldDef) +
|
|
2655
2650
|
`[${this.getIdentifier()}.__row_id]`;
|
|
2656
2651
|
return x;
|
|
@@ -2662,17 +2657,17 @@ class QueryStruct extends QueryNode {
|
|
|
2662
2657
|
// return the name of the field in SQL
|
|
2663
2658
|
getIdentifier() {
|
|
2664
2659
|
// if it is the root table, use provided alias if we have one.
|
|
2665
|
-
if (this.fieldDef.structRelationship.type ===
|
|
2660
|
+
if (this.fieldDef.structRelationship.type === 'basetable') {
|
|
2666
2661
|
if (this.fieldDef.as === undefined) {
|
|
2667
|
-
return
|
|
2662
|
+
return 'base';
|
|
2668
2663
|
}
|
|
2669
2664
|
else {
|
|
2670
2665
|
return super.getIdentifier();
|
|
2671
2666
|
}
|
|
2672
2667
|
}
|
|
2673
2668
|
// if this is an inline object, include the parents alias.
|
|
2674
|
-
if (this.fieldDef.structRelationship.type ===
|
|
2675
|
-
return this.parent.getSQLIdentifier() +
|
|
2669
|
+
if (this.fieldDef.structRelationship.type === 'inline' && this.parent) {
|
|
2670
|
+
return this.parent.getSQLIdentifier() + '.' + super.getIdentifier();
|
|
2676
2671
|
}
|
|
2677
2672
|
// we are somewhere in the join tree. Make sure the alias is unique.
|
|
2678
2673
|
return this.getAliasIdentifier();
|
|
@@ -2680,10 +2675,10 @@ class QueryStruct extends QueryNode {
|
|
|
2680
2675
|
// return the name of the field in Malloy
|
|
2681
2676
|
getFullOutputName() {
|
|
2682
2677
|
if (this.parent) {
|
|
2683
|
-
return (this.parent.getFullOutputName() + (0, malloy_types_1.getIdentifier)(this.fieldDef) +
|
|
2678
|
+
return (this.parent.getFullOutputName() + (0, malloy_types_1.getIdentifier)(this.fieldDef) + '.');
|
|
2684
2679
|
}
|
|
2685
2680
|
else {
|
|
2686
|
-
return
|
|
2681
|
+
return '';
|
|
2687
2682
|
}
|
|
2688
2683
|
}
|
|
2689
2684
|
needsSymetricCalculation(resultSet) {
|
|
@@ -2696,16 +2691,16 @@ class QueryStruct extends QueryNode {
|
|
|
2696
2691
|
}
|
|
2697
2692
|
unnestWithNumbers() {
|
|
2698
2693
|
return (this.dialect.unnestWithNumbers &&
|
|
2699
|
-
this.fieldDef.structRelationship.type ===
|
|
2694
|
+
this.fieldDef.structRelationship.type === 'nested');
|
|
2700
2695
|
}
|
|
2701
2696
|
getJoinableParent() {
|
|
2702
2697
|
// if it is inline it should always have a parent
|
|
2703
|
-
if (this.fieldDef.structRelationship.type ===
|
|
2698
|
+
if (this.fieldDef.structRelationship.type === 'inline') {
|
|
2704
2699
|
if (this.parent) {
|
|
2705
2700
|
return this.parent.getJoinableParent();
|
|
2706
2701
|
}
|
|
2707
2702
|
else {
|
|
2708
|
-
throw new Error(
|
|
2703
|
+
throw new Error('Internal Error: inline struct cannot be root');
|
|
2709
2704
|
}
|
|
2710
2705
|
}
|
|
2711
2706
|
return this;
|
|
@@ -2732,7 +2727,7 @@ class QueryStruct extends QueryNode {
|
|
|
2732
2727
|
* they exist) to the structure.
|
|
2733
2728
|
*/
|
|
2734
2729
|
resolveQueryFields() {
|
|
2735
|
-
if (this.fieldDef.structSource.type ===
|
|
2730
|
+
if (this.fieldDef.structSource.type === 'query') {
|
|
2736
2731
|
const structDef = this.model
|
|
2737
2732
|
.loadQuery(this.fieldDef.structSource.query, undefined)
|
|
2738
2733
|
.structs.pop();
|
|
@@ -2765,16 +2760,16 @@ class QueryStruct extends QueryNode {
|
|
|
2765
2760
|
}
|
|
2766
2761
|
else {
|
|
2767
2762
|
if (this.parent === undefined) {
|
|
2768
|
-
throw new Error(
|
|
2763
|
+
throw new Error('Expected this query struct to have a parent, as no model was present.');
|
|
2769
2764
|
}
|
|
2770
2765
|
return this.parent.getModel();
|
|
2771
2766
|
}
|
|
2772
2767
|
}
|
|
2773
2768
|
setParent(parent) {
|
|
2774
|
-
if (
|
|
2769
|
+
if ('struct' in parent) {
|
|
2775
2770
|
this.parent = parent.struct;
|
|
2776
2771
|
}
|
|
2777
|
-
if (
|
|
2772
|
+
if ('model' in parent) {
|
|
2778
2773
|
this.model = parent.model;
|
|
2779
2774
|
}
|
|
2780
2775
|
else {
|
|
@@ -2784,24 +2779,24 @@ class QueryStruct extends QueryNode {
|
|
|
2784
2779
|
/** makes a new queryable field object from a fieldDef */
|
|
2785
2780
|
makeQueryField(field) {
|
|
2786
2781
|
switch (field.type) {
|
|
2787
|
-
case
|
|
2782
|
+
case 'string':
|
|
2788
2783
|
return new QueryFieldString(field, this);
|
|
2789
|
-
case
|
|
2784
|
+
case 'date':
|
|
2790
2785
|
return new QueryFieldDate(field, this);
|
|
2791
|
-
case
|
|
2786
|
+
case 'timestamp':
|
|
2792
2787
|
return new QueryFieldTimestamp(field, this);
|
|
2793
|
-
case
|
|
2788
|
+
case 'number':
|
|
2794
2789
|
return new QueryFieldNumber(field, this);
|
|
2795
|
-
case
|
|
2790
|
+
case 'boolean':
|
|
2796
2791
|
return new QueryFieldBoolean(field, this);
|
|
2797
|
-
case
|
|
2792
|
+
case 'json':
|
|
2798
2793
|
return new QueryFieldJSON(field, this);
|
|
2799
|
-
case
|
|
2794
|
+
case 'unsupported':
|
|
2800
2795
|
return new QueryFieldUnsupported(field, this);
|
|
2801
2796
|
// case "reduce":
|
|
2802
2797
|
// case "project":
|
|
2803
2798
|
// case "index":
|
|
2804
|
-
case
|
|
2799
|
+
case 'turtle':
|
|
2805
2800
|
return new QueryTurtle(field, this);
|
|
2806
2801
|
default:
|
|
2807
2802
|
throw new Error(`unknown field definition ${JSON.stringify(field)}`);
|
|
@@ -2811,7 +2806,7 @@ class QueryStruct extends QueryNode {
|
|
|
2811
2806
|
* return a field if it exists, make one if we are passed a field definition.
|
|
2812
2807
|
*/
|
|
2813
2808
|
getOrMakeField(fieldRef) {
|
|
2814
|
-
if (typeof fieldRef ===
|
|
2809
|
+
if (typeof fieldRef === 'string') {
|
|
2815
2810
|
return this.getFieldByName(fieldRef);
|
|
2816
2811
|
}
|
|
2817
2812
|
else {
|
|
@@ -2830,31 +2825,31 @@ class QueryStruct extends QueryNode {
|
|
|
2830
2825
|
}
|
|
2831
2826
|
structSourceSQL(stageWriter) {
|
|
2832
2827
|
switch (this.fieldDef.structSource.type) {
|
|
2833
|
-
case
|
|
2828
|
+
case 'table': {
|
|
2834
2829
|
const tablePath = this.fieldDef.structSource.tablePath;
|
|
2835
2830
|
return this.dialect.quoteTablePath(tablePath);
|
|
2836
2831
|
}
|
|
2837
|
-
case
|
|
2838
|
-
if (this.fieldDef.structSource.method ===
|
|
2839
|
-
this.fieldDef.structSource.method ===
|
|
2832
|
+
case 'sql':
|
|
2833
|
+
if (this.fieldDef.structSource.method === 'nested' ||
|
|
2834
|
+
this.fieldDef.structSource.method === 'lastStage') {
|
|
2840
2835
|
return this.fieldDef.name;
|
|
2841
2836
|
}
|
|
2842
|
-
else if (this.fieldDef.structSource.method ===
|
|
2837
|
+
else if (this.fieldDef.structSource.method === 'subquery') {
|
|
2843
2838
|
return `(${this.fieldDef.structSource.sqlBlock.selectStr})`;
|
|
2844
2839
|
}
|
|
2845
2840
|
throw new Error("Internal Error: Unknown structSource type 'sql' method");
|
|
2846
|
-
case
|
|
2841
|
+
case 'nested':
|
|
2847
2842
|
// 'name' is always the source field even if has been renamed through
|
|
2848
2843
|
// 'as'
|
|
2849
|
-
return
|
|
2850
|
-
case
|
|
2851
|
-
return
|
|
2852
|
-
case
|
|
2844
|
+
return 'UNNEST(this.fieldDef.name)';
|
|
2845
|
+
case 'inline':
|
|
2846
|
+
return '';
|
|
2847
|
+
case 'query': {
|
|
2853
2848
|
// cache derived table.
|
|
2854
2849
|
const name = (0, malloy_types_1.getIdentifier)(this.fieldDef);
|
|
2855
2850
|
// this is a hack for now. Need some way to denote this table
|
|
2856
2851
|
// should be cached.
|
|
2857
|
-
if (name.includes(
|
|
2852
|
+
if (name.includes('cache')) {
|
|
2858
2853
|
const dtStageWriter = new StageWriter(true, stageWriter);
|
|
2859
2854
|
this.model.loadQuery(this.fieldDef.structSource.query, dtStageWriter);
|
|
2860
2855
|
return dtStageWriter.addPDT(name, this.dialect);
|
|
@@ -2886,7 +2881,7 @@ class QueryStruct extends QueryNode {
|
|
|
2886
2881
|
}
|
|
2887
2882
|
/** get the componennts of a field path */
|
|
2888
2883
|
static resolvePath(name) {
|
|
2889
|
-
return name.split(
|
|
2884
|
+
return name.split('.');
|
|
2890
2885
|
}
|
|
2891
2886
|
getChildByName(name) {
|
|
2892
2887
|
return this.nameMap.get(name);
|
|
@@ -2942,14 +2937,14 @@ class QueryStruct extends QueryNode {
|
|
|
2942
2937
|
}
|
|
2943
2938
|
}
|
|
2944
2939
|
getDistinctKey() {
|
|
2945
|
-
if (this.fieldDef.structRelationship.type !==
|
|
2946
|
-
return this.getDimensionByName(
|
|
2940
|
+
if (this.fieldDef.structRelationship.type !== 'inline') {
|
|
2941
|
+
return this.getDimensionByName('__distinct_key');
|
|
2947
2942
|
}
|
|
2948
2943
|
else if (this.parent) {
|
|
2949
2944
|
return this.parent.getDistinctKey();
|
|
2950
2945
|
}
|
|
2951
2946
|
else {
|
|
2952
|
-
throw new Error(
|
|
2947
|
+
throw new Error('Internal Error. inline struct can not be top level');
|
|
2953
2948
|
}
|
|
2954
2949
|
}
|
|
2955
2950
|
// take a TurtleDef that might have names and make it so it doesn't.
|
|
@@ -2970,10 +2965,10 @@ class QueryStruct extends QueryNode {
|
|
|
2970
2965
|
pipeline = (0, cloneDeep_1.default)(pipeline);
|
|
2971
2966
|
pipeline[0].filterList = addedFilters.concat(pipeline[0].filterList || [], this.fieldDef.filterList || []);
|
|
2972
2967
|
const flatTurtleDef = {
|
|
2973
|
-
|
|
2974
|
-
|
|
2968
|
+
type: 'turtle',
|
|
2969
|
+
name: turtleDef.name,
|
|
2975
2970
|
pipeline,
|
|
2976
|
-
|
|
2971
|
+
location: turtleDef.location,
|
|
2977
2972
|
};
|
|
2978
2973
|
return flatTurtleDef;
|
|
2979
2974
|
}
|
|
@@ -2995,21 +2990,21 @@ class QueryModel {
|
|
|
2995
2990
|
this.modelDef = modelDef;
|
|
2996
2991
|
for (const s of Object.values(this.modelDef.contents)) {
|
|
2997
2992
|
let qs;
|
|
2998
|
-
if (s.type ===
|
|
2999
|
-
qs = new QueryStruct(s, {
|
|
2993
|
+
if (s.type === 'struct') {
|
|
2994
|
+
qs = new QueryStruct(s, { model: this });
|
|
3000
2995
|
this.structs.set((0, malloy_types_1.getIdentifier)(s), qs);
|
|
3001
2996
|
qs.resolveQueryFields();
|
|
3002
2997
|
}
|
|
3003
|
-
else if (s.type ===
|
|
2998
|
+
else if (s.type === 'query') {
|
|
3004
2999
|
/* TODO */
|
|
3005
3000
|
}
|
|
3006
3001
|
else {
|
|
3007
|
-
throw new Error(
|
|
3002
|
+
throw new Error('Internal Error: Unknown structure type');
|
|
3008
3003
|
}
|
|
3009
3004
|
}
|
|
3010
3005
|
}
|
|
3011
3006
|
parseQueryPath(name) {
|
|
3012
|
-
const path = name.split(
|
|
3007
|
+
const path = name.split('.');
|
|
3013
3008
|
let struct;
|
|
3014
3009
|
if ((struct = this.structs.get(path[0]))) {
|
|
3015
3010
|
if (path.length > 1) {
|
|
@@ -3018,7 +3013,7 @@ class QueryModel {
|
|
|
3018
3013
|
else {
|
|
3019
3014
|
throw new Error(`No query specified in Struct '${path[0]}'`);
|
|
3020
3015
|
}
|
|
3021
|
-
return {
|
|
3016
|
+
return { queryName: path.join('.'), struct };
|
|
3022
3017
|
}
|
|
3023
3018
|
else {
|
|
3024
3019
|
throw new Error(`Cannot find Struct '${path[0]}' Model`);
|
|
@@ -3035,28 +3030,28 @@ class QueryModel {
|
|
|
3035
3030
|
}
|
|
3036
3031
|
getStructFromRef(structRef) {
|
|
3037
3032
|
let structDef;
|
|
3038
|
-
if (typeof structRef ===
|
|
3033
|
+
if (typeof structRef === 'string') {
|
|
3039
3034
|
return this.getStructByName(structRef);
|
|
3040
3035
|
}
|
|
3041
|
-
else if (structRef.type ===
|
|
3036
|
+
else if (structRef.type === 'struct') {
|
|
3042
3037
|
structDef = structRef;
|
|
3043
3038
|
}
|
|
3044
3039
|
else {
|
|
3045
|
-
throw new Error(
|
|
3040
|
+
throw new Error('Broken for now');
|
|
3046
3041
|
}
|
|
3047
|
-
return new QueryStruct(structDef, {
|
|
3042
|
+
return new QueryStruct(structDef, { model: this });
|
|
3048
3043
|
}
|
|
3049
3044
|
loadQuery(query, stageWriter, emitFinalStage = false) {
|
|
3050
|
-
const malloy =
|
|
3045
|
+
const malloy = '';
|
|
3051
3046
|
if (!stageWriter) {
|
|
3052
3047
|
stageWriter = new StageWriter(true, undefined);
|
|
3053
3048
|
}
|
|
3054
3049
|
const turtleDef = {
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3050
|
+
type: 'turtle',
|
|
3051
|
+
name: 'ignoreme',
|
|
3052
|
+
pipeHead: query.pipeHead,
|
|
3053
|
+
pipeline: query.pipeline,
|
|
3054
|
+
filterList: query.filterList,
|
|
3060
3055
|
};
|
|
3061
3056
|
const q = QueryQuery.makeQuery(turtleDef, this.getStructFromRef(query.structRef), stageWriter);
|
|
3062
3057
|
const ret = q.generateSQLFromPipeline(stageWriter);
|
|
@@ -3065,28 +3060,28 @@ class QueryModel {
|
|
|
3065
3060
|
// for (const f of ret.outputStruct.fields) {
|
|
3066
3061
|
// fieldNames.push(getIdentifier(f));
|
|
3067
3062
|
// }
|
|
3068
|
-
const fieldNames = (0, malloy_types_1.getPhysicalFields)(ret.outputStruct).map(
|
|
3063
|
+
const fieldNames = (0, malloy_types_1.getPhysicalFields)(ret.outputStruct).map(fieldDef => q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name));
|
|
3069
3064
|
ret.lastStageName = stageWriter.addStage(q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames));
|
|
3070
3065
|
}
|
|
3071
3066
|
return {
|
|
3072
|
-
|
|
3067
|
+
lastStageName: ret.lastStageName,
|
|
3073
3068
|
malloy,
|
|
3074
3069
|
stageWriter,
|
|
3075
|
-
|
|
3076
|
-
|
|
3070
|
+
structs: [ret.outputStruct],
|
|
3071
|
+
connectionName: q.parent.connectionName,
|
|
3077
3072
|
};
|
|
3078
3073
|
}
|
|
3079
3074
|
compileQuery(query, finalize = true) {
|
|
3080
3075
|
let newModel;
|
|
3081
3076
|
const m = newModel || this;
|
|
3082
3077
|
const ret = m.loadQuery(query, undefined, finalize);
|
|
3083
|
-
const sourceExplore = typeof query.structRef ===
|
|
3078
|
+
const sourceExplore = typeof query.structRef === 'string'
|
|
3084
3079
|
? query.structRef
|
|
3085
3080
|
: // LTNOTE: the parser needs to capture the query before the |. This will work
|
|
3086
3081
|
// in most cases but isn't actually complete.
|
|
3087
|
-
query.structRef.type ===
|
|
3082
|
+
query.structRef.type === 'struct'
|
|
3088
3083
|
? query.structRef.as || query.structRef.name
|
|
3089
|
-
:
|
|
3084
|
+
: '(need to figure this out)';
|
|
3090
3085
|
// LTNote: I don't understand why this might be here. It should have happened in loadQuery...
|
|
3091
3086
|
if (finalize && this.dialect.hasFinalStage) {
|
|
3092
3087
|
ret.lastStageName = ret.stageWriter.addStage(
|
|
@@ -3094,16 +3089,16 @@ class QueryModel {
|
|
|
3094
3089
|
this.dialect.sqlFinalStage(ret.lastStageName, []));
|
|
3095
3090
|
}
|
|
3096
3091
|
return {
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3092
|
+
lastStageName: ret.lastStageName,
|
|
3093
|
+
malloy: ret.malloy,
|
|
3094
|
+
sql: ret.stageWriter.generateSQLStages(),
|
|
3095
|
+
structs: ret.structs,
|
|
3101
3096
|
sourceExplore,
|
|
3102
|
-
|
|
3103
|
-
|
|
3097
|
+
sourceFilters: query.filterList,
|
|
3098
|
+
queryName: query.pipeHead && query.pipeline.length === 0
|
|
3104
3099
|
? query.pipeHead.name
|
|
3105
3100
|
: undefined,
|
|
3106
|
-
|
|
3101
|
+
connectionName: ret.connectionName,
|
|
3107
3102
|
};
|
|
3108
3103
|
}
|
|
3109
3104
|
async searchIndex(connection, explore, searchValue, limit = 1000, searchField = undefined) {
|
|
@@ -3113,28 +3108,28 @@ class QueryModel {
|
|
|
3113
3108
|
// make a search index if one isn't modelled.
|
|
3114
3109
|
const struct = this.getStructByName(explore);
|
|
3115
3110
|
let indexQuery;
|
|
3116
|
-
if (!struct.nameMap.get(
|
|
3111
|
+
if (!struct.nameMap.get('search_index')) {
|
|
3117
3112
|
indexQuery = {
|
|
3118
|
-
|
|
3119
|
-
|
|
3113
|
+
structRef: explore,
|
|
3114
|
+
pipeline: [
|
|
3120
3115
|
{
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
}
|
|
3125
|
-
]
|
|
3116
|
+
type: 'index',
|
|
3117
|
+
fields: ['*'],
|
|
3118
|
+
sample: struct.dialect.defaultSampling,
|
|
3119
|
+
},
|
|
3120
|
+
],
|
|
3126
3121
|
};
|
|
3127
3122
|
}
|
|
3128
3123
|
else {
|
|
3129
3124
|
indexQuery = {
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3125
|
+
structRef: explore,
|
|
3126
|
+
pipeHead: { name: 'search_index' },
|
|
3127
|
+
pipeline: [],
|
|
3133
3128
|
};
|
|
3134
3129
|
}
|
|
3135
|
-
const fieldNameColumn = struct.dialect.sqlMaybeQuoteIdentifier(
|
|
3136
|
-
const fieldValueColumn = struct.dialect.sqlMaybeQuoteIdentifier(
|
|
3137
|
-
const fieldTypeColumn = struct.dialect.sqlMaybeQuoteIdentifier(
|
|
3130
|
+
const fieldNameColumn = struct.dialect.sqlMaybeQuoteIdentifier('fieldName');
|
|
3131
|
+
const fieldValueColumn = struct.dialect.sqlMaybeQuoteIdentifier('fieldValue');
|
|
3132
|
+
const fieldTypeColumn = struct.dialect.sqlMaybeQuoteIdentifier('fieldType');
|
|
3138
3133
|
// if we've compiled the SQL before use it otherwise
|
|
3139
3134
|
let sqlPDT = this.exploreSearchSQLMap.get(explore);
|
|
3140
3135
|
if (sqlPDT === undefined) {
|
|
@@ -3146,25 +3141,25 @@ class QueryModel {
|
|
|
3146
3141
|
${fieldValueColumn},
|
|
3147
3142
|
${fieldTypeColumn},
|
|
3148
3143
|
weight,
|
|
3149
|
-
CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue +
|
|
3144
|
+
CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue + '%')}) THEN 1 ELSE 0 END as match_first
|
|
3150
3145
|
FROM ${await connection.manifestTemporaryTable(sqlPDT)}
|
|
3151
|
-
WHERE lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(
|
|
3146
|
+
WHERE lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral('%' + searchValue + '%')}) ${searchField !== undefined
|
|
3152
3147
|
? ` AND ${fieldNameColumn} = '` + searchField + "' \n"
|
|
3153
|
-
:
|
|
3154
|
-
ORDER BY CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue +
|
|
3148
|
+
: ''}
|
|
3149
|
+
ORDER BY CASE WHEN lower(${fieldValueColumn}) LIKE lower(${generateSQLStringLiteral(searchValue + '%')}) THEN 1 ELSE 0 END DESC, weight DESC
|
|
3155
3150
|
LIMIT ${limit}
|
|
3156
3151
|
`;
|
|
3157
3152
|
if (struct.dialect.hasFinalStage) {
|
|
3158
|
-
query = `WITH __stage0 AS(\n${query}\n)\n${struct.dialect.sqlFinalStage(
|
|
3153
|
+
query = `WITH __stage0 AS(\n${query}\n)\n${struct.dialect.sqlFinalStage('__stage0', [
|
|
3159
3154
|
fieldNameColumn,
|
|
3160
3155
|
fieldValueColumn,
|
|
3161
3156
|
fieldTypeColumn,
|
|
3162
|
-
|
|
3163
|
-
|
|
3157
|
+
'weight',
|
|
3158
|
+
'match_first',
|
|
3164
3159
|
])}`;
|
|
3165
3160
|
}
|
|
3166
3161
|
const result = await connection.runSQL(query, {
|
|
3167
|
-
|
|
3162
|
+
rowLimit: 1000,
|
|
3168
3163
|
});
|
|
3169
3164
|
return result.rows;
|
|
3170
3165
|
}
|
|
@@ -3172,14 +3167,14 @@ class QueryModel {
|
|
|
3172
3167
|
exports.QueryModel = QueryModel;
|
|
3173
3168
|
function flattenQuery(model, query) {
|
|
3174
3169
|
let structRef = query.structRef;
|
|
3175
|
-
if (typeof structRef !==
|
|
3170
|
+
if (typeof structRef !== 'string') {
|
|
3176
3171
|
structRef = structRef.as || structRef.name;
|
|
3177
3172
|
}
|
|
3178
3173
|
const queryModel = new QueryModel(model);
|
|
3179
3174
|
const queryStruct = queryModel.getStructByName(structRef);
|
|
3180
3175
|
const turtleDef = queryStruct.flattenTurtleDef({
|
|
3181
3176
|
...query,
|
|
3182
|
-
|
|
3177
|
+
type: 'turtle',
|
|
3183
3178
|
});
|
|
3184
3179
|
return turtleDef;
|
|
3185
3180
|
}
|