@malloydata/malloy 0.0.433 → 0.0.434
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/api/foundation/compile.d.ts +2 -1
- package/dist/api/foundation/compile.js +8 -6
- package/dist/api/foundation/config.js +6 -15
- package/dist/api/foundation/runtime.d.ts +2 -7
- package/dist/api/foundation/runtime.js +10 -16
- package/dist/api/foundation/types.d.ts +1 -0
- package/dist/connection/validate_table_path.d.ts +1 -2
- package/dist/connection/validate_table_path.js +1 -2
- package/dist/dialect/databricks/databricks.js +6 -6
- package/dist/dialect/dialect.d.ts +21 -2
- package/dist/dialect/dialect.js +33 -3
- package/dist/dialect/duckdb/duckdb.js +7 -21
- package/dist/dialect/mysql/mysql.js +8 -8
- package/dist/dialect/pg_impl.js +12 -12
- package/dist/dialect/postgres/postgres.js +3 -3
- package/dist/dialect/snowflake/snowflake.js +12 -12
- package/dist/dialect/standardsql/standardsql.d.ts +1 -1
- package/dist/dialect/standardsql/standardsql.js +16 -13
- package/dist/dialect/trino/trino.d.ts +3 -3
- package/dist/dialect/trino/trino.js +32 -28
- package/dist/lang/ast/source-elements/refined-source.js +1 -1
- package/dist/lang/ast/source-elements/sql-source.js +2 -1
- package/dist/lang/ast/source-properties/timezone-statement.d.ts +4 -3
- package/dist/lang/ast/source-properties/timezone-statement.js +14 -12
- package/dist/lang/composite-source-utils.js +10 -3
- package/dist/lang/malloy-to-ast.js +2 -8
- package/dist/lang/parse-malloy.d.ts +3 -2
- package/dist/lang/parse-malloy.js +2 -1
- package/dist/lang/test/test-translator.d.ts +2 -1
- package/dist/lang/test/test-translator.js +1 -1
- package/dist/model/join_instance.d.ts +1 -0
- package/dist/model/join_instance.js +9 -1
- package/dist/model/query_query.d.ts +25 -0
- package/dist/model/query_query.js +142 -17
- package/dist/model/sql_block.d.ts +2 -2
- package/dist/model/sql_block.js +11 -47
- package/dist/model/sql_compiled.d.ts +3 -1
- package/dist/model/sql_compiled.js +8 -6
- package/dist/model/stage_writer.js +3 -3
- package/dist/model/utils.d.ts +2 -2
- package/dist/model/utils.js +2 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/femto-config.motly +2 -0
- package/package.json +4 -4
|
@@ -30,6 +30,14 @@ class JoinInstance {
|
|
|
30
30
|
}, this.queryStruct));
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
// A filtered join whose own joins go inside its subquery, so that the
|
|
34
|
+
// subquery has to project them back out for anything outside to name them.
|
|
35
|
+
isSubquery() {
|
|
36
|
+
return (this.children.length > 0 &&
|
|
37
|
+
this.joinFilterConditions !== undefined &&
|
|
38
|
+
(0, malloy_types_1.isJoinedSource)(this.queryStruct.structDef) &&
|
|
39
|
+
this.queryStruct.dialect.supportsComplexFilteredSources);
|
|
40
|
+
}
|
|
33
41
|
parentRelationship() {
|
|
34
42
|
if (this.queryStruct.parent === undefined) {
|
|
35
43
|
return 'root';
|
|
@@ -64,7 +72,7 @@ class JoinInstance {
|
|
|
64
72
|
}
|
|
65
73
|
// postgres unnest needs to know the names of the physical fields.
|
|
66
74
|
getDialectFieldList() {
|
|
67
|
-
return (0, utils_1.getDialectFieldList)(this.queryStruct.structDef);
|
|
75
|
+
return (0, utils_1.getDialectFieldList)(this.queryStruct.structDef, this.queryStruct.dialect);
|
|
68
76
|
}
|
|
69
77
|
}
|
|
70
78
|
exports.JoinInstance = JoinInstance;
|
|
@@ -34,6 +34,7 @@ export declare class QueryQuery extends QueryField {
|
|
|
34
34
|
maxDepth: number;
|
|
35
35
|
maxGroupSet: number;
|
|
36
36
|
rootResult: FieldInstanceResultRoot;
|
|
37
|
+
private packedColumns;
|
|
37
38
|
resultStage: string | undefined;
|
|
38
39
|
stageWriter: StageWriter | undefined;
|
|
39
40
|
isJoinedSubquery: boolean;
|
|
@@ -90,6 +91,30 @@ export declare class QueryQuery extends QueryField {
|
|
|
90
91
|
sql?: string;
|
|
91
92
|
};
|
|
92
93
|
generateSQLJoinBlock(stageWriter: StageWriter, ji: JoinInstance, depth: number): string;
|
|
94
|
+
/**
|
|
95
|
+
* Which column of which join a reference needs, or undefined when it needs
|
|
96
|
+
* none. Walking `path` from `from`, the answer is the member of the
|
|
97
|
+
* innermost join along it -- a record is kept whole, since a reference into
|
|
98
|
+
* one is a reference to the column holding it.
|
|
99
|
+
*
|
|
100
|
+
* Two kinds of path need no column. One ending at a computed field
|
|
101
|
+
* contributes that field's name, which is no column of the source; the
|
|
102
|
+
* columns the computation reads arrive as references of their own. And a
|
|
103
|
+
* `having:` or `calculate:` names an *output* field, so its path is rooted
|
|
104
|
+
* in the query's output rather than its source and need not resolve here at
|
|
105
|
+
* all. An output name which happens to match a source column resolves as
|
|
106
|
+
* though it were one, which costs nothing: a query's own source is never
|
|
107
|
+
* packed, only the joins beneath a filtered one.
|
|
108
|
+
*/
|
|
109
|
+
private packedColumnFor;
|
|
110
|
+
/**
|
|
111
|
+
* Which columns of each join have to survive being packed into a struct:
|
|
112
|
+
* every column the segment's field usage names, plus each join's primary
|
|
113
|
+
* key, which the compiler reaches for as a distinct key without any
|
|
114
|
+
* expression in the query naming it.
|
|
115
|
+
*/
|
|
116
|
+
private packedColumnsByJoin;
|
|
117
|
+
private joinProjectionFieldList;
|
|
93
118
|
generateSQLPassthroughKeys(qs: QueryStruct): string;
|
|
94
119
|
generateSQLJoins(stageWriter: StageWriter): string;
|
|
95
120
|
/**
|
|
@@ -39,9 +39,9 @@ function groupByPositions(columns) {
|
|
|
39
39
|
return columns.flatMap((c, i) => (c.isDimension ? [i + 1] : []));
|
|
40
40
|
}
|
|
41
41
|
function pushDialectField(dl, f) {
|
|
42
|
-
const { sqlExpression,
|
|
42
|
+
const { sqlExpression, rawName } = f;
|
|
43
43
|
if ((0, malloy_types_1.isAtomic)(f.fieldDef)) {
|
|
44
|
-
dl.push({ typeDef: f.fieldDef, sqlExpression,
|
|
44
|
+
dl.push({ typeDef: f.fieldDef, sqlExpression, rawName });
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
/** Query builder object. */
|
|
@@ -539,13 +539,23 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
539
539
|
case 'virtual': {
|
|
540
540
|
const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
|
|
541
541
|
const tablePath = (_b = virtualMap === null || virtualMap === void 0 ? void 0 : virtualMap.get(qs.structDef.connection)) === null || _b === void 0 ? void 0 : _b.get(qs.structDef.name);
|
|
542
|
-
if (
|
|
542
|
+
if (tablePath === undefined) {
|
|
543
543
|
throw new malloy_compile_error_1.MalloyCompileError(`No virtual-map entry for virtual source '${qs.structDef.name}' ` +
|
|
544
544
|
`on connection '${qs.structDef.connection}'. ` +
|
|
545
|
-
'
|
|
545
|
+
'Supply the entry in `virtualMap` when compiling or running the model.', 'runtime-virtual-map-missing', qs.structDef.location);
|
|
546
|
+
}
|
|
547
|
+
// Check the binding immediately before SQL substitution, using the
|
|
548
|
+
// executing query's dialect. Other entries in the map may be unused.
|
|
549
|
+
const dialect = this.parent.dialect;
|
|
550
|
+
const checked = dialect.sqlValidateTableName(tablePath);
|
|
551
|
+
if (!checked.ok || checked.canonical !== tablePath) {
|
|
552
|
+
const reason = checked.ok
|
|
553
|
+
? `use the canonical path ${checked.canonical}`
|
|
554
|
+
: checked.error;
|
|
555
|
+
throw new malloy_compile_error_1.MalloyCompileError(`virtualMap entry '${qs.structDef.connection}.${qs.structDef.name}' ` +
|
|
556
|
+
`has table path ${JSON.stringify(tablePath)}, which is not a ` +
|
|
557
|
+
`canonical table path for ${dialect.name}: ${reason}`, 'runtime-virtual-map-invalid', qs.structDef.location);
|
|
546
558
|
}
|
|
547
|
-
// virtualMap entries are application-supplied — assumed already
|
|
548
|
-
// canonical SQL.
|
|
549
559
|
return tablePath;
|
|
550
560
|
}
|
|
551
561
|
case 'composite':
|
|
@@ -676,9 +686,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
676
686
|
return tempInstance.generateExpression();
|
|
677
687
|
});
|
|
678
688
|
}
|
|
679
|
-
if (ji.
|
|
680
|
-
conditions === undefined ||
|
|
681
|
-
!this.parent.dialect.supportsComplexFilteredSources) {
|
|
689
|
+
if (!ji.isSubquery()) {
|
|
682
690
|
// LTNOTE: need a check here to see the children's where: conditions are local
|
|
683
691
|
// to the source and not to any of it's joined children.
|
|
684
692
|
// In Presto, we're going to get a SQL error if in this case
|
|
@@ -700,9 +708,27 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
700
708
|
else {
|
|
701
709
|
let select = `SELECT ${ji.alias}.*`;
|
|
702
710
|
let joins = '';
|
|
711
|
+
// Every join below this one lives inside the subquery, so every one of
|
|
712
|
+
// them has to be projected out of it, not just the direct children.
|
|
713
|
+
// A child which became a subquery of its own has already flattened its
|
|
714
|
+
// whole subtree into columns, so everything under it is read from that
|
|
715
|
+
// child rather than packed a second time.
|
|
716
|
+
const projectJoin = (j, from) => {
|
|
717
|
+
const fieldList = this.joinProjectionFieldList(j);
|
|
718
|
+
if (fieldList.length > 0) {
|
|
719
|
+
select +=
|
|
720
|
+
from === undefined
|
|
721
|
+
? `, ${this.parent.dialect.sqlSelectAliasAsStruct(j.alias, fieldList)} AS ${j.alias}`
|
|
722
|
+
: `, ${from}.${j.alias} AS ${j.alias}`;
|
|
723
|
+
}
|
|
724
|
+
const readFrom = from !== null && from !== void 0 ? from : (j.isSubquery() ? j.alias : undefined);
|
|
725
|
+
for (const below of j.children) {
|
|
726
|
+
projectJoin(below, readFrom);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
703
729
|
for (const childJoin of ji.children) {
|
|
704
730
|
joins += this.generateSQLJoinBlock(stageWriter, childJoin, depth + 1);
|
|
705
|
-
|
|
731
|
+
projectJoin(childJoin, undefined);
|
|
706
732
|
}
|
|
707
733
|
select += `\nFROM ${structSQL} AS ${ji.alias}\n${joins}\nWHERE ${conditions === null || conditions === void 0 ? void 0 : conditions.join(' AND ')}\n`;
|
|
708
734
|
s += `${matrixOperation} JOIN (\n${(0, utils_1.indent)(select)}) AS ${ji.alias}\n ON ${onCondition}\n`;
|
|
@@ -746,6 +772,111 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
746
772
|
}
|
|
747
773
|
return s;
|
|
748
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Which column of which join a reference needs, or undefined when it needs
|
|
777
|
+
* none. Walking `path` from `from`, the answer is the member of the
|
|
778
|
+
* innermost join along it -- a record is kept whole, since a reference into
|
|
779
|
+
* one is a reference to the column holding it.
|
|
780
|
+
*
|
|
781
|
+
* Two kinds of path need no column. One ending at a computed field
|
|
782
|
+
* contributes that field's name, which is no column of the source; the
|
|
783
|
+
* columns the computation reads arrive as references of their own. And a
|
|
784
|
+
* `having:` or `calculate:` names an *output* field, so its path is rooted
|
|
785
|
+
* in the query's output rather than its source and need not resolve here at
|
|
786
|
+
* all. An output name which happens to match a source column resolves as
|
|
787
|
+
* though it were one, which costs nothing: a query's own source is never
|
|
788
|
+
* packed, only the joins beneath a filtered one.
|
|
789
|
+
*/
|
|
790
|
+
packedColumnFor(from, path) {
|
|
791
|
+
let struct = from;
|
|
792
|
+
let alias = struct.getIdentifier();
|
|
793
|
+
let column = undefined;
|
|
794
|
+
for (const name of path) {
|
|
795
|
+
const child = struct.getChildByName(name);
|
|
796
|
+
if (child === undefined)
|
|
797
|
+
return undefined;
|
|
798
|
+
if (!(child instanceof query_node_1.QueryFieldStruct)) {
|
|
799
|
+
column !== null && column !== void 0 ? column : (column = name);
|
|
800
|
+
break;
|
|
801
|
+
}
|
|
802
|
+
struct = child.queryStruct;
|
|
803
|
+
if (struct.getJoinableParent() === struct) {
|
|
804
|
+
// A join: what the path named above it says nothing about what this
|
|
805
|
+
// one has to pack.
|
|
806
|
+
alias = struct.getIdentifier();
|
|
807
|
+
column = undefined;
|
|
808
|
+
}
|
|
809
|
+
else {
|
|
810
|
+
column !== null && column !== void 0 ? column : (column = name);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return column === undefined ? undefined : [alias, column];
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Which columns of each join have to survive being packed into a struct:
|
|
817
|
+
* every column the segment's field usage names, plus each join's primary
|
|
818
|
+
* key, which the compiler reaches for as a distinct key without any
|
|
819
|
+
* expression in the query naming it.
|
|
820
|
+
*/
|
|
821
|
+
packedColumnsByJoin() {
|
|
822
|
+
var _a;
|
|
823
|
+
if (this.packedColumns === undefined) {
|
|
824
|
+
const packed = new Map();
|
|
825
|
+
const needed = (found) => {
|
|
826
|
+
if (found === undefined)
|
|
827
|
+
return;
|
|
828
|
+
const [alias, column] = found;
|
|
829
|
+
const columns = packed.get(alias);
|
|
830
|
+
if (columns) {
|
|
831
|
+
columns.add(column);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
packed.set(alias, new Set([column]));
|
|
835
|
+
}
|
|
836
|
+
};
|
|
837
|
+
const usage = (0, malloy_types_1.isRawSegment)(this.firstSegment)
|
|
838
|
+
? []
|
|
839
|
+
: ((_a = this.firstSegment.expandedFieldUsage) !== null && _a !== void 0 ? _a : []);
|
|
840
|
+
for (const { path } of usage) {
|
|
841
|
+
needed(this.packedColumnFor(this.parent, path));
|
|
842
|
+
}
|
|
843
|
+
// What a primary key needs is what it reads: a computed key's name is
|
|
844
|
+
// not a column, and the columns its expression reads are named nowhere
|
|
845
|
+
// else in the query.
|
|
846
|
+
for (const ji of this.rootResult.joins.values()) {
|
|
847
|
+
const primaryKey = ji.queryStruct.primaryKey();
|
|
848
|
+
if (primaryKey === undefined)
|
|
849
|
+
continue;
|
|
850
|
+
const keyDef = primaryKey.fieldDef;
|
|
851
|
+
if ((0, malloy_types_1.hasExpression)(keyDef)) {
|
|
852
|
+
for (const { path } of (0, malloy_types_1.fieldUsageFrom)(keyDef.refSummary)) {
|
|
853
|
+
needed(this.packedColumnFor(ji.queryStruct, path));
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
else {
|
|
857
|
+
needed(this.packedColumnFor(ji.queryStruct, [(0, malloy_types_1.activeName)(keyDef)]));
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
this.packedColumns = packed;
|
|
861
|
+
}
|
|
862
|
+
return this.packedColumns;
|
|
863
|
+
}
|
|
864
|
+
// What a join contributes to the SELECT of a filtered join's subquery: every
|
|
865
|
+
// column named outside the subquery, and the generated __distinct_key when
|
|
866
|
+
// the join needed one.
|
|
867
|
+
joinProjectionFieldList(ji) {
|
|
868
|
+
const packed = this.packedColumnsByJoin().get(ji.alias);
|
|
869
|
+
const dialect = ji.queryStruct.dialect;
|
|
870
|
+
const fieldList = (0, utils_1.getDialectFieldList)(ji.queryStruct.structDef, dialect).filter(d => packed === null || packed === void 0 ? void 0 : packed.has(d.rawName));
|
|
871
|
+
if (ji.makeUniqueKey) {
|
|
872
|
+
fieldList.push({
|
|
873
|
+
typeDef: { type: 'string' },
|
|
874
|
+
sqlExpression: dialect.sqlQuoteIdentifier('__distinct_key'),
|
|
875
|
+
rawName: '__distinct_key',
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
return fieldList;
|
|
879
|
+
}
|
|
749
880
|
// BigQuery has wildcard psudo columns that are treated differently
|
|
750
881
|
// SELECT * FROM xxx doesn't include these psuedo columns but we need them so
|
|
751
882
|
// filters can get pushed down properly when generating a UNIQUE key.
|
|
@@ -1510,8 +1641,6 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1510
1641
|
buildDialectFieldList(resultStruct) {
|
|
1511
1642
|
const dialectFieldList = [];
|
|
1512
1643
|
for (const [name, field] of resultStruct.allFields) {
|
|
1513
|
-
const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
|
|
1514
|
-
//
|
|
1515
1644
|
if (resultStruct.firstSegment.type === 'reduce' &&
|
|
1516
1645
|
field instanceof field_instance_1.FieldInstanceResult) {
|
|
1517
1646
|
const { structDef, repeatedResultType } = this.generateTurtlePipelineSQL(field, new stage_writer_1.StageWriter(true, undefined), '<nosource>');
|
|
@@ -1527,7 +1656,6 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1527
1656
|
typeDef: multiLineNest,
|
|
1528
1657
|
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1529
1658
|
rawName: name,
|
|
1530
|
-
sqlOutputName: sqlName,
|
|
1531
1659
|
});
|
|
1532
1660
|
}
|
|
1533
1661
|
else {
|
|
@@ -1541,7 +1669,6 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1541
1669
|
typeDef: oneLineNest,
|
|
1542
1670
|
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1543
1671
|
rawName: name,
|
|
1544
|
-
sqlOutputName: sqlName,
|
|
1545
1672
|
});
|
|
1546
1673
|
}
|
|
1547
1674
|
}
|
|
@@ -1552,7 +1679,6 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1552
1679
|
fieldDef: field.f.fieldDef,
|
|
1553
1680
|
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1554
1681
|
rawName: name,
|
|
1555
|
-
sqlOutputName: sqlName,
|
|
1556
1682
|
});
|
|
1557
1683
|
}
|
|
1558
1684
|
else if (resultStruct.firstSegment.type === 'project' &&
|
|
@@ -1562,7 +1688,6 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1562
1688
|
fieldDef: field.f.fieldDef,
|
|
1563
1689
|
sqlExpression: field.generateExpression(),
|
|
1564
1690
|
rawName: name,
|
|
1565
|
-
sqlOutputName: sqlName,
|
|
1566
1691
|
});
|
|
1567
1692
|
}
|
|
1568
1693
|
}
|
|
@@ -1684,7 +1809,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1684
1809
|
const inputStruct = {
|
|
1685
1810
|
type: 'nest_source',
|
|
1686
1811
|
name: '~pipe~',
|
|
1687
|
-
pipeSQL: this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === 'inline_all_numbers', sourceSQLExpression, (0, utils_1.getDialectFieldList)(structDef)),
|
|
1812
|
+
pipeSQL: this.parent.dialect.sqlUnnestPipelineHead(repeatedResultType === 'inline_all_numbers', sourceSQLExpression, (0, utils_1.getDialectFieldList)(structDef, this.parent.dialect)),
|
|
1688
1813
|
fields: structDef.fields,
|
|
1689
1814
|
connection: structDef.connection,
|
|
1690
1815
|
dialect: structDef.dialect,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { SQLSourceRequest } from '../lang/translate-response';
|
|
2
|
-
import type { SQLPhraseSegment, ModelDef } from './malloy_types';
|
|
2
|
+
import type { SQLPhraseSegment, ModelDef, VirtualMap } from './malloy_types';
|
|
3
3
|
/**
|
|
4
4
|
* The translator needs to know the output schema of an SQLSourceDef
|
|
5
5
|
* this will prepare an SQL string suitable to answer that question.
|
|
6
6
|
*/
|
|
7
|
-
export declare function getSourceRequest(select: SQLPhraseSegment[], connection: string, partialModel: ModelDef | undefined): SQLSourceRequest;
|
|
7
|
+
export declare function getSourceRequest(select: SQLPhraseSegment[], connection: string, partialModel: ModelDef | undefined, virtualMap?: VirtualMap): SQLSourceRequest;
|
|
8
8
|
export declare function sqlKey(connectionName: string, sql: string): string;
|
package/dist/model/sql_block.js
CHANGED
|
@@ -7,62 +7,26 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
exports.getSourceRequest = getSourceRequest;
|
|
8
8
|
exports.sqlKey = sqlKey;
|
|
9
9
|
const query_model_1 = require("./query_model");
|
|
10
|
-
const
|
|
10
|
+
const sql_compiled_1 = require("./sql_compiled");
|
|
11
11
|
const utils_1 = require("./utils");
|
|
12
12
|
/**
|
|
13
13
|
* The translator needs to know the output schema of an SQLSourceDef
|
|
14
14
|
* this will prepare an SQL string suitable to answer that question.
|
|
15
15
|
*/
|
|
16
|
-
function getSourceRequest(select, connection, partialModel) {
|
|
17
|
-
let queryModel
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
selectStr += segment.sql;
|
|
23
|
-
parenAlready = segment.sql.match(/\(\s*$/) !== null;
|
|
24
|
-
}
|
|
25
|
-
else if ((0, malloy_types_1.isSegmentSource)(segment)) {
|
|
26
|
-
// PersistableSourceDef (sql_select or query_source)
|
|
27
|
-
let compiledSql;
|
|
28
|
-
if (segment.type === 'sql_select') {
|
|
29
|
-
compiledSql = segment.selectStr;
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
// query_source - compile the inner query
|
|
33
|
-
if (!queryModel) {
|
|
34
|
-
if (!partialModel) {
|
|
35
|
-
throw new Error('Internal error: Partial model missing when compiling SQL block');
|
|
36
|
-
}
|
|
37
|
-
queryModel = (0, query_model_1.makeQueryModel)(partialModel);
|
|
38
|
-
}
|
|
39
|
-
compiledSql = queryModel.compileQuery(segment.query, {
|
|
40
|
-
defaultRowLimit: undefined,
|
|
41
|
-
isPartialQuery: true,
|
|
42
|
-
}, false).sql;
|
|
16
|
+
function getSourceRequest(select, connection, partialModel, virtualMap) {
|
|
17
|
+
let queryModel;
|
|
18
|
+
const compileQuery = (query, options) => {
|
|
19
|
+
if (!queryModel) {
|
|
20
|
+
if (!partialModel) {
|
|
21
|
+
throw new Error('Internal error: Partial model missing when compiling SQL block');
|
|
43
22
|
}
|
|
44
|
-
|
|
45
|
-
parenAlready = false;
|
|
23
|
+
queryModel = (0, query_model_1.makeQueryModel)(partialModel);
|
|
46
24
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
if (!queryModel) {
|
|
50
|
-
if (!partialModel) {
|
|
51
|
-
throw new Error('Internal error: Partial model missing when compiling SQL block');
|
|
52
|
-
}
|
|
53
|
-
queryModel = (0, query_model_1.makeQueryModel)(partialModel);
|
|
54
|
-
}
|
|
55
|
-
const compiledSql = queryModel.compileQuery(segment, {
|
|
56
|
-
defaultRowLimit: undefined,
|
|
57
|
-
isPartialQuery: true,
|
|
58
|
-
}, false).sql;
|
|
59
|
-
selectStr += parenAlready ? compiledSql : `(${compiledSql})`;
|
|
60
|
-
parenAlready = false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
25
|
+
return queryModel.compileQuery(query, { ...options, defaultRowLimit: undefined, isPartialQuery: true }, false).sql;
|
|
26
|
+
};
|
|
63
27
|
return {
|
|
64
28
|
connection,
|
|
65
|
-
selectStr,
|
|
29
|
+
selectStr: (0, sql_compiled_1.expandSQLSegments)(select, { virtualMap }, compileQuery),
|
|
66
30
|
};
|
|
67
31
|
}
|
|
68
32
|
function sqlKey(connectionName, sql) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PrepareResultOptions, SQLSourceDef, PersistableSourceDef, Query, VirtualMap } from './malloy_types';
|
|
1
|
+
import type { PrepareResultOptions, SQLSourceDef, SQLPhraseSegment, PersistableSourceDef, Query, VirtualMap } from './malloy_types';
|
|
2
2
|
/**
|
|
3
3
|
* Callback type for compiling a Query to SQL.
|
|
4
4
|
* opts is optional - omit for "full SQL" (BuildID computation),
|
|
@@ -16,6 +16,8 @@ export type CompileQueryCallback = (query: Query, opts?: PrepareResultOptions) =
|
|
|
16
16
|
* @param compileQuery Callback to compile a Query to SQL
|
|
17
17
|
*/
|
|
18
18
|
export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
|
|
19
|
+
/** Expand SQL text and interpolations for schema discovery or execution. */
|
|
20
|
+
export declare function expandSQLSegments(segments: SQLPhraseSegment[], opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
|
|
19
21
|
/**
|
|
20
22
|
* The options a BuildID's SQL is compiled under — everything the builder and
|
|
21
23
|
* the compiler must both know, and nothing else.
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.getCompiledSQL = getCompiledSQL;
|
|
8
|
+
exports.expandSQLSegments = expandSQLSegments;
|
|
8
9
|
exports.buildIdOptions = buildIdOptions;
|
|
9
10
|
exports.persistedTableFor = persistedTableFor;
|
|
10
11
|
exports.getSourceSQL = getSourceSQL;
|
|
@@ -26,12 +27,13 @@ function getCompiledSQL(src, opts, compileQuery) {
|
|
|
26
27
|
if (!src.selectSegments || src.selectSegments.length === 0) {
|
|
27
28
|
return src.selectStr;
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
30
|
+
return expandSQLSegments(src.selectSegments, opts, compileQuery);
|
|
31
|
+
}
|
|
32
|
+
/** Expand SQL text and interpolations for schema discovery or execution. */
|
|
33
|
+
function expandSQLSegments(segments, opts, compileQuery) {
|
|
34
|
+
return segments
|
|
35
|
+
.map(segment => expandSegment(segment, opts, compileQuery))
|
|
36
|
+
.join('');
|
|
35
37
|
}
|
|
36
38
|
/**
|
|
37
39
|
* Expand a single SQLPhraseSegment to SQL.
|
|
@@ -51,7 +51,7 @@ class StageWriter {
|
|
|
51
51
|
if (lastStageName === undefined) {
|
|
52
52
|
throw new Error('Internal Error: no stage to combine');
|
|
53
53
|
}
|
|
54
|
-
sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, (0, utils_1.getDialectFieldList)(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
|
|
54
|
+
sql += dialect.sqlCreateFunctionCombineLastStage(lastStageName, (0, utils_1.getDialectFieldList)(structDef, dialect), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
|
|
55
55
|
const id = `${dialect.udfPrefix}${this.root().udfs.length}`;
|
|
56
56
|
sql = dialect.sqlCreateFunction(id, sql);
|
|
57
57
|
this.root().udfs.push(sql);
|
|
@@ -98,11 +98,11 @@ class StageWriter {
|
|
|
98
98
|
generateCoorelatedSubQuery(dialect, structDef) {
|
|
99
99
|
var _a, _b;
|
|
100
100
|
if (!this.useCTE) {
|
|
101
|
-
return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, (0, utils_1.getDialectFieldList)(structDef), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
|
|
101
|
+
return dialect.sqlCreateFunctionCombineLastStage(`(${this.withs[0]})`, (0, utils_1.getDialectFieldList)(structDef, dialect), (_a = structDef.resultMetadata) === null || _a === void 0 ? void 0 : _a.orderBy);
|
|
102
102
|
}
|
|
103
103
|
else {
|
|
104
104
|
return (this.combineStages(true).sql +
|
|
105
|
-
dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), (0, utils_1.getDialectFieldList)(structDef), (_b = structDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy));
|
|
105
|
+
dialect.sqlCreateFunctionCombineLastStage(this.getName(this.withs.length - 1), (0, utils_1.getDialectFieldList)(structDef, dialect), (_b = structDef.resultMetadata) === null || _b === void 0 ? void 0 : _b.orderBy));
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
}
|
package/dist/model/utils.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AtomicTypeDef, Expr, FieldDef, GenericSQLExpr, GivenTypeDef, ModelDef, ModelID, StructDef } from './malloy_types';
|
|
2
|
-
import type { DialectFieldList } from '../dialect';
|
|
2
|
+
import type { Dialect, DialectFieldList } from '../dialect';
|
|
3
3
|
/**
|
|
4
4
|
* Format a typeDef as user-readable Malloy type syntax. Used in error
|
|
5
5
|
* messages where we want to echo a compound type back to the user in the
|
|
@@ -58,7 +58,7 @@ export type SQLExprElement = string | Expr;
|
|
|
58
58
|
* expression node.
|
|
59
59
|
*/
|
|
60
60
|
export declare function composeSQLExpr(from: SQLExprElement[]): GenericSQLExpr;
|
|
61
|
-
export declare function getDialectFieldList(structDef: StructDef): DialectFieldList;
|
|
61
|
+
export declare function getDialectFieldList(structDef: StructDef, dialect: Dialect): DialectFieldList;
|
|
62
62
|
/**
|
|
63
63
|
* A little extra paranoia to save us from fields which contain the special
|
|
64
64
|
* characters used to build the grouping key.
|
package/dist/model/utils.js
CHANGED
|
@@ -251,14 +251,13 @@ function composeSQLExpr(from) {
|
|
|
251
251
|
}
|
|
252
252
|
return ret;
|
|
253
253
|
}
|
|
254
|
-
function getDialectFieldList(structDef) {
|
|
254
|
+
function getDialectFieldList(structDef, dialect) {
|
|
255
255
|
const dialectFieldList = [];
|
|
256
256
|
for (const f of structDef.fields.filter(malloy_types_1.fieldIsIntrinsic)) {
|
|
257
257
|
dialectFieldList.push({
|
|
258
258
|
typeDef: f,
|
|
259
|
-
sqlExpression: (0, malloy_types_1.activeName)(f),
|
|
259
|
+
sqlExpression: dialect.sqlQuoteIdentifier((0, malloy_types_1.activeName)(f)),
|
|
260
260
|
rawName: (0, malloy_types_1.activeName)(f),
|
|
261
|
-
sqlOutputName: (0, malloy_types_1.activeName)(f),
|
|
262
261
|
});
|
|
263
262
|
}
|
|
264
263
|
return dialectFieldList;
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const MALLOY_VERSION = "0.0.
|
|
1
|
+
export declare const MALLOY_VERSION = "0.0.434";
|
package/dist/version.js
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MALLOY_VERSION = void 0;
|
|
4
4
|
// generated with 'generate-version-file' script; do not edit manually
|
|
5
|
-
exports.MALLOY_VERSION = '0.0.
|
|
5
|
+
exports.MALLOY_VERSION = '0.0.434';
|
|
6
6
|
//# sourceMappingURL=version.js.map
|
package/femto-config.motly
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloydata/malloy",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.434",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./dist/index.js",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"generate-version-file": "VERSION=$(npm pkg get version --workspaces=false | tr -d \\\")\necho \"// generated with 'generate-version-file' script; do not edit manually\\nexport const MALLOY_VERSION = '$VERSION';\" > src/version.ts"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@malloydata/malloy-filter": "0.0.
|
|
55
|
-
"@malloydata/malloy-interfaces": "0.0.
|
|
56
|
-
"@malloydata/malloy-tag": "0.0.
|
|
54
|
+
"@malloydata/malloy-filter": "0.0.434",
|
|
55
|
+
"@malloydata/malloy-interfaces": "0.0.434",
|
|
56
|
+
"@malloydata/malloy-tag": "0.0.434",
|
|
57
57
|
"@noble/hashes": "^1.8.0",
|
|
58
58
|
"antlr4ts": "^0.5.0-alpha.4",
|
|
59
59
|
"assert": "^2.0.0",
|