@malloydata/malloy 0.0.394 → 0.0.395
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/config.d.ts +2 -3
- package/dist/api/foundation/config.js +23 -11
- package/dist/api/foundation/core.js +1 -1
- package/dist/api/foundation/runtime.js +21 -1
- package/dist/api/util.js +4 -0
- package/dist/connection/base_connection.js +6 -0
- package/dist/connection/validate_table_path.d.ts +10 -0
- package/dist/connection/validate_table_path.js +56 -0
- package/dist/dialect/databricks/databricks.d.ts +4 -4
- package/dist/dialect/databricks/databricks.js +17 -22
- package/dist/dialect/dialect.d.ts +100 -4
- package/dist/dialect/dialect.js +145 -1
- package/dist/dialect/duckdb/duckdb.d.ts +2 -3
- package/dist/dialect/duckdb/duckdb.js +12 -14
- package/dist/dialect/duckdb/table-path-parser.d.ts +2 -0
- package/dist/dialect/duckdb/table-path-parser.js +57 -0
- package/dist/dialect/index.d.ts +2 -0
- package/dist/dialect/index.js +4 -1
- package/dist/dialect/mysql/mysql.d.ts +4 -4
- package/dist/dialect/mysql/mysql.js +25 -20
- package/dist/dialect/pg_impl.d.ts +3 -1
- package/dist/dialect/pg_impl.js +6 -3
- package/dist/dialect/postgres/postgres.d.ts +1 -3
- package/dist/dialect/postgres/postgres.js +8 -16
- package/dist/dialect/snowflake/snowflake.d.ts +4 -4
- package/dist/dialect/snowflake/snowflake.js +11 -27
- package/dist/dialect/standardsql/standardsql.d.ts +6 -4
- package/dist/dialect/standardsql/standardsql.js +36 -15
- package/dist/dialect/table-path.d.ts +54 -0
- package/dist/dialect/table-path.js +144 -0
- package/dist/dialect/trino/trino.d.ts +0 -3
- package/dist/dialect/trino/trino.js +7 -20
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -2
- package/dist/lang/ast/source-elements/table-source.d.ts +1 -7
- package/dist/lang/ast/source-elements/table-source.js +20 -19
- package/dist/lang/parse-log.d.ts +1 -0
- package/dist/lang/parse-malloy.js +37 -7
- package/dist/lang/parse-tree-walkers/find-external-references.d.ts +2 -15
- package/dist/lang/parse-tree-walkers/find-external-references.js +6 -23
- package/dist/lang/translate-response.d.ts +1 -1
- package/dist/model/filter_compilers.js +1 -1
- package/dist/model/query_model_impl.js +7 -7
- package/dist/model/query_query.js +37 -33
- package/dist/model/sql_compiled.d.ts +2 -4
- package/dist/model/sql_compiled.js +14 -15
- package/dist/test/test-models.js +2 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -4
|
@@ -22,24 +22,38 @@
|
|
|
22
22
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
25
|
+
exports.TableMethodSource = exports.TableSource = void 0;
|
|
26
26
|
const find_external_references_1 = require("../../parse-tree-walkers/find-external-references");
|
|
27
27
|
const source_1 = require("./source");
|
|
28
28
|
const error_factory_1 = require("../error-factory");
|
|
29
|
+
const dialect_1 = require("../../../dialect");
|
|
29
30
|
class TableSource extends source_1.Source {
|
|
30
31
|
getSourceDef() {
|
|
31
|
-
var _a, _b, _c;
|
|
32
|
+
var _a, _b, _c, _d;
|
|
32
33
|
const info = this.getTableInfo();
|
|
33
34
|
if (info === undefined) {
|
|
34
35
|
return error_factory_1.ErrorFactory.structDef;
|
|
35
36
|
}
|
|
36
|
-
const { tablePath, connectionName } = info;
|
|
37
|
+
const { tablePath: rawTablePath, connectionName } = info;
|
|
38
|
+
// Re-validate the table path. ImportsAndTablesStep validated and
|
|
39
|
+
// silently skipped invalid entries; we re-validate here so we can
|
|
40
|
+
// log a precise translator error at the AST element's location.
|
|
41
|
+
let tablePath = rawTablePath;
|
|
42
|
+
const dialectName = (_a = this.translator()) === null || _a === void 0 ? void 0 : _a.root.connectionDialectZone.get(connectionName);
|
|
43
|
+
if (dialectName !== undefined) {
|
|
44
|
+
const validation = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(rawTablePath);
|
|
45
|
+
if (!validation.ok) {
|
|
46
|
+
this.logError('invalid-table-path', validation.error);
|
|
47
|
+
return error_factory_1.ErrorFactory.structDef;
|
|
48
|
+
}
|
|
49
|
+
tablePath = validation.canonical;
|
|
50
|
+
}
|
|
37
51
|
const key = (0, find_external_references_1.constructTableKey)(connectionName, tablePath);
|
|
38
|
-
const tableDefEntry = (
|
|
52
|
+
const tableDefEntry = (_b = this.translator()) === null || _b === void 0 ? void 0 : _b.root.schemaZone.getEntry(key);
|
|
39
53
|
let msg = `Schema read failure for table '${tablePath}' for connection '${connectionName}'`;
|
|
40
54
|
if (tableDefEntry) {
|
|
41
55
|
if (tableDefEntry.status === 'present') {
|
|
42
|
-
(
|
|
56
|
+
(_c = this.document()) === null || _c === void 0 ? void 0 : _c.checkExperimentalDialect(this, tableDefEntry.value.dialect);
|
|
43
57
|
tableDefEntry.value.location = this.location;
|
|
44
58
|
tableDefEntry.value.fields.forEach(field => {
|
|
45
59
|
field.location = this.location;
|
|
@@ -58,7 +72,7 @@ class TableSource extends source_1.Source {
|
|
|
58
72
|
}),
|
|
59
73
|
location: this.location,
|
|
60
74
|
};
|
|
61
|
-
(
|
|
75
|
+
(_d = this.document()) === null || _d === void 0 ? void 0 : _d.rememberToAddModelAnnotations(ret);
|
|
62
76
|
return ret;
|
|
63
77
|
}
|
|
64
78
|
if (tableDefEntry.status === 'error') {
|
|
@@ -96,17 +110,4 @@ class TableMethodSource extends TableSource {
|
|
|
96
110
|
}
|
|
97
111
|
}
|
|
98
112
|
exports.TableMethodSource = TableMethodSource;
|
|
99
|
-
class TableFunctionSource extends TableSource {
|
|
100
|
-
constructor(tableURI) {
|
|
101
|
-
super();
|
|
102
|
-
this.tableURI = tableURI;
|
|
103
|
-
this.elementType = 'tableFunctionSource';
|
|
104
|
-
}
|
|
105
|
-
getTableInfo() {
|
|
106
|
-
// This use of `deprecatedParseTableURI` is ok because it is for handling the
|
|
107
|
-
// old, soon-to-be-deprecated table syntax.
|
|
108
|
-
return (0, find_external_references_1.deprecatedParseTableURI)(this.tableURI);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
exports.TableFunctionSource = TableFunctionSource;
|
|
112
113
|
//# sourceMappingURL=table-source.js.map
|
package/dist/lang/parse-log.d.ts
CHANGED
|
@@ -275,6 +275,7 @@ type MessageParameterTypes = {
|
|
|
275
275
|
'parameter-name-conflict': string;
|
|
276
276
|
'parameter-shadowing-field': string;
|
|
277
277
|
'invalid-import-url': string;
|
|
278
|
+
'invalid-table-path': string;
|
|
278
279
|
'no-translator-for-import': string;
|
|
279
280
|
'name-conflict-on-selective-import': string;
|
|
280
281
|
'selective-import-not-found': string;
|
|
@@ -61,6 +61,7 @@ const ast = __importStar(require("./ast"));
|
|
|
61
61
|
const malloy_to_ast_1 = require("./malloy-to-ast");
|
|
62
62
|
const parse_log_1 = require("./parse-log");
|
|
63
63
|
const find_external_references_1 = require("./parse-tree-walkers/find-external-references");
|
|
64
|
+
const dialect_1 = require("../dialect");
|
|
64
65
|
const zone_1 = require("./zone");
|
|
65
66
|
const document_symbol_walker_1 = require("./parse-tree-walkers/document-symbol-walker");
|
|
66
67
|
const document_completion_walker_1 = require("./parse-tree-walkers/document-completion-walker");
|
|
@@ -140,12 +141,9 @@ class ImportsAndTablesStep {
|
|
|
140
141
|
}
|
|
141
142
|
if (!this.parseReferences) {
|
|
142
143
|
this.parseReferences = (0, find_external_references_1.findReferences)(that, parseReq.parse.tokenStream, parseReq.parse.root);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
range: this.parseReferences.tables[ref].firstReference,
|
|
147
|
-
});
|
|
148
|
-
}
|
|
144
|
+
// Register connection dialects and imports immediately. Table
|
|
145
|
+
// references are deferred until dialects are resolved, because
|
|
146
|
+
// validating a table path requires knowing its dialect's grammar.
|
|
149
147
|
for (const connName in this.parseReferences.connectionDialects) {
|
|
150
148
|
that.root.connectionDialectZone.reference(connName, {
|
|
151
149
|
url: that.sourceURL,
|
|
@@ -180,17 +178,49 @@ class ImportsAndTablesStep {
|
|
|
180
178
|
return { timingInfo: parseReq.timingInfo };
|
|
181
179
|
}
|
|
182
180
|
let allMissing = {};
|
|
181
|
+
// Validate each table reference against its dialect's grammar (if
|
|
182
|
+
// the dialect is resolved) and register the canonical entry. Bad
|
|
183
|
+
// paths are silently dropped — the AST step re-validates and logs
|
|
184
|
+
// an error at the precise source range. Entries whose dialect
|
|
185
|
+
// isn't resolved yet are preserved unchanged and re-processed on a
|
|
186
|
+
// later step.
|
|
187
|
+
{
|
|
188
|
+
const refs = this.parseReferences.tables;
|
|
189
|
+
const canonical = {};
|
|
190
|
+
for (const rawKey in refs) {
|
|
191
|
+
const info = refs[rawKey];
|
|
192
|
+
let { tablePath } = info;
|
|
193
|
+
const dialectName = that.root.connectionDialectZone.get(info.connectionName);
|
|
194
|
+
if (dialectName !== undefined) {
|
|
195
|
+
const result = (0, dialect_1.getDialect)(dialectName).sqlValidateTableName(tablePath);
|
|
196
|
+
if (!result.ok)
|
|
197
|
+
continue;
|
|
198
|
+
tablePath = result.canonical;
|
|
199
|
+
}
|
|
200
|
+
const key = `${info.connectionName}:${tablePath}`;
|
|
201
|
+
canonical[key] = { ...info, tablePath };
|
|
202
|
+
that.root.schemaZone.reference(key, {
|
|
203
|
+
url: that.sourceURL,
|
|
204
|
+
range: info.firstReference,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
this.parseReferences.tables = canonical;
|
|
208
|
+
}
|
|
183
209
|
const missingTables = that.root.schemaZone.getUndefined();
|
|
184
210
|
if (missingTables) {
|
|
185
211
|
const tables = {};
|
|
186
212
|
for (const key of missingTables) {
|
|
187
213
|
const info = this.parseReferences.tables[key];
|
|
214
|
+
if (info === undefined)
|
|
215
|
+
continue;
|
|
188
216
|
tables[key] = {
|
|
189
217
|
connectionName: info.connectionName,
|
|
190
218
|
tablePath: info.tablePath,
|
|
191
219
|
};
|
|
192
220
|
}
|
|
193
|
-
|
|
221
|
+
if (Object.keys(tables).length > 0) {
|
|
222
|
+
allMissing = { tables };
|
|
223
|
+
}
|
|
194
224
|
}
|
|
195
225
|
const missingDialects = that.root.connectionDialectZone.getUndefined();
|
|
196
226
|
if (missingDialects) {
|
|
@@ -4,27 +4,14 @@ import type { DocumentRange } from '../../model/malloy_types';
|
|
|
4
4
|
import type { MalloyTranslation } from '../parse-malloy';
|
|
5
5
|
type NeedImports = Record<string, DocumentRange>;
|
|
6
6
|
type NeedTables = Record<string, {
|
|
7
|
-
connectionName: string
|
|
7
|
+
connectionName: string;
|
|
8
8
|
tablePath: string;
|
|
9
9
|
firstReference: DocumentRange;
|
|
10
10
|
}>;
|
|
11
11
|
type NeedConnectionDialects = Record<string, {
|
|
12
12
|
firstReference: DocumentRange;
|
|
13
13
|
}>;
|
|
14
|
-
export declare function constructTableKey(connectionName: string
|
|
15
|
-
/**
|
|
16
|
-
* This function parses an old-style `tableURI` into a connection name and
|
|
17
|
-
* table path. The name includes `deprecated` because it should only be used
|
|
18
|
-
* in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
|
|
19
|
-
* anywhere else is bad.
|
|
20
|
-
* @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
|
|
21
|
-
* @returns A connection name and table path.
|
|
22
|
-
* @deprecated
|
|
23
|
-
*/
|
|
24
|
-
export declare function deprecatedParseTableURI(tableURI: string): {
|
|
25
|
-
connectionName?: string;
|
|
26
|
-
tablePath: string;
|
|
27
|
-
};
|
|
14
|
+
export declare function constructTableKey(connectionName: string, tablePath: string): string;
|
|
28
15
|
export interface FindReferencesData {
|
|
29
16
|
tables: NeedTables;
|
|
30
17
|
urls: NeedImports;
|
|
@@ -23,7 +23,6 @@
|
|
|
23
23
|
*/
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.constructTableKey = constructTableKey;
|
|
26
|
-
exports.deprecatedParseTableURI = deprecatedParseTableURI;
|
|
27
26
|
exports.findReferences = findReferences;
|
|
28
27
|
const ParseTreeWalker_1 = require("antlr4ts/tree/ParseTreeWalker");
|
|
29
28
|
const parse_utils_1 = require("../parse-utils");
|
|
@@ -70,6 +69,11 @@ class FindExternalReferences {
|
|
|
70
69
|
const tablePath = getPlainString(pcx.tablePath());
|
|
71
70
|
const reference = this.trans.rangeFromContext(pcx);
|
|
72
71
|
this.registerTableReference(connId, tablePath, reference);
|
|
72
|
+
// Register a need for the connection's dialect so the validator in
|
|
73
|
+
// ImportsAndTablesStep can run against it.
|
|
74
|
+
if (!this.needConnectionDialects[connId]) {
|
|
75
|
+
this.needConnectionDialects[connId] = { firstReference: reference };
|
|
76
|
+
}
|
|
73
77
|
}
|
|
74
78
|
enterVirtualSource(pcx) {
|
|
75
79
|
const connId = (0, parse_utils_1.getId)(pcx.connectionId());
|
|
@@ -87,28 +91,7 @@ class FindExternalReferences {
|
|
|
87
91
|
}
|
|
88
92
|
}
|
|
89
93
|
function constructTableKey(connectionName, tablePath) {
|
|
90
|
-
return connectionName
|
|
91
|
-
? tablePath
|
|
92
|
-
: `${connectionName}:${tablePath}`;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* This function parses an old-style `tableURI` into a connection name and
|
|
96
|
-
* table path. The name includes `deprecated` because it should only be used
|
|
97
|
-
* in the (deprecated) old-style `table('conn:tab')` syntax. Any use of this
|
|
98
|
-
* anywhere else is bad.
|
|
99
|
-
* @param tableURI The sting that is passed into the `table('conn:tab')` syntax.
|
|
100
|
-
* @returns A connection name and table path.
|
|
101
|
-
* @deprecated
|
|
102
|
-
*/
|
|
103
|
-
function deprecatedParseTableURI(tableURI) {
|
|
104
|
-
const parts = tableURI.match(/^([^:]*):(.*)$/);
|
|
105
|
-
if (parts) {
|
|
106
|
-
const [, firstPart, secondPart] = parts;
|
|
107
|
-
return { connectionName: firstPart, tablePath: secondPart };
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
return { tablePath: tableURI };
|
|
111
|
-
}
|
|
94
|
+
return `${connectionName}:${tablePath}`;
|
|
112
95
|
}
|
|
113
96
|
function findReferences(trans, tokens, parseTree) {
|
|
114
97
|
const finder = new FindExternalReferences(trans, tokens);
|
|
@@ -22,7 +22,7 @@ export interface ProblemResponse {
|
|
|
22
22
|
export type FatalResponse = FinalResponse & ProblemResponse;
|
|
23
23
|
export interface NeedSchemaData {
|
|
24
24
|
tables: Record<string, {
|
|
25
|
-
connectionName: string
|
|
25
|
+
connectionName: string;
|
|
26
26
|
tablePath: string;
|
|
27
27
|
}>;
|
|
28
28
|
}
|
|
@@ -129,7 +129,7 @@ exports.FilterCompilers = {
|
|
|
129
129
|
// For some databases checking NULL combined with a boolean check
|
|
130
130
|
// is faster than a COALESCE, for now, just detect if the expression
|
|
131
131
|
// is just a column reference, and if so, don't use COALECSE.
|
|
132
|
-
const quoteChar = d.
|
|
132
|
+
const quoteChar = d.sqlQuoteIdentifier('select')[0];
|
|
133
133
|
const isColumn = x.match(`^[()${quoteChar}\\w.]+$`);
|
|
134
134
|
if (isColumn) {
|
|
135
135
|
if (bc.operator === 'true') {
|
|
@@ -107,12 +107,12 @@ class QueryModelImpl {
|
|
|
107
107
|
const fieldNames = [];
|
|
108
108
|
for (const f of ret.outputStruct.fields) {
|
|
109
109
|
if ((0, malloy_types_1.isAtomic)(f)) {
|
|
110
|
-
const quoted = q.parent.dialect.
|
|
110
|
+
const quoted = q.parent.dialect.sqlQuoteIdentifier(f.name);
|
|
111
111
|
fieldNames.push(quoted);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
// const fieldNames = getAtomicFields(ret.outputStruct).map(fieldDef =>
|
|
115
|
-
// q.parent.dialect.
|
|
115
|
+
// q.parent.dialect.sqlQuoteIdentifier(fieldDef.name)
|
|
116
116
|
// );
|
|
117
117
|
ret.lastStageName = stageWriter.addStage(q.parent.dialect.sqlFinalStage(ret.lastStageName, fieldNames));
|
|
118
118
|
}
|
|
@@ -217,11 +217,11 @@ class QueryModelImpl {
|
|
|
217
217
|
},
|
|
218
218
|
],
|
|
219
219
|
};
|
|
220
|
-
const fieldNameColumn = d.
|
|
221
|
-
const fieldPathColumn = d.
|
|
222
|
-
const fieldValueColumn = d.
|
|
223
|
-
const fieldTypeColumn = d.
|
|
224
|
-
const weightColumn = d.
|
|
220
|
+
const fieldNameColumn = d.sqlQuoteIdentifier('fieldName');
|
|
221
|
+
const fieldPathColumn = d.sqlQuoteIdentifier('fieldPath');
|
|
222
|
+
const fieldValueColumn = d.sqlQuoteIdentifier('fieldValue');
|
|
223
|
+
const fieldTypeColumn = d.sqlQuoteIdentifier('fieldType');
|
|
224
|
+
const weightColumn = d.sqlQuoteIdentifier('weight');
|
|
225
225
|
// if we've compiled the SQL before use it otherwise
|
|
226
226
|
let sqlPDT = this.exploreSearchSQLMap.get(explore);
|
|
227
227
|
if (sqlPDT === undefined) {
|
|
@@ -517,14 +517,17 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
517
517
|
var _a, _b, _c, _d;
|
|
518
518
|
switch (qs.structDef.type) {
|
|
519
519
|
case 'table':
|
|
520
|
-
|
|
520
|
+
// tablePath is canonical SQL — translator pre-validated.
|
|
521
|
+
return qs.structDef.tablePath;
|
|
521
522
|
case 'virtual': {
|
|
522
523
|
const virtualMap = (_a = qs.prepareResultOptions) === null || _a === void 0 ? void 0 : _a.virtualMap;
|
|
523
524
|
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);
|
|
524
525
|
if (!tablePath) {
|
|
525
526
|
throw new Error(`No virtual map entry for '${qs.structDef.name}' on connection '${qs.structDef.connection}'`);
|
|
526
527
|
}
|
|
527
|
-
|
|
528
|
+
// virtualMap entries are application-supplied — assumed already
|
|
529
|
+
// canonical SQL.
|
|
530
|
+
return tablePath;
|
|
528
531
|
}
|
|
529
532
|
case 'composite':
|
|
530
533
|
// TODO: throw an error here; not simple because we call into this
|
|
@@ -533,7 +536,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
533
536
|
case 'finalize':
|
|
534
537
|
return qs.structDef.name;
|
|
535
538
|
case 'sql_select':
|
|
536
|
-
return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_c = qs.prepareResultOptions) !== null && _c !== void 0 ? _c : {},
|
|
539
|
+
return `(${(0, sql_compiled_1.getCompiledSQL)(qs.structDef, (_c = qs.prepareResultOptions) !== null && _c !== void 0 ? _c : {}, (query, opts) => {
|
|
537
540
|
// Compile query to isolated SQL (not into parent's stageWriter)
|
|
538
541
|
const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
|
|
539
542
|
return ret.sql;
|
|
@@ -551,8 +554,9 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
551
554
|
const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, fullRet.sql);
|
|
552
555
|
const entry = buildManifest.entries[buildId];
|
|
553
556
|
if (entry) {
|
|
554
|
-
// Found in manifest - use persisted table
|
|
555
|
-
|
|
557
|
+
// Found in manifest - use persisted table.
|
|
558
|
+
// entry.tableName comes from the manifest, assumed canonical.
|
|
559
|
+
return entry.tableName;
|
|
556
560
|
}
|
|
557
561
|
if (buildManifest.strict) {
|
|
558
562
|
const base = `Persist source '${qs.structDef.sourceID}' not found in manifest (buildId: ${buildId})`;
|
|
@@ -631,7 +635,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
631
635
|
}
|
|
632
636
|
if (ji.makeUniqueKey) {
|
|
633
637
|
const passKeys = this.generateSQLPassthroughKeys(qs);
|
|
634
|
-
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.
|
|
638
|
+
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
|
|
635
639
|
}
|
|
636
640
|
let onCondition = '';
|
|
637
641
|
if (qs.parent === undefined) {
|
|
@@ -764,7 +768,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
764
768
|
if ((0, malloy_types_1.isBaseTable)(qs.structDef)) {
|
|
765
769
|
if (ji.makeUniqueKey) {
|
|
766
770
|
const passKeys = this.generateSQLPassthroughKeys(qs);
|
|
767
|
-
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.
|
|
771
|
+
structSQL = `(SELECT ${qs.dialect.sqlGenerateUUID()} as ${qs.dialect.sqlQuoteIdentifier('__distinct_key')}, x.* ${passKeys} FROM ${structSQL} as x)`;
|
|
768
772
|
}
|
|
769
773
|
s += `FROM ${structSQL} as ${ji.alias}\n`;
|
|
770
774
|
}
|
|
@@ -846,7 +850,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
846
850
|
o.push(`${fi.fieldUsage.resultIndex} ${f.dir || 'ASC'}`);
|
|
847
851
|
}
|
|
848
852
|
else if (this.parent.dialect.orderByClause === 'output_name') {
|
|
849
|
-
o.push(`${this.parent.dialect.
|
|
853
|
+
o.push(`${this.parent.dialect.sqlQuoteIdentifier(f.field)} ${f.dir || 'ASC'}`);
|
|
850
854
|
}
|
|
851
855
|
else if (this.parent.dialect.orderByClause === 'expression') {
|
|
852
856
|
const fieldExpr = fi.getSQL();
|
|
@@ -863,7 +867,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
863
867
|
}
|
|
864
868
|
else if (this.parent.dialect.orderByClause === 'output_name') {
|
|
865
869
|
const orderingField = resultStruct.getFieldByNumber(f.field);
|
|
866
|
-
o.push(`${this.parent.dialect.
|
|
870
|
+
o.push(`${this.parent.dialect.sqlQuoteIdentifier(orderingField.name)} ${f.dir || 'ASC'}`);
|
|
867
871
|
}
|
|
868
872
|
else if (this.parent.dialect.orderByClause === 'expression') {
|
|
869
873
|
const orderingField = resultStruct.getFieldByNumber(f.field);
|
|
@@ -887,7 +891,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
887
891
|
const fields = [];
|
|
888
892
|
for (const [name, field] of this.rootResult.allFields) {
|
|
889
893
|
const fi = field;
|
|
890
|
-
const sqlName = this.parent.dialect.
|
|
894
|
+
const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
|
|
891
895
|
if (fi.fieldUsage.type === 'result') {
|
|
892
896
|
fields.push(` ${fi.generateExpression()} as ${sqlName}`);
|
|
893
897
|
}
|
|
@@ -937,7 +941,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
937
941
|
.map(o => `${o.pipelineSQL} as ${o.sqlFieldName}`)
|
|
938
942
|
.join(',\n');
|
|
939
943
|
const outputFields = outputPipelinedSQL.map(f => f.sqlFieldName);
|
|
940
|
-
const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.
|
|
944
|
+
const allFields = Array.from(this.rootResult.allFields.keys()).map(f => this.parent.dialect.sqlQuoteIdentifier(f));
|
|
941
945
|
const fields = allFields.filter(f => outputFields.indexOf(f) === -1);
|
|
942
946
|
retSQL = `SELECT ${fields.length > 0 ? fields.join(', ') + ',' : ''} ${pipelinesSQL} FROM ${lastStageName}`;
|
|
943
947
|
}
|
|
@@ -956,7 +960,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
956
960
|
}
|
|
957
961
|
const orderedFields = [...scalarFields, ...otherFields];
|
|
958
962
|
for (const [name, fi] of orderedFields) {
|
|
959
|
-
const outputName = this.parent.dialect.
|
|
963
|
+
const outputName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
|
|
960
964
|
if (fi instanceof field_instance_1.FieldInstanceField) {
|
|
961
965
|
if (fi.fieldUsage.type === 'result') {
|
|
962
966
|
const exp = fi.getSQL();
|
|
@@ -975,7 +979,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
975
979
|
});
|
|
976
980
|
output.sql.push(outputFieldName);
|
|
977
981
|
if (fi.f.fieldDef.type === 'number') {
|
|
978
|
-
const outputNameString = this.parent.dialect.
|
|
982
|
+
const outputNameString = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}_string`);
|
|
979
983
|
const outputFieldNameString = `__lateral_join_bag.${outputNameString}`;
|
|
980
984
|
output.sql.push(outputFieldNameString);
|
|
981
985
|
output.dimensionIndexes.push(output.fieldIndex++);
|
|
@@ -1086,7 +1090,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1086
1090
|
let r = result;
|
|
1087
1091
|
while (r) {
|
|
1088
1092
|
for (const name of r.fieldNames(fi => (0, query_node_1.isScalarField)(fi.f))) {
|
|
1089
|
-
dimensions.push(this.parent.dialect.
|
|
1093
|
+
dimensions.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${r.groupSet}`));
|
|
1090
1094
|
}
|
|
1091
1095
|
r = r.parent;
|
|
1092
1096
|
}
|
|
@@ -1120,7 +1124,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1120
1124
|
orderingField = result.getFieldByNumber(ordering.field);
|
|
1121
1125
|
}
|
|
1122
1126
|
obSQL.push(' ' +
|
|
1123
|
-
this.parent.dialect.
|
|
1127
|
+
this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${result.groupSet}`) +
|
|
1124
1128
|
` ${ordering.dir || 'ASC'}`);
|
|
1125
1129
|
}
|
|
1126
1130
|
// partition for a row number is the parent if it exists.
|
|
@@ -1225,7 +1229,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1225
1229
|
generateDepthNFields(depth, resultSet, output, stageWriter) {
|
|
1226
1230
|
const groupsToMap = [];
|
|
1227
1231
|
for (const [name, fi] of resultSet.allFields) {
|
|
1228
|
-
const sqlFieldName = this.parent.dialect.
|
|
1232
|
+
const sqlFieldName = this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultSet.groupSet}`);
|
|
1229
1233
|
if (fi instanceof field_instance_1.FieldInstanceField) {
|
|
1230
1234
|
if (fi.fieldUsage.type === 'result') {
|
|
1231
1235
|
if ((0, query_node_1.isScalarField)(fi.f)) {
|
|
@@ -1329,15 +1333,15 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1329
1333
|
const outputPipelinedSQL = [];
|
|
1330
1334
|
const dimensionIndexes = [];
|
|
1331
1335
|
for (const [name, fi] of this.rootResult.allFields) {
|
|
1332
|
-
const sqlName = this.parent.dialect.
|
|
1336
|
+
const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
|
|
1333
1337
|
if (fi instanceof field_instance_1.FieldInstanceField) {
|
|
1334
1338
|
if (fi.fieldUsage.type === 'result') {
|
|
1335
1339
|
if ((0, query_node_1.isScalarField)(fi.f)) {
|
|
1336
|
-
fieldsSQL.push(this.parent.dialect.
|
|
1340
|
+
fieldsSQL.push(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`) + ` as ${sqlName}`);
|
|
1337
1341
|
dimensionIndexes.push(fieldIndex++);
|
|
1338
1342
|
}
|
|
1339
1343
|
else if ((0, query_node_1.isBasicCalculation)(fi.f)) {
|
|
1340
|
-
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.
|
|
1344
|
+
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
|
|
1341
1345
|
fieldIndex++;
|
|
1342
1346
|
}
|
|
1343
1347
|
}
|
|
@@ -1348,7 +1352,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1348
1352
|
fieldIndex++;
|
|
1349
1353
|
}
|
|
1350
1354
|
else if (fi.firstSegment.type === 'project') {
|
|
1351
|
-
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.
|
|
1355
|
+
fieldsSQL.push(this.parent.dialect.sqlAnyValueLastTurtle(this.parent.dialect.sqlQuoteIdentifier(`${name}__${this.rootResult.groupSet}`), this.rootResult.groupSet, sqlName));
|
|
1352
1356
|
fieldIndex++;
|
|
1353
1357
|
}
|
|
1354
1358
|
}
|
|
@@ -1375,7 +1379,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1375
1379
|
buildDialectFieldList(resultStruct) {
|
|
1376
1380
|
const dialectFieldList = [];
|
|
1377
1381
|
for (const [name, field] of resultStruct.allFields) {
|
|
1378
|
-
const sqlName = this.parent.dialect.
|
|
1382
|
+
const sqlName = this.parent.dialect.sqlQuoteIdentifier(name);
|
|
1379
1383
|
//
|
|
1380
1384
|
if (resultStruct.firstSegment.type === 'reduce' &&
|
|
1381
1385
|
field instanceof field_instance_1.FieldInstanceResult) {
|
|
@@ -1390,7 +1394,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1390
1394
|
};
|
|
1391
1395
|
dialectFieldList.push({
|
|
1392
1396
|
typeDef: multiLineNest,
|
|
1393
|
-
sqlExpression: this.parent.dialect.
|
|
1397
|
+
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1394
1398
|
rawName: name,
|
|
1395
1399
|
sqlOutputName: sqlName,
|
|
1396
1400
|
});
|
|
@@ -1404,7 +1408,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1404
1408
|
};
|
|
1405
1409
|
dialectFieldList.push({
|
|
1406
1410
|
typeDef: oneLineNest,
|
|
1407
|
-
sqlExpression: this.parent.dialect.
|
|
1411
|
+
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1408
1412
|
rawName: name,
|
|
1409
1413
|
sqlOutputName: sqlName,
|
|
1410
1414
|
});
|
|
@@ -1415,7 +1419,7 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1415
1419
|
field.fieldUsage.type === 'result') {
|
|
1416
1420
|
pushDialectField(dialectFieldList, {
|
|
1417
1421
|
fieldDef: field.f.fieldDef,
|
|
1418
|
-
sqlExpression: this.parent.dialect.
|
|
1422
|
+
sqlExpression: this.parent.dialect.sqlQuoteIdentifier(`${name}__${resultStruct.groupSet}`),
|
|
1419
1423
|
rawName: name,
|
|
1420
1424
|
sqlOutputName: sqlName,
|
|
1421
1425
|
});
|
|
@@ -1449,10 +1453,10 @@ class QueryQuery extends query_node_1.QueryField {
|
|
|
1449
1453
|
else {
|
|
1450
1454
|
orderingField = resultStruct.getFieldByNumber(ordering.field);
|
|
1451
1455
|
}
|
|
1452
|
-
const structField = this.parent.dialect.
|
|
1456
|
+
const structField = this.parent.dialect.sqlQuoteIdentifier(orderingField.name);
|
|
1453
1457
|
if (resultStruct.firstSegment.type === 'reduce') {
|
|
1454
1458
|
compiledOrderBy.push({
|
|
1455
|
-
field: this.parent.dialect.
|
|
1459
|
+
field: this.parent.dialect.sqlQuoteIdentifier(`${orderingField.name}__${resultStruct.groupSet}`),
|
|
1456
1460
|
structField,
|
|
1457
1461
|
dir: ordering.dir || 'asc',
|
|
1458
1462
|
});
|
|
@@ -1636,12 +1640,12 @@ class QueryQueryIndexStage extends QueryQuery {
|
|
|
1636
1640
|
generateSQL(stageWriter) {
|
|
1637
1641
|
let measureSQL = 'COUNT(*)';
|
|
1638
1642
|
const dialect = this.parent.dialect;
|
|
1639
|
-
const fieldNameColumn = dialect.
|
|
1640
|
-
const fieldPathColumn = dialect.
|
|
1641
|
-
const fieldValueColumn = dialect.
|
|
1642
|
-
const fieldTypeColumn = dialect.
|
|
1643
|
-
const fieldRangeColumn = dialect.
|
|
1644
|
-
const weightColumn = dialect.
|
|
1643
|
+
const fieldNameColumn = dialect.sqlQuoteIdentifier('fieldName');
|
|
1644
|
+
const fieldPathColumn = dialect.sqlQuoteIdentifier('fieldPath');
|
|
1645
|
+
const fieldValueColumn = dialect.sqlQuoteIdentifier('fieldValue');
|
|
1646
|
+
const fieldTypeColumn = dialect.sqlQuoteIdentifier('fieldType');
|
|
1647
|
+
const fieldRangeColumn = dialect.sqlQuoteIdentifier('fieldRange');
|
|
1648
|
+
const weightColumn = dialect.sqlQuoteIdentifier('weight');
|
|
1645
1649
|
const measureName = this.firstSegment.weightMeasure;
|
|
1646
1650
|
if (measureName) {
|
|
1647
1651
|
measureSQL = this.rootResult.getField(measureName).generateExpression();
|
|
@@ -1819,7 +1823,7 @@ class QueryQueryRaw extends QueryQuery {
|
|
|
1819
1823
|
if (this.parent.structDef.type !== 'sql_select') {
|
|
1820
1824
|
throw new Error('Invalid struct for QueryQueryRaw, currently only supports SQL');
|
|
1821
1825
|
}
|
|
1822
|
-
return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {},
|
|
1826
|
+
return stageWriter.addStage((0, sql_compiled_1.getCompiledSQL)(this.parent.structDef, (_a = this.parent.prepareResultOptions) !== null && _a !== void 0 ? _a : {}, (query, opts) => {
|
|
1823
1827
|
// Compile query to isolated SQL (not into parent's stageWriter)
|
|
1824
1828
|
const ret = this.compileQueryToStages(query, opts !== null && opts !== void 0 ? opts : {}, undefined, false);
|
|
1825
1829
|
return ret.sql;
|
|
@@ -13,17 +13,15 @@ export type CompileQueryCallback = (query: Query, opts?: PrepareResultOptions) =
|
|
|
13
13
|
*
|
|
14
14
|
* @param src The SQLSourceDef to compile
|
|
15
15
|
* @param opts PrepareResultOptions with buildManifest and connectionDigests
|
|
16
|
-
* @param quoteTablePath Dialect function to safely quote a table path
|
|
17
16
|
* @param compileQuery Callback to compile a Query to SQL
|
|
18
17
|
*/
|
|
19
|
-
export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions,
|
|
18
|
+
export declare function getCompiledSQL(src: SQLSourceDef, opts: PrepareResultOptions, compileQuery: CompileQueryCallback): string;
|
|
20
19
|
/**
|
|
21
20
|
* Get the SQL for a PersistableSourceDef.
|
|
22
21
|
*
|
|
23
22
|
* @param source The persistable source to compile
|
|
24
|
-
* @param quoteTablePath Dialect function to quote table paths
|
|
25
23
|
* @param compileQuery Callback to compile a Query to SQL
|
|
26
24
|
* @param opts Optional - if provided with manifest, nested sources may be substituted.
|
|
27
25
|
* Omit for "full SQL" (e.g., when computing BuildID).
|
|
28
26
|
*/
|
|
29
|
-
export declare function getSourceSQL(source: PersistableSourceDef,
|
|
27
|
+
export declare function getSourceSQL(source: PersistableSourceDef, compileQuery: CompileQueryCallback, opts?: PrepareResultOptions): string;
|
|
@@ -16,10 +16,9 @@ const source_def_utils_1 = require("./source_def_utils");
|
|
|
16
16
|
*
|
|
17
17
|
* @param src The SQLSourceDef to compile
|
|
18
18
|
* @param opts PrepareResultOptions with buildManifest and connectionDigests
|
|
19
|
-
* @param quoteTablePath Dialect function to safely quote a table path
|
|
20
19
|
* @param compileQuery Callback to compile a Query to SQL
|
|
21
20
|
*/
|
|
22
|
-
function getCompiledSQL(src, opts,
|
|
21
|
+
function getCompiledSQL(src, opts, compileQuery) {
|
|
23
22
|
// If no segments, just return the pre-computed selectStr
|
|
24
23
|
if (!src.selectSegments || src.selectSegments.length === 0) {
|
|
25
24
|
return src.selectStr;
|
|
@@ -27,42 +26,43 @@ function getCompiledSQL(src, opts, quoteTablePath, compileQuery) {
|
|
|
27
26
|
// Expand each segment
|
|
28
27
|
const parts = [];
|
|
29
28
|
for (const segment of src.selectSegments) {
|
|
30
|
-
parts.push(expandSegment(segment, opts,
|
|
29
|
+
parts.push(expandSegment(segment, opts, compileQuery));
|
|
31
30
|
}
|
|
32
31
|
return parts.join('');
|
|
33
32
|
}
|
|
34
33
|
/**
|
|
35
34
|
* Expand a single SQLPhraseSegment to SQL.
|
|
36
35
|
*/
|
|
37
|
-
function expandSegment(segment, opts,
|
|
36
|
+
function expandSegment(segment, opts, compileQuery) {
|
|
38
37
|
// Plain SQL string
|
|
39
38
|
if ((0, malloy_types_1.isSegmentSQL)(segment)) {
|
|
40
39
|
return segment.sql;
|
|
41
40
|
}
|
|
42
41
|
// PersistableSourceDef (sql_select or query_source)
|
|
43
42
|
if ((0, malloy_types_1.isSegmentSource)(segment)) {
|
|
44
|
-
return expandPersistableSource(segment, opts,
|
|
43
|
+
return expandPersistableSource(segment, opts, compileQuery);
|
|
45
44
|
}
|
|
46
45
|
// Query segment
|
|
47
|
-
return expandQuery(segment, opts,
|
|
46
|
+
return expandQuery(segment, opts, compileQuery);
|
|
48
47
|
}
|
|
49
48
|
/**
|
|
50
49
|
* Expand a PersistableSourceDef, checking manifest for pre-built table.
|
|
51
50
|
* Always returns a subquery form: (SELECT * FROM table) or (inline SQL)
|
|
52
51
|
*/
|
|
53
|
-
function expandPersistableSource(source, opts,
|
|
52
|
+
function expandPersistableSource(source, opts, compileQuery) {
|
|
54
53
|
const { buildManifest, connectionDigests } = opts;
|
|
55
54
|
// Try manifest lookup if we have the required info (only for persistent sources)
|
|
56
55
|
if (buildManifest && connectionDigests && source.persistent) {
|
|
57
56
|
const connDigest = (0, malloy_types_1.safeRecordGet)(connectionDigests, source.connection);
|
|
58
57
|
if (connDigest) {
|
|
59
58
|
// Get the SQL for this source to compute BuildID (no opts = full SQL)
|
|
60
|
-
const sql = getSourceSQL(source,
|
|
59
|
+
const sql = getSourceSQL(source, compileQuery);
|
|
61
60
|
const buildId = (0, source_def_utils_1.mkBuildID)(connDigest, sql);
|
|
62
61
|
const entry = buildManifest.entries[buildId];
|
|
63
62
|
if (entry) {
|
|
64
|
-
// Found in manifest - substitute with subquery from persisted table
|
|
65
|
-
|
|
63
|
+
// Found in manifest - substitute with subquery from persisted table.
|
|
64
|
+
// entry.tableName is canonical SQL, supplied by the manifest builder.
|
|
65
|
+
return `(SELECT * FROM ${entry.tableName})`;
|
|
66
66
|
}
|
|
67
67
|
// Not in manifest
|
|
68
68
|
if (buildManifest.strict) {
|
|
@@ -74,13 +74,13 @@ function expandPersistableSource(source, opts, quoteTablePath, compileQuery) {
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
// No manifest or not found - expand inline as subquery
|
|
77
|
-
const sql = getSourceSQL(source,
|
|
77
|
+
const sql = getSourceSQL(source, compileQuery, opts);
|
|
78
78
|
return `(${sql})`;
|
|
79
79
|
}
|
|
80
80
|
/**
|
|
81
81
|
* Expand a Query segment.
|
|
82
82
|
*/
|
|
83
|
-
function expandQuery(query, opts,
|
|
83
|
+
function expandQuery(query, opts, compileQuery) {
|
|
84
84
|
// Set isPartialQuery so CTEs aren't used (they can't be nested in subqueries)
|
|
85
85
|
const sql = compileQuery(query, { ...opts, isPartialQuery: true });
|
|
86
86
|
return `(${sql})`;
|
|
@@ -89,15 +89,14 @@ function expandQuery(query, opts, _quoteTablePath, compileQuery) {
|
|
|
89
89
|
* Get the SQL for a PersistableSourceDef.
|
|
90
90
|
*
|
|
91
91
|
* @param source The persistable source to compile
|
|
92
|
-
* @param quoteTablePath Dialect function to quote table paths
|
|
93
92
|
* @param compileQuery Callback to compile a Query to SQL
|
|
94
93
|
* @param opts Optional - if provided with manifest, nested sources may be substituted.
|
|
95
94
|
* Omit for "full SQL" (e.g., when computing BuildID).
|
|
96
95
|
*/
|
|
97
|
-
function getSourceSQL(source,
|
|
96
|
+
function getSourceSQL(source, compileQuery, opts) {
|
|
98
97
|
if (source.type === 'sql_select') {
|
|
99
98
|
// Recursive call for nested sql_select
|
|
100
|
-
return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {},
|
|
99
|
+
return getCompiledSQL(source, opts !== null && opts !== void 0 ? opts : {}, compileQuery);
|
|
101
100
|
}
|
|
102
101
|
// query_source - compile the inner query
|
|
103
102
|
return compileQuery(source.query, opts);
|