@malloydata/malloy 0.0.304 → 0.0.305
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dialect/dialect.d.ts +1 -1
- package/dist/dialect/duckdb/duckdb.d.ts +1 -1
- package/dist/dialect/duckdb/duckdb.js +2 -6
- package/dist/dialect/mysql/mysql.d.ts +1 -1
- package/dist/dialect/mysql/mysql.js +2 -6
- package/dist/dialect/postgres/postgres.d.ts +1 -1
- package/dist/dialect/postgres/postgres.js +2 -6
- package/dist/dialect/snowflake/snowflake.d.ts +1 -1
- package/dist/dialect/snowflake/snowflake.js +2 -5
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +2 -6
- package/dist/dialect/trino/trino.d.ts +1 -1
- package/dist/dialect/trino/trino.js +2 -6
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -3
- package/dist/lang/ast/expressions/expr-aggregate-function.js +12 -2
- package/dist/lang/ast/expressions/expr-count.js +3 -1
- package/dist/lang/ast/expressions/expr-func.js +34 -10
- package/dist/lang/ast/expressions/expr-props.js +1 -1
- package/dist/lang/ast/expressions/expr-ungroup.js +7 -3
- package/dist/lang/ast/expressions/function-ordering.d.ts +19 -5
- package/dist/lang/ast/expressions/function-ordering.js +61 -9
- package/dist/lang/ast/field-space/include-utils.js +1 -1
- package/dist/lang/ast/field-space/index-field-space.js +3 -1
- package/dist/lang/ast/field-space/query-spaces.js +20 -11
- package/dist/lang/ast/query-builders/index-builder.js +1 -1
- package/dist/lang/ast/query-builders/reduce-builder.js +1 -1
- package/dist/lang/ast/query-elements/query-arrow.js +14 -4
- package/dist/lang/ast/query-elements/query-base.d.ts +1 -0
- package/dist/lang/ast/query-elements/query-base.js +14 -4
- package/dist/lang/ast/query-elements/query-refine.js +2 -0
- package/dist/lang/ast/query-properties/drill.js +1 -1
- package/dist/lang/ast/source-properties/join.js +6 -2
- package/dist/lang/ast/statements/define-source.js +1 -1
- package/dist/lang/ast/types/expr-value.js +1 -1
- package/dist/lang/ast/view-elements/reference-view.js +4 -1
- package/dist/lang/ast/view-elements/refine-utils.js +1 -1
- package/dist/{model/composite_source_utils.d.ts → lang/composite-source-utils.d.ts} +4 -17
- package/dist/{model/composite_source_utils.js → lang/composite-source-utils.js} +274 -44
- package/dist/lang/test/parse-expects.d.ts +1 -1
- package/dist/lang/test/parse-expects.js +6 -2
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/malloy.js +1 -1
- package/dist/model/expression_compiler.d.ts +27 -0
- package/dist/model/expression_compiler.js +780 -0
- package/dist/model/field_instance.d.ts +108 -0
- package/dist/model/field_instance.js +520 -0
- package/dist/model/index.d.ts +5 -1
- package/dist/model/index.js +25 -4
- package/dist/model/join_instance.d.ts +18 -0
- package/dist/model/join_instance.js +71 -0
- package/dist/model/malloy_types.d.ts +48 -2
- package/dist/model/malloy_types.js +39 -1
- package/dist/model/query_model.d.ts +2 -0
- package/dist/model/query_model.js +7 -0
- package/dist/model/query_model_contract.d.ts +32 -0
- package/dist/model/query_model_contract.js +7 -0
- package/dist/model/query_model_impl.d.ts +30 -0
- package/dist/model/query_model_impl.js +266 -0
- package/dist/model/query_node.d.ts +132 -0
- package/dist/model/query_node.js +638 -0
- package/dist/model/query_query.d.ts +86 -0
- package/dist/model/query_query.js +1724 -0
- package/dist/model/sql_block.js +2 -2
- package/dist/model/stage_writer.d.ts +25 -0
- package/dist/model/stage_writer.js +120 -0
- package/dist/model/utils.d.ts +18 -1
- package/dist/model/utils.js +66 -1
- package/dist/to_stable.js +3 -4
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
- package/dist/model/malloy_query.d.ts +0 -391
- package/dist/model/malloy_query.js +0 -3926
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { QueryStruct } from './query_node';
|
|
2
|
+
import { QueryFieldBoolean } from './query_node';
|
|
3
|
+
import type { JoinRelationship, UniqueKeyRequirement } from './malloy_types';
|
|
4
|
+
import type { DialectFieldList } from '../dialect';
|
|
5
|
+
export declare class JoinInstance {
|
|
6
|
+
queryStruct: QueryStruct;
|
|
7
|
+
alias: string;
|
|
8
|
+
parent: JoinInstance | undefined;
|
|
9
|
+
uniqueKeyRequirement?: UniqueKeyRequirement;
|
|
10
|
+
makeUniqueKey: boolean;
|
|
11
|
+
leafiest: boolean;
|
|
12
|
+
joinFilterConditions?: QueryFieldBoolean[];
|
|
13
|
+
children: JoinInstance[];
|
|
14
|
+
constructor(queryStruct: QueryStruct, alias: string, parent: JoinInstance | undefined);
|
|
15
|
+
parentRelationship(): 'root' | JoinRelationship;
|
|
16
|
+
forceAllSymmetricCalculations(): boolean;
|
|
17
|
+
getDialectFieldList(): DialectFieldList;
|
|
18
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.JoinInstance = void 0;
|
|
8
|
+
const query_node_1 = require("./query_node");
|
|
9
|
+
const utils_1 = require("./utils");
|
|
10
|
+
const malloy_types_1 = require("./malloy_types");
|
|
11
|
+
class JoinInstance {
|
|
12
|
+
constructor(queryStruct, alias, parent) {
|
|
13
|
+
this.queryStruct = queryStruct;
|
|
14
|
+
this.alias = alias;
|
|
15
|
+
this.parent = parent;
|
|
16
|
+
this.makeUniqueKey = false;
|
|
17
|
+
this.leafiest = false;
|
|
18
|
+
this.children = [];
|
|
19
|
+
if (parent) {
|
|
20
|
+
parent.children.push(this);
|
|
21
|
+
}
|
|
22
|
+
// convert the filter list into a list of boolean fields so we can
|
|
23
|
+
// generate dependancies and code for them.
|
|
24
|
+
const sd = this.queryStruct.structDef;
|
|
25
|
+
if ((0, malloy_types_1.isSourceDef)(sd) && sd.filterList) {
|
|
26
|
+
this.joinFilterConditions = sd.filterList.map(filter => new query_node_1.QueryFieldBoolean({
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
name: 'ignoreme',
|
|
29
|
+
e: filter.e,
|
|
30
|
+
}, this.queryStruct));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
parentRelationship() {
|
|
34
|
+
if (this.queryStruct.parent === undefined) {
|
|
35
|
+
return 'root';
|
|
36
|
+
}
|
|
37
|
+
const thisStruct = this.queryStruct.structDef;
|
|
38
|
+
if ((0, malloy_types_1.isJoined)(thisStruct)) {
|
|
39
|
+
switch (thisStruct.join) {
|
|
40
|
+
case 'one':
|
|
41
|
+
return 'many_to_one';
|
|
42
|
+
case 'cross':
|
|
43
|
+
return 'many_to_many';
|
|
44
|
+
case 'many':
|
|
45
|
+
return 'one_to_many';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
throw new Error(`Internal error unknown relationship type to parent for ${this.queryStruct.structDef.name}`);
|
|
49
|
+
}
|
|
50
|
+
// For now, we force all symmetric calculations for full and right joins
|
|
51
|
+
// because we need distinct keys for COUNT(xx) operations. Don't really need
|
|
52
|
+
// this for sums. This will produce correct results and we can optimize this
|
|
53
|
+
// at some point..
|
|
54
|
+
forceAllSymmetricCalculations() {
|
|
55
|
+
if (this.queryStruct.parent === undefined) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
const thisStruct = this.queryStruct.structDef;
|
|
59
|
+
if ((0, malloy_types_1.isJoined)(thisStruct)) {
|
|
60
|
+
return (thisStruct.matrixOperation === 'right' ||
|
|
61
|
+
thisStruct.matrixOperation === 'full');
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
// postgres unnest needs to know the names of the physical fields.
|
|
66
|
+
getDialectFieldList() {
|
|
67
|
+
return (0, utils_1.getDialectFieldList)(this.queryStruct.structDef);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.JoinInstance = JoinInstance;
|
|
71
|
+
//# sourceMappingURL=join_instance.js.map
|
|
@@ -426,6 +426,12 @@ export interface BasicArrayDef extends BasicArrayTypeDef, StructDefBase, JoinBas
|
|
|
426
426
|
type: 'array';
|
|
427
427
|
join: 'many';
|
|
428
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Create a clean FieldDef from a TypeDef descendent
|
|
431
|
+
* @param atd Usually a TypeDesc
|
|
432
|
+
* @param name
|
|
433
|
+
* @returns Field with `name` and no type meta data
|
|
434
|
+
*/
|
|
429
435
|
export declare function mkFieldDef(atd: AtomicTypeDef, name: string): AtomicFieldDef;
|
|
430
436
|
export declare function mkArrayDef(ofType: AtomicTypeDef, name: string): ArrayDef;
|
|
431
437
|
export interface RecordTypeDef {
|
|
@@ -580,7 +586,24 @@ export interface RawSegment extends Filtered {
|
|
|
580
586
|
export declare function isRawSegment(pe: PipeSegment): pe is RawSegment;
|
|
581
587
|
export type IndexFieldDef = RefToField;
|
|
582
588
|
export type SegmentFieldDef = IndexFieldDef | QueryFieldDef;
|
|
583
|
-
|
|
589
|
+
/**
|
|
590
|
+
* The compiler needs to know a number of things computed for a query.
|
|
591
|
+
* We've modified the fieldUsage code from composite sources to collect
|
|
592
|
+
* the information needed by the compiler and a query is processed
|
|
593
|
+
* as a final step to append this information.
|
|
594
|
+
*
|
|
595
|
+
* 0) An ordered list list of active joins
|
|
596
|
+
* 1) Each field that is referenced, even indirectly
|
|
597
|
+
* 2) Each join path ending in a count
|
|
598
|
+
* 3) Each join path ending in an assymmetric aggregate
|
|
599
|
+
* 4) Each join path ending in an analytic funtion
|
|
600
|
+
*/
|
|
601
|
+
export interface SegmentUsageSummary {
|
|
602
|
+
activeJoins?: FieldUsage[];
|
|
603
|
+
expandedFieldUsage?: FieldUsage[];
|
|
604
|
+
expandedUngroupings?: AggregateUngrouping[];
|
|
605
|
+
}
|
|
606
|
+
export interface IndexSegment extends Filtered, SegmentUsageSummary {
|
|
584
607
|
type: 'index';
|
|
585
608
|
indexFields: IndexFieldDef[];
|
|
586
609
|
limit?: number;
|
|
@@ -595,8 +618,11 @@ export declare function isIndexSegment(pe: PipeSegment): pe is IndexSegment;
|
|
|
595
618
|
export interface FieldUsage {
|
|
596
619
|
path: string[];
|
|
597
620
|
at?: DocumentLocation;
|
|
621
|
+
uniqueKeyRequirement?: UniqueKeyRequirement;
|
|
622
|
+
analyticFunctionUse?: boolean;
|
|
598
623
|
}
|
|
599
|
-
export
|
|
624
|
+
export declare function bareFieldUsage(fu: FieldUsage): boolean;
|
|
625
|
+
export interface QuerySegment extends Filtered, Ordered, SegmentUsageSummary {
|
|
600
626
|
type: 'reduce' | 'project' | 'partial';
|
|
601
627
|
queryFields: QueryFieldDef[];
|
|
602
628
|
extendSource?: FieldDef[];
|
|
@@ -617,6 +643,8 @@ export interface TurtleDef extends NamedObject, Pipeline {
|
|
|
617
643
|
fieldUsage?: FieldUsage[];
|
|
618
644
|
requiredGroupBys?: string[][];
|
|
619
645
|
}
|
|
646
|
+
export interface TurtleDefPlusFilters extends TurtleDef, Filtered {
|
|
647
|
+
}
|
|
620
648
|
interface StructDefBase extends HasLocation, NamedObject {
|
|
621
649
|
type: string;
|
|
622
650
|
annotation?: Annotation;
|
|
@@ -711,6 +739,9 @@ export interface AggregateUngrouping {
|
|
|
711
739
|
ungroupedFields: string[][] | '*';
|
|
712
740
|
fieldUsage: FieldUsage[];
|
|
713
741
|
requiresGroupBy?: RequiredGroupBy[];
|
|
742
|
+
exclude: boolean;
|
|
743
|
+
path: string[];
|
|
744
|
+
refFields?: string[];
|
|
714
745
|
}
|
|
715
746
|
export type TypeInfo = {
|
|
716
747
|
expressionType: ExpressionType;
|
|
@@ -964,4 +995,19 @@ export declare const TD: {
|
|
|
964
995
|
isError: (td: UTD) => td is ErrorTypeDef;
|
|
965
996
|
eq(x: UTD, y: UTD): boolean;
|
|
966
997
|
};
|
|
998
|
+
/**
|
|
999
|
+
* Aggregate functions carry this meta data. Used to determine if
|
|
1000
|
+
* a function requires the existence of a unique key. This used
|
|
1001
|
+
* be a pair of types: UniqueKeyUse and UniqueKeyPossibleUse.
|
|
1002
|
+
*
|
|
1003
|
+
* The three states are:
|
|
1004
|
+
*
|
|
1005
|
+
* 1. undefined - not recorded, symmetric MIN/MAX/COUNT_DISTINCT
|
|
1006
|
+
* 2. {isCount: true} - this is a COUNT aggregate
|
|
1007
|
+
* 3. {isCount: false} - this is an asymmetric aggregate, SUM or AVG
|
|
1008
|
+
*/
|
|
1009
|
+
export type UniqueKeyRequirement = undefined | {
|
|
1010
|
+
isCount: boolean;
|
|
1011
|
+
};
|
|
1012
|
+
export declare function mergeUniqueKeyRequirement(existing: UniqueKeyRequirement, newInfo: UniqueKeyRequirement): UniqueKeyRequirement;
|
|
967
1013
|
export {};
|
|
@@ -72,6 +72,7 @@ exports.isSamplingPercent = isSamplingPercent;
|
|
|
72
72
|
exports.isSamplingEnable = isSamplingEnable;
|
|
73
73
|
exports.isRawSegment = isRawSegment;
|
|
74
74
|
exports.isIndexSegment = isIndexSegment;
|
|
75
|
+
exports.bareFieldUsage = bareFieldUsage;
|
|
75
76
|
exports.isSegmentSQL = isSegmentSQL;
|
|
76
77
|
exports.sourceBase = sourceBase;
|
|
77
78
|
exports.isSourceDef = isSourceDef;
|
|
@@ -87,6 +88,7 @@ exports.isValueNumber = isValueNumber;
|
|
|
87
88
|
exports.isValueBoolean = isValueBoolean;
|
|
88
89
|
exports.isValueTimestamp = isValueTimestamp;
|
|
89
90
|
exports.isValueDate = isValueDate;
|
|
91
|
+
exports.mergeUniqueKeyRequirement = mergeUniqueKeyRequirement;
|
|
90
92
|
function exprHasKids(e) {
|
|
91
93
|
return 'kids' in e;
|
|
92
94
|
}
|
|
@@ -236,6 +238,12 @@ function isCastType(s) {
|
|
|
236
238
|
function fieldIsIntrinsic(f) {
|
|
237
239
|
return isAtomicFieldType(f.type) && !hasExpression(f);
|
|
238
240
|
}
|
|
241
|
+
/**
|
|
242
|
+
* Create a clean FieldDef from a TypeDef descendent
|
|
243
|
+
* @param atd Usually a TypeDesc
|
|
244
|
+
* @param name
|
|
245
|
+
* @returns Field with `name` and no type meta data
|
|
246
|
+
*/
|
|
239
247
|
function mkFieldDef(atd, name) {
|
|
240
248
|
if (isBasicArray(atd)) {
|
|
241
249
|
return mkArrayDef(atd.elementTypeDef, name);
|
|
@@ -248,7 +256,24 @@ function mkFieldDef(atd, name) {
|
|
|
248
256
|
const { type, fields } = atd;
|
|
249
257
|
return { type, fields, join: 'one', name };
|
|
250
258
|
}
|
|
251
|
-
|
|
259
|
+
const ret = { name, type: atd.type };
|
|
260
|
+
switch (atd.type) {
|
|
261
|
+
case 'sql native':
|
|
262
|
+
return { ...ret, rawType: atd.rawType };
|
|
263
|
+
case 'number': {
|
|
264
|
+
const numberType = atd.numberType;
|
|
265
|
+
return numberType ? { ...ret, numberType } : ret;
|
|
266
|
+
}
|
|
267
|
+
case 'date': {
|
|
268
|
+
const timeframe = atd.timeframe;
|
|
269
|
+
return timeframe ? { name, type: 'date', timeframe } : ret;
|
|
270
|
+
}
|
|
271
|
+
case 'timestamp': {
|
|
272
|
+
const timeframe = atd.timeframe;
|
|
273
|
+
return timeframe ? { name, type: 'timestamp', timeframe } : ret;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return ret;
|
|
252
277
|
}
|
|
253
278
|
function mkArrayDef(ofType, name) {
|
|
254
279
|
if (ofType.type === 'record') {
|
|
@@ -365,6 +390,10 @@ function isRawSegment(pe) {
|
|
|
365
390
|
function isIndexSegment(pe) {
|
|
366
391
|
return pe.type === 'index';
|
|
367
392
|
}
|
|
393
|
+
function bareFieldUsage(fu) {
|
|
394
|
+
return (fu.uniqueKeyRequirement === undefined &&
|
|
395
|
+
fu.analyticFunctionUse === undefined);
|
|
396
|
+
}
|
|
368
397
|
function isSegmentSQL(f) {
|
|
369
398
|
return 'sql' in f;
|
|
370
399
|
}
|
|
@@ -510,5 +539,14 @@ exports.TD = {
|
|
|
510
539
|
return x.type === y.type;
|
|
511
540
|
},
|
|
512
541
|
};
|
|
542
|
+
function mergeUniqueKeyRequirement(existing, newInfo) {
|
|
543
|
+
if (!existing)
|
|
544
|
+
return newInfo;
|
|
545
|
+
if (!newInfo)
|
|
546
|
+
return existing;
|
|
547
|
+
return {
|
|
548
|
+
isCount: existing.isCount || newInfo.isCount,
|
|
549
|
+
};
|
|
550
|
+
}
|
|
513
551
|
// clang-format on
|
|
514
552
|
//# sourceMappingURL=malloy_types.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getResultStructDefForQuery = exports.makeQueryModel = void 0;
|
|
4
|
+
var query_model_impl_1 = require("./query_model_impl");
|
|
5
|
+
Object.defineProperty(exports, "makeQueryModel", { enumerable: true, get: function () { return query_model_impl_1.makeQueryModel; } });
|
|
6
|
+
Object.defineProperty(exports, "getResultStructDefForQuery", { enumerable: true, get: function () { return query_model_impl_1.getResultStructDefForQuery; } });
|
|
7
|
+
//# sourceMappingURL=query_model.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Connection } from '../connection/types';
|
|
2
|
+
import type { Dialect } from '../dialect';
|
|
3
|
+
import type { EventStream } from '../runtime_types';
|
|
4
|
+
import type { SourceDef, ModelDef, StructRef, Argument, PrepareResultOptions, CompiledQuery, SearchIndexResult, Query } from './malloy_types';
|
|
5
|
+
import type { QueryStruct } from './query_node';
|
|
6
|
+
import type { StageWriter } from './stage_writer';
|
|
7
|
+
export interface ParentQueryModel {
|
|
8
|
+
model: QueryModel;
|
|
9
|
+
}
|
|
10
|
+
export interface QueryResults {
|
|
11
|
+
lastStageName: string;
|
|
12
|
+
stageWriter: StageWriter;
|
|
13
|
+
structs: SourceDef[];
|
|
14
|
+
malloy: string;
|
|
15
|
+
connectionName: string;
|
|
16
|
+
}
|
|
17
|
+
export interface QueryModel {
|
|
18
|
+
dialect: Dialect;
|
|
19
|
+
modelDef: ModelDef | undefined;
|
|
20
|
+
structs: Map<string, QueryStruct>;
|
|
21
|
+
eventStream?: EventStream;
|
|
22
|
+
loadModelFromDef(modelDef: ModelDef): void;
|
|
23
|
+
getStructByName(name: string): QueryStruct;
|
|
24
|
+
getStructFromRef(structRef: StructRef, sourceArguments: Record<string, Argument> | undefined, prepareResultOptions?: PrepareResultOptions | undefined): QueryStruct;
|
|
25
|
+
loadQuery(query: Query, stageWriter: StageWriter | undefined, prepareResultOptions: PrepareResultOptions | undefined, emitFinalStage: boolean | undefined, isJoinedSubquery: boolean | undefined): QueryResults;
|
|
26
|
+
addDefaultRowLimit(query: Query, defaultRowLimit?: number): {
|
|
27
|
+
query: Query;
|
|
28
|
+
addedDefaultRowLimit?: number;
|
|
29
|
+
};
|
|
30
|
+
compileQuery(query: Query, prepareResultOptions: PrepareResultOptions | undefined, finalize: boolean | undefined): CompiledQuery;
|
|
31
|
+
searchIndex(connection: Connection, explore: string, searchValue: string, limit: number, searchField: string | undefined): Promise<SearchIndexResult[] | undefined>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ModelDef, StructRef, Argument, PrepareResultOptions, Query, SourceDef, SearchIndexResult, CompiledQuery, TurtleDef } from './malloy_types';
|
|
2
|
+
import { StageWriter } from './stage_writer';
|
|
3
|
+
import { type Dialect } from '../dialect';
|
|
4
|
+
import type { EventStream } from '../runtime_types';
|
|
5
|
+
import type { Connection } from '../connection/types';
|
|
6
|
+
import type { ModelRootInterface } from './query_node';
|
|
7
|
+
import { QueryStruct } from './query_node';
|
|
8
|
+
import type { QueryModel, QueryResults } from './query_model_contract';
|
|
9
|
+
export declare function makeQueryModel(modelDef: ModelDef | undefined, eventStream?: EventStream): QueryModel;
|
|
10
|
+
export declare class QueryModelImpl implements QueryModel, ModelRootInterface {
|
|
11
|
+
readonly eventStream?: EventStream | undefined;
|
|
12
|
+
dialect: Dialect;
|
|
13
|
+
modelDef: ModelDef | undefined;
|
|
14
|
+
structs: Map<string, QueryStruct>;
|
|
15
|
+
constructor(modelDef: ModelDef | undefined, eventStream?: EventStream | undefined);
|
|
16
|
+
getFinalOutputStruct(query: Query, options: PrepareResultOptions | undefined): SourceDef | undefined;
|
|
17
|
+
loadModelFromDef(modelDef: ModelDef): void;
|
|
18
|
+
getStructByName(name: string): QueryStruct;
|
|
19
|
+
getStructFromRef(structRef: StructRef, sourceArguments: Record<string, Argument> | undefined, prepareResultOptions?: PrepareResultOptions): QueryStruct;
|
|
20
|
+
loadQuery(query: Query, stageWriter: StageWriter | undefined, prepareResultOptions?: PrepareResultOptions, emitFinalStage?: boolean, isJoinedSubquery?: boolean): QueryResults;
|
|
21
|
+
addDefaultRowLimit(query: Query, defaultRowLimit?: number): {
|
|
22
|
+
query: Query;
|
|
23
|
+
addedDefaultRowLimit?: number;
|
|
24
|
+
};
|
|
25
|
+
compileQuery(query: Query, prepareResultOptions?: PrepareResultOptions, finalize?: boolean): CompiledQuery;
|
|
26
|
+
exploreSearchSQLMap: Map<any, any>;
|
|
27
|
+
searchIndex(connection: Connection, explore: string, searchValue: string, limit?: number, searchField?: string | undefined): Promise<SearchIndexResult[] | undefined>;
|
|
28
|
+
}
|
|
29
|
+
export declare function getResultStructDefForQuery(model: ModelDef, query: Query): SourceDef;
|
|
30
|
+
export declare function getResultStructDefForView(source: SourceDef, view: TurtleDef): SourceDef;
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright Contributors to the Malloy project
|
|
4
|
+
* SPDX-License-Identifier: MIT
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.QueryModelImpl = void 0;
|
|
8
|
+
exports.makeQueryModel = makeQueryModel;
|
|
9
|
+
exports.getResultStructDefForQuery = getResultStructDefForQuery;
|
|
10
|
+
exports.getResultStructDefForView = getResultStructDefForView;
|
|
11
|
+
const query_query_1 = require("./query_query");
|
|
12
|
+
const malloy_types_1 = require("./malloy_types");
|
|
13
|
+
const stage_writer_1 = require("./stage_writer");
|
|
14
|
+
const dialect_1 = require("../dialect");
|
|
15
|
+
const utils_1 = require("./materialization/utils");
|
|
16
|
+
const query_node_1 = require("./query_node");
|
|
17
|
+
function makeQueryModel(modelDef, eventStream) {
|
|
18
|
+
return new QueryModelImpl(modelDef, eventStream);
|
|
19
|
+
}
|
|
20
|
+
class QueryModelImpl {
|
|
21
|
+
constructor(modelDef, eventStream) {
|
|
22
|
+
this.eventStream = eventStream;
|
|
23
|
+
this.dialect = new dialect_1.StandardSQLDialect();
|
|
24
|
+
// dialect: Dialect = new PostgresDialect();
|
|
25
|
+
this.modelDef = undefined;
|
|
26
|
+
this.structs = new Map();
|
|
27
|
+
this.exploreSearchSQLMap = new Map();
|
|
28
|
+
if (modelDef) {
|
|
29
|
+
this.loadModelFromDef(modelDef);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Another circularity breaking method ... call into QueryQuery
|
|
33
|
+
// to find the output shape of a query
|
|
34
|
+
getFinalOutputStruct(query, options) {
|
|
35
|
+
const result = this.loadQuery(query, undefined, options, false, false);
|
|
36
|
+
return result.structs.pop();
|
|
37
|
+
}
|
|
38
|
+
loadModelFromDef(modelDef) {
|
|
39
|
+
this.modelDef = modelDef;
|
|
40
|
+
for (const s of Object.values(this.modelDef.contents)) {
|
|
41
|
+
let qs;
|
|
42
|
+
if ((0, malloy_types_1.isSourceDef)(s)) {
|
|
43
|
+
qs = new query_node_1.QueryStruct(s, undefined, { model: this }, {});
|
|
44
|
+
this.structs.set((0, malloy_types_1.getIdentifier)(s), qs);
|
|
45
|
+
qs.resolveQueryFields((query, options) => this.getFinalOutputStruct(query, options));
|
|
46
|
+
}
|
|
47
|
+
else if (s.type === 'query') {
|
|
48
|
+
/* TODO */
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
throw new Error('Internal Error: Unknown structure type');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
getStructByName(name) {
|
|
56
|
+
const s = this.structs.get(name);
|
|
57
|
+
if (s) {
|
|
58
|
+
return s;
|
|
59
|
+
}
|
|
60
|
+
throw new Error(`Struct ${name} not found in model.`);
|
|
61
|
+
}
|
|
62
|
+
getStructFromRef(structRef, sourceArguments, prepareResultOptions) {
|
|
63
|
+
prepareResultOptions !== null && prepareResultOptions !== void 0 ? prepareResultOptions : (prepareResultOptions = {});
|
|
64
|
+
if (typeof structRef === 'string') {
|
|
65
|
+
const ret = this.getStructByName(structRef);
|
|
66
|
+
if (sourceArguments !== undefined) {
|
|
67
|
+
return new query_node_1.QueryStruct(ret.structDef, sourceArguments, ret.parent ? { struct: ret.parent } : { model: this }, prepareResultOptions);
|
|
68
|
+
}
|
|
69
|
+
return ret;
|
|
70
|
+
}
|
|
71
|
+
return new query_node_1.QueryStruct(structRef, sourceArguments, { model: this }, prepareResultOptions);
|
|
72
|
+
}
|
|
73
|
+
loadQuery(query, stageWriter, prepareResultOptions, emitFinalStage = false, isJoinedSubquery = false) {
|
|
74
|
+
var _a;
|
|
75
|
+
const malloy = '';
|
|
76
|
+
if (!stageWriter) {
|
|
77
|
+
stageWriter = new stage_writer_1.StageWriter(true, undefined);
|
|
78
|
+
}
|
|
79
|
+
const turtleDef = {
|
|
80
|
+
type: 'turtle',
|
|
81
|
+
name: 'ignoreme',
|
|
82
|
+
pipeline: query.pipeline,
|
|
83
|
+
filterList: query.filterList,
|
|
84
|
+
};
|
|
85
|
+
const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
|
|
86
|
+
const q = query_query_1.QueryQuery.makeQuery(turtleDef, this.getStructFromRef(structRef, query.sourceArguments, prepareResultOptions), stageWriter, isJoinedSubquery, (name) => this.structs.get(name));
|
|
87
|
+
const ret = q.generateSQLFromPipeline(stageWriter);
|
|
88
|
+
if (emitFinalStage && q.parent.dialect.hasFinalStage) {
|
|
89
|
+
// const fieldNames: string[] = [];
|
|
90
|
+
// for (const f of ret.outputStruct.fields) {
|
|
91
|
+
// fieldNames.push(getIdentifier(f));
|
|
92
|
+
// }
|
|
93
|
+
const fieldNames = [];
|
|
94
|
+
for (const f of ret.outputStruct.fields) {
|
|
95
|
+
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
96
|
+
const quoted = q.parent.dialect.sqlMaybeQuoteIdentifier(f.name);
|
|
97
|
+
fieldNames.push(quoted);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// const fieldNames = getAtomicFields(ret.outputStruct).map(fieldDef =>
|
|
101
|
+
// q.parent.dialect.sqlMaybeQuoteIdentifier(fieldDef.name)
|
|
102
|
+
// );
|
|
103
|
+
ret.lastStageName = stageWriter.addStage(q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames));
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
lastStageName: ret.lastStageName,
|
|
107
|
+
malloy,
|
|
108
|
+
stageWriter,
|
|
109
|
+
structs: [ret.outputStruct],
|
|
110
|
+
connectionName: q.parent.connectionName,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
addDefaultRowLimit(query, defaultRowLimit) {
|
|
114
|
+
const nope = { query, addedDefaultRowLimit: undefined };
|
|
115
|
+
if (defaultRowLimit === undefined)
|
|
116
|
+
return nope;
|
|
117
|
+
const lastSegment = query.pipeline[query.pipeline.length - 1];
|
|
118
|
+
if (lastSegment.type === 'raw')
|
|
119
|
+
return nope;
|
|
120
|
+
if (lastSegment.limit !== undefined)
|
|
121
|
+
return nope;
|
|
122
|
+
return {
|
|
123
|
+
query: {
|
|
124
|
+
...query,
|
|
125
|
+
pipeline: [
|
|
126
|
+
...query.pipeline.slice(0, -1),
|
|
127
|
+
{
|
|
128
|
+
...lastSegment,
|
|
129
|
+
limit: defaultRowLimit,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
addedDefaultRowLimit: defaultRowLimit,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
compileQuery(query, prepareResultOptions, finalize = true) {
|
|
137
|
+
var _a, _b, _c;
|
|
138
|
+
let newModel;
|
|
139
|
+
const addDefaultRowLimit = this.addDefaultRowLimit(query, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.defaultRowLimit);
|
|
140
|
+
query = addDefaultRowLimit.query;
|
|
141
|
+
const addedDefaultRowLimit = addDefaultRowLimit.addedDefaultRowLimit;
|
|
142
|
+
const m = newModel || this;
|
|
143
|
+
const ret = m.loadQuery(query, undefined, prepareResultOptions, finalize, false);
|
|
144
|
+
const structRef = (_a = query.compositeResolvedSourceDef) !== null && _a !== void 0 ? _a : query.structRef;
|
|
145
|
+
const sourceExplore = typeof structRef === 'string'
|
|
146
|
+
? structRef
|
|
147
|
+
: structRef.as || structRef.name;
|
|
148
|
+
const sourceArguments = (_b = query.sourceArguments) !== null && _b !== void 0 ? _b : (typeof structRef === 'string' ? undefined : structRef.arguments);
|
|
149
|
+
// LTNote: I don't understand why this might be here. It should have happened in loadQuery...
|
|
150
|
+
if (finalize && this.dialect.hasFinalStage) {
|
|
151
|
+
ret.lastStageName = ret.stageWriter.addStage(
|
|
152
|
+
// note this will be broken on duckDB waiting on a real fix.
|
|
153
|
+
this.dialect.sqlFinalStage(ret.lastStageName, []));
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
lastStageName: ret.lastStageName,
|
|
157
|
+
malloy: ret.malloy,
|
|
158
|
+
sql: ret.stageWriter.generateSQLStages(),
|
|
159
|
+
dependenciesToMaterialize: ret.stageWriter.dependenciesToMaterialize,
|
|
160
|
+
materialization: (0, utils_1.shouldMaterialize)(query.annotation)
|
|
161
|
+
? (0, utils_1.buildQueryMaterializationSpec)((_c = query.location) === null || _c === void 0 ? void 0 : _c.url, query.name, prepareResultOptions === null || prepareResultOptions === void 0 ? void 0 : prepareResultOptions.materializedTablePrefix)
|
|
162
|
+
: undefined,
|
|
163
|
+
structs: ret.structs,
|
|
164
|
+
sourceExplore,
|
|
165
|
+
sourceFilters: query.filterList,
|
|
166
|
+
sourceArguments,
|
|
167
|
+
queryName: query.name,
|
|
168
|
+
connectionName: ret.connectionName,
|
|
169
|
+
annotation: query.annotation,
|
|
170
|
+
queryTimezone: ret.structs[0].queryTimezone,
|
|
171
|
+
defaultRowLimitAdded: addedDefaultRowLimit,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async searchIndex(connection, explore, searchValue, limit = 1000, searchField = undefined) {
|
|
175
|
+
if (!connection.canPersist()) {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
// make a search index if one isn't modelled.
|
|
179
|
+
const struct = this.getStructByName(explore);
|
|
180
|
+
const d = struct.dialect;
|
|
181
|
+
let indexStar = [];
|
|
182
|
+
for (const [fn, fv] of struct.nameMap) {
|
|
183
|
+
if ((0, query_node_1.isScalarField)(fv) && fv.includeInWildcard()) {
|
|
184
|
+
indexStar.push({ type: 'fieldref', path: [fn] });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
indexStar = indexStar.sort((a, b) => a.path[0].localeCompare(b.path[0]));
|
|
188
|
+
const indexQuery = {
|
|
189
|
+
structRef: explore,
|
|
190
|
+
pipeline: [
|
|
191
|
+
{
|
|
192
|
+
type: 'index',
|
|
193
|
+
indexFields: indexStar,
|
|
194
|
+
sample: d.defaultSampling,
|
|
195
|
+
outputStruct: {
|
|
196
|
+
type: 'query_result',
|
|
197
|
+
name: 'index',
|
|
198
|
+
connection: struct.connectionName,
|
|
199
|
+
dialect: struct.dialect.name,
|
|
200
|
+
fields: [
|
|
201
|
+
{ name: 'fieldName', type: 'string' },
|
|
202
|
+
{ name: 'fieldPath', type: 'string' },
|
|
203
|
+
{ name: 'fieldType', type: 'string' },
|
|
204
|
+
{ name: 'weight', type: 'number' },
|
|
205
|
+
{ name: 'fieldValue', type: 'string' },
|
|
206
|
+
],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
],
|
|
210
|
+
};
|
|
211
|
+
const fieldNameColumn = d.sqlMaybeQuoteIdentifier('fieldName');
|
|
212
|
+
const fieldPathColumn = d.sqlMaybeQuoteIdentifier('fieldPath');
|
|
213
|
+
const fieldValueColumn = d.sqlMaybeQuoteIdentifier('fieldValue');
|
|
214
|
+
const fieldTypeColumn = d.sqlMaybeQuoteIdentifier('fieldType');
|
|
215
|
+
const weightColumn = d.sqlMaybeQuoteIdentifier('weight');
|
|
216
|
+
// if we've compiled the SQL before use it otherwise
|
|
217
|
+
let sqlPDT = this.exploreSearchSQLMap.get(explore);
|
|
218
|
+
if (sqlPDT === undefined) {
|
|
219
|
+
sqlPDT = this.compileQuery(indexQuery, undefined, false).sql;
|
|
220
|
+
this.exploreSearchSQLMap.set(explore, sqlPDT);
|
|
221
|
+
}
|
|
222
|
+
let query = `SELECT
|
|
223
|
+
${fieldNameColumn},
|
|
224
|
+
${fieldPathColumn},
|
|
225
|
+
${fieldValueColumn},
|
|
226
|
+
${fieldTypeColumn},
|
|
227
|
+
${weightColumn},
|
|
228
|
+
CASE WHEN lower(${fieldValueColumn}) LIKE lower(${d.sqlLiteralString(searchValue + '%')}) THEN 1 ELSE 0 END as match_first
|
|
229
|
+
FROM ${await connection.manifestTemporaryTable(sqlPDT)}
|
|
230
|
+
WHERE lower(${fieldValueColumn}) LIKE lower(${d.sqlLiteralString('%' + searchValue + '%')}) ${searchField !== undefined
|
|
231
|
+
? ` AND ${fieldNameColumn} = '` + searchField + "' \n"
|
|
232
|
+
: ''}
|
|
233
|
+
ORDER BY CASE WHEN lower(${fieldValueColumn}) LIKE lower(${d.sqlLiteralString(searchValue + '%')}) THEN 1 ELSE 0 END DESC, ${weightColumn} DESC
|
|
234
|
+
LIMIT ${limit}
|
|
235
|
+
`;
|
|
236
|
+
if (d.hasFinalStage) {
|
|
237
|
+
query = `WITH __stage0 AS(\n${query}\n)\n${d.sqlFinalStage('__stage0', [
|
|
238
|
+
fieldNameColumn,
|
|
239
|
+
fieldPathColumn,
|
|
240
|
+
fieldValueColumn,
|
|
241
|
+
fieldTypeColumn,
|
|
242
|
+
weightColumn,
|
|
243
|
+
'match_first',
|
|
244
|
+
])}`;
|
|
245
|
+
}
|
|
246
|
+
const result = await connection.runSQL(query, {
|
|
247
|
+
rowLimit: 1000,
|
|
248
|
+
});
|
|
249
|
+
return result.rows;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
exports.QueryModelImpl = QueryModelImpl;
|
|
253
|
+
function getResultStructDefForQuery(model, query) {
|
|
254
|
+
const queryModel = makeQueryModel(model);
|
|
255
|
+
const compiled = queryModel.compileQuery(query, undefined, true);
|
|
256
|
+
return compiled.structs[compiled.structs.length - 1];
|
|
257
|
+
}
|
|
258
|
+
function getResultStructDefForView(source, view) {
|
|
259
|
+
const qs = new query_node_1.QueryStruct(source, undefined, {
|
|
260
|
+
model: makeQueryModel(undefined),
|
|
261
|
+
}, {});
|
|
262
|
+
const queryQueryQuery = query_query_1.QueryQuery.makeQuery(view, qs, new stage_writer_1.StageWriter(true, undefined), // stage write indicates we want to get a result.
|
|
263
|
+
false, () => undefined);
|
|
264
|
+
return queryQueryQuery.getResultStructDef();
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=query_model_impl.js.map
|