@google-cloud/kcmd 0.1.0 → 0.2.1
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/README.md +18 -20
- package/build/ts/kcmd/cli.js +6164 -5631
- package/build/ts/kcmd/gcp/biglake.d.ts +2 -0
- package/build/ts/kcmd/gcp/biglake.js +13 -4
- package/build/ts/kcmd/gcp/spanner.d.ts +10 -0
- package/build/ts/kcmd/gcp/spanner.js +6 -3
- package/build/ts/kcmd/layouts/okf.js +49 -7
- package/build/ts/kcmd/layouts/semantic-model.d.ts +13 -0
- package/build/ts/kcmd/layouts/semantic-model.js +129 -50
- package/build/ts/kcmd/okf/concept.d.ts +1 -1
- package/build/ts/kcmd/okf/concept.js +6 -0
- package/build/ts/kcmd/okf/concept_to_entry.d.ts +2 -0
- package/build/ts/kcmd/okf/concept_to_entry.js +29 -2
- package/build/ts/kcmd/okf/init_okf_workspace.js +7 -9
- package/build/ts/kcmd/okf/okf_metadata.js +17 -0
- package/build/ts/kcmd/semantic/bigquery.js +139 -195
- package/build/ts/kcmd/semantic/binding.js +14 -19
- package/build/ts/kcmd/semantic/deploy_bigquery.js +6 -13
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.d.ts +10 -35
- package/build/ts/kcmd/semantic/deploy_knowledge_catalog.js +105 -162
- package/build/ts/kcmd/semantic/deploy_spanner.js +7 -14
- package/build/ts/kcmd/semantic/deployment_target.d.ts +1 -4
- package/build/ts/kcmd/semantic/deployment_target.js +6 -62
- package/build/ts/kcmd/semantic/graph_ddl.d.ts +10 -0
- package/build/ts/kcmd/semantic/graph_ddl.js +152 -59
- package/build/ts/kcmd/semantic/import_model.d.ts +26 -4
- package/build/ts/kcmd/semantic/import_model.js +531 -111
- package/build/ts/kcmd/semantic/ir.d.ts +2 -4
- package/build/ts/kcmd/semantic/kc_converter.d.ts +7 -1
- package/build/ts/kcmd/semantic/kc_converter.js +32 -27
- package/build/ts/kcmd/semantic/kc_custom_types.d.ts +5 -12
- package/build/ts/kcmd/semantic/kc_custom_types.js +43 -114
- package/build/ts/kcmd/semantic/kc_ids.d.ts +4 -5
- package/build/ts/kcmd/semantic/kc_ids.js +4 -7
- package/build/ts/kcmd/semantic/knowledge_catalog.d.ts +0 -3
- package/build/ts/kcmd/semantic/knowledge_catalog.js +3 -86
- package/build/ts/kcmd/semantic/loader.d.ts +7 -11
- package/build/ts/kcmd/semantic/loader.js +282 -171
- package/build/ts/kcmd/semantic/osi_converter.js +14 -65
- package/build/ts/kcmd/semantic/parameters.d.ts +1 -0
- package/build/ts/kcmd/semantic/parameters.js +22 -3
- package/build/ts/kcmd/semantic/pull_kc.d.ts +1 -5
- package/build/ts/kcmd/semantic/pull_kc.js +38 -112
- package/build/ts/kcmd/semantic/resolve_inheritance.d.ts +7 -2
- package/build/ts/kcmd/semantic/resolve_inheritance.js +37 -21
- package/build/ts/kcmd/semantic/resolve_profiles.d.ts +51 -10
- package/build/ts/kcmd/semantic/resolve_profiles.js +213 -32
- package/build/ts/kcmd/semantic/runtime/runtime.d.ts +4 -12
- package/build/ts/kcmd/semantic/runtime/runtime.js +4 -21
- package/build/ts/kcmd/semantic/runtime/store.d.ts +14 -19
- package/build/ts/kcmd/semantic/runtime/store.js +21 -37
- package/build/ts/kcmd/semantic/source_dialect.d.ts +9 -2
- package/build/ts/kcmd/semantic/source_dialect.js +83 -33
- package/build/ts/kcmd/semantic/source_entries.d.ts +1 -1
- package/build/ts/kcmd/semantic/source_entries.js +95 -36
- package/build/ts/kcmd/semantic/sql_expr_utils.d.ts +13 -7
- package/build/ts/kcmd/semantic/sql_expr_utils.js +240 -67
- package/build/ts/kcmd/semantic/sql_parser.d.ts +5 -2
- package/build/ts/kcmd/semantic/sql_parser.js +22 -0
- package/build/ts/kcmd/semantic/transpile.d.ts +12 -10
- package/build/ts/kcmd/semantic/transpile.js +79 -61
- package/build/ts/kcmd/semantic/validate.d.ts +37 -2
- package/build/ts/kcmd/semantic/validate.js +467 -126
- package/build/ts/kcmd/snapshot.d.ts +4 -0
- package/build/ts/kcmd/snapshot.js +7 -0
- package/build/ts/kcmd/sync.js +44 -7
- package/package.json +1 -1
|
@@ -23,7 +23,8 @@ export interface PolyglotEngine {
|
|
|
23
23
|
error?: string;
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
interface SqlToken {
|
|
26
|
+
export interface SqlToken {
|
|
27
|
+
token_type: string;
|
|
27
28
|
text: string;
|
|
28
29
|
span: {
|
|
29
30
|
start: number;
|
|
@@ -33,12 +34,14 @@ interface SqlToken {
|
|
|
33
34
|
trailing_comments: string[];
|
|
34
35
|
}
|
|
35
36
|
export declare function loadSqlEngine(): Promise<PolyglotEngine>;
|
|
37
|
+
export declare function loadedSqlEngine(): PolyglotEngine | undefined;
|
|
36
38
|
export interface SqlColumn {
|
|
37
39
|
qualifier?: string;
|
|
38
40
|
name: string;
|
|
39
41
|
}
|
|
40
42
|
export declare function sqlColumns(expression: string, dialect: string): SqlColumn[] | undefined;
|
|
43
|
+
export declare function soleColumn(expression: string, dialect: string): SqlColumn | undefined;
|
|
41
44
|
export declare function sqlExpressionTree(expression: string, dialect: string): any;
|
|
45
|
+
export declare function sqlTokens(expression: string, dialect: string): SqlToken[] | undefined;
|
|
42
46
|
export declare function holdsNoSql(expression: string, dialect: string): boolean;
|
|
43
47
|
export declare function withoutComments(expression: string, dialect: string): string;
|
|
44
|
-
export {};
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
// first, and so does a test.
|
|
9
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
10
|
exports.loadSqlEngine = loadSqlEngine;
|
|
11
|
+
exports.loadedSqlEngine = loadedSqlEngine;
|
|
11
12
|
exports.sqlColumns = sqlColumns;
|
|
13
|
+
exports.soleColumn = soleColumn;
|
|
12
14
|
exports.sqlExpressionTree = sqlExpressionTree;
|
|
15
|
+
exports.sqlTokens = sqlTokens;
|
|
13
16
|
exports.holdsNoSql = holdsNoSql;
|
|
14
17
|
exports.withoutComments = withoutComments;
|
|
15
18
|
const node_fs_1 = require("node:fs");
|
|
@@ -42,6 +45,10 @@ function loadSqlEngine() {
|
|
|
42
45
|
}
|
|
43
46
|
return enginePromise;
|
|
44
47
|
}
|
|
48
|
+
// The engine loadSqlEngine has loaded, or undefined before it has.
|
|
49
|
+
function loadedSqlEngine() {
|
|
50
|
+
return loadedEngine;
|
|
51
|
+
}
|
|
45
52
|
// The engine's dialect for each kcmd dialect. The engine has no Spanner
|
|
46
53
|
// dialect, so Spanner's GoogleSQL is read as BigQuery's.
|
|
47
54
|
const PARSER_DIALECTS = {
|
|
@@ -73,6 +80,21 @@ function sqlColumns(expression, dialect) {
|
|
|
73
80
|
collectColumns(tree, columns, new Set(), PARSER_DIALECTS[dialect.toUpperCase()] ?? 'generic');
|
|
74
81
|
return columns;
|
|
75
82
|
}
|
|
83
|
+
// The column `expression` is, read in `dialect`, when it is one column and
|
|
84
|
+
// nothing else, perhaps in parentheses: `orders.amount`, `` (`order`) `` or
|
|
85
|
+
// `amount`, but not a struct path such as `orders.address.city` or any other
|
|
86
|
+
// expression. Undefined otherwise, or when the engine cannot read it. Throws
|
|
87
|
+
// when the engine is not loaded.
|
|
88
|
+
function soleColumn(expression, dialect) {
|
|
89
|
+
let tree = sqlExpressionTree(expression, dialect);
|
|
90
|
+
const kind = (node) => node && Object.keys(node).length === 1 ? Object.keys(node)[0] : undefined;
|
|
91
|
+
while (kind(tree) === 'paren')
|
|
92
|
+
tree = tree.paren.this;
|
|
93
|
+
if (kind(tree) !== 'column')
|
|
94
|
+
return undefined;
|
|
95
|
+
const columns = sqlColumns(expression, dialect);
|
|
96
|
+
return columns?.length === 1 ? columns[0] : undefined;
|
|
97
|
+
}
|
|
76
98
|
// The engine's tree of `expression` read as one expression in `dialect`, or
|
|
77
99
|
// undefined when it cannot be read so or is empty. A text that starts with a
|
|
78
100
|
// keyword, such as `Order.amount`, is read again inside parentheses on lines
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SemanticModel } from './ir';
|
|
1
|
+
import { Field, ProfileSpec, SemanticModel } from './ir';
|
|
2
2
|
export interface TranspileRequest {
|
|
3
3
|
id: string;
|
|
4
4
|
dialect: string;
|
|
@@ -13,11 +13,13 @@ export type SqlTranspiler = (requests: TranspileRequest[], target: string) => Pr
|
|
|
13
13
|
export interface TranspileOptions {
|
|
14
14
|
target?: string;
|
|
15
15
|
transpiler?: SqlTranspiler;
|
|
16
|
+
translatesProfile?: (profile: ProfileSpec, model: SemanticModel) => boolean;
|
|
16
17
|
}
|
|
17
18
|
export interface TranspileResult {
|
|
18
19
|
model: SemanticModel;
|
|
19
20
|
warnings: string[];
|
|
20
21
|
}
|
|
22
|
+
type TranspileNode = Pick<Field, 'expression' | 'dialects' | 'stringForm' | 'importedExpression' | 'importedDialect'>;
|
|
21
23
|
/**
|
|
22
24
|
* Returns true if `expression`, read as generic SQL (`ANSI_SQL`), quotes a
|
|
23
25
|
* column, table qualifier, or struct field with double quotes (`"..."`).
|
|
@@ -43,9 +45,10 @@ export declare function hasDoubleQuotedColumn(expression: string): boolean;
|
|
|
43
45
|
*/
|
|
44
46
|
export declare function doubleQuotedNames(expression: string): string[];
|
|
45
47
|
/**
|
|
46
|
-
* Adds a translated `opts.target` dialect entry to every field
|
|
47
|
-
* `model` whose only texts are in vendor dialects or
|
|
48
|
-
* double-quotes a column name, returning a cloned model
|
|
48
|
+
* Adds a translated `opts.target` dialect entry to every field, metric and
|
|
49
|
+
* profile field binding in `model` whose only texts are in vendor dialects or
|
|
50
|
+
* whose `ANSI_SQL` text double-quotes a column name, returning a cloned model
|
|
51
|
+
* plus diagnostics.
|
|
49
52
|
*
|
|
50
53
|
* The input model is never mutated. When no node needs transpilation, the clone
|
|
51
54
|
* is returned unchanged and the transpiler is not invoked. The
|
|
@@ -56,13 +59,11 @@ export declare function doubleQuotedNames(expression: string): string[];
|
|
|
56
59
|
*/
|
|
57
60
|
export declare function transpileModel(model: SemanticModel, opts?: TranspileOptions): Promise<TranspileResult>;
|
|
58
61
|
/**
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
* `transpileModel` leaves a text in any other dialect as written, with a
|
|
63
|
-
* warning.
|
|
62
|
+
* Whether `transpileModel`, with the default translator, gives `node` a
|
|
63
|
+
* BIGQUERY text in a model whose entities are `entityNames`. False until
|
|
64
|
+
* `loadSqlEngine()` has loaded the engine.
|
|
64
65
|
*/
|
|
65
|
-
export declare function
|
|
66
|
+
export declare function translatesToBigQuery(node: TranspileNode, entityNames: string[]): boolean;
|
|
66
67
|
/**
|
|
67
68
|
* The default {@link SqlTranspiler}: transpiles via the embedded
|
|
68
69
|
* @polyglot-sql/sdk Rust/WASM engine. There is no external runtime dependency
|
|
@@ -72,3 +73,4 @@ export declare function transpilesFrom(dialect: string): boolean;
|
|
|
72
73
|
* that request, so the caller degrades to the imported form + warning.
|
|
73
74
|
*/
|
|
74
75
|
export declare const polyglotTranspiler: SqlTranspiler;
|
|
76
|
+
export {};
|
|
@@ -34,7 +34,7 @@ exports.polyglotTranspiler = void 0;
|
|
|
34
34
|
exports.hasDoubleQuotedColumn = hasDoubleQuotedColumn;
|
|
35
35
|
exports.doubleQuotedNames = doubleQuotedNames;
|
|
36
36
|
exports.transpileModel = transpileModel;
|
|
37
|
-
exports.
|
|
37
|
+
exports.translatesToBigQuery = translatesToBigQuery;
|
|
38
38
|
const sql_expr_utils_1 = require("./sql_expr_utils");
|
|
39
39
|
const sql_identifiers_1 = require("./sql_identifiers");
|
|
40
40
|
const sql_parser_1 = require("./sql_parser");
|
|
@@ -164,9 +164,10 @@ function sourceToTranspile(node, target) {
|
|
|
164
164
|
return undefined;
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
|
-
* Adds a translated `opts.target` dialect entry to every field
|
|
168
|
-
* `model` whose only texts are in vendor dialects or
|
|
169
|
-
* double-quotes a column name, returning a cloned model
|
|
167
|
+
* Adds a translated `opts.target` dialect entry to every field, metric and
|
|
168
|
+
* profile field binding in `model` whose only texts are in vendor dialects or
|
|
169
|
+
* whose `ANSI_SQL` text double-quotes a column name, returning a cloned model
|
|
170
|
+
* plus diagnostics.
|
|
170
171
|
*
|
|
171
172
|
* The input model is never mutated. When no node needs transpilation, the clone
|
|
172
173
|
* is returned unchanged and the transpiler is not invoked. The
|
|
@@ -214,6 +215,15 @@ async function transpileModel(model, opts = {}) {
|
|
|
214
215
|
}
|
|
215
216
|
for (const m of clone.metrics)
|
|
216
217
|
add(m, `metric '${m.name}'`);
|
|
218
|
+
for (const p of clone.profiles ?? []) {
|
|
219
|
+
if (opts.translatesProfile && !opts.translatesProfile(p, clone))
|
|
220
|
+
continue;
|
|
221
|
+
for (const pe of p.entities) {
|
|
222
|
+
for (const f of pe.fields ?? []) {
|
|
223
|
+
add(f, `profile '${p.name}': field '${pe.name}.${f.name}'`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
217
227
|
if (!pending.length)
|
|
218
228
|
return { model: clone, warnings };
|
|
219
229
|
const responses = await transpiler(pending.map(p => p.request), target);
|
|
@@ -236,27 +246,17 @@ async function transpileModel(model, opts = {}) {
|
|
|
236
246
|
// the exact case the emitter needs. Comparing the output against itself
|
|
237
247
|
// (rather than the vendor input) avoids false positives from dialect-
|
|
238
248
|
// specific identifier quoting.
|
|
239
|
-
const
|
|
240
|
-
if (
|
|
249
|
+
const reached = qualifiersReached(res.sql, target, entityNames);
|
|
250
|
+
if (!reached) {
|
|
241
251
|
warnings.push(`${ctx}: transpiled '${request.dialect}' -> '${target}' could not ` +
|
|
242
252
|
`be parsed; qualifier check was skipped`);
|
|
243
253
|
}
|
|
244
|
-
else {
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
const lower = c.qualifier.toLowerCase();
|
|
251
|
-
return entityNames.filter(n => n.toLowerCase() === lower);
|
|
252
|
-
})),
|
|
253
|
-
];
|
|
254
|
-
if (!sameSet(emitterVisible, anyCase)) {
|
|
255
|
-
warnings.push(`${ctx}: transpiled '${request.dialect}' -> '${target}' but it re-cased an entity qualifier the emitter matches ` +
|
|
256
|
-
`case-sensitively (${fmtSet(emitterVisible)} vs ${fmtSet(anyCase)}); ` +
|
|
257
|
-
`left untranslated`);
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
254
|
+
else if (!sameSet(reached.exact, reached.anyCase)) {
|
|
255
|
+
warnings.push(`${ctx}: transpiled '${request.dialect}' -> '${target}' but it ` +
|
|
256
|
+
`re-cased an entity qualifier the emitter matches case-sensitively ` +
|
|
257
|
+
`(${fmtSet(reached.exact)} vs ${fmtSet(reached.anyCase)}); left ` +
|
|
258
|
+
`untranslated`);
|
|
259
|
+
continue;
|
|
260
260
|
}
|
|
261
261
|
p.accept(res.sql);
|
|
262
262
|
warnings.push(`${ctx}: transpiled '${request.dialect}' -> '${target}'`);
|
|
@@ -268,6 +268,35 @@ async function transpileModel(model, opts = {}) {
|
|
|
268
268
|
}
|
|
269
269
|
return { model: clone, warnings };
|
|
270
270
|
}
|
|
271
|
+
// The entities among `entityNames` that `sql`, in `target`, qualifies a
|
|
272
|
+
// column with in their exact case and in any case, or undefined when the
|
|
273
|
+
// parser cannot read `sql`.
|
|
274
|
+
function qualifiersReached(sql, target, entityNames) {
|
|
275
|
+
const cols = (0, sql_expr_utils_1.columnReferences)(sql, target);
|
|
276
|
+
if (cols === undefined)
|
|
277
|
+
return undefined;
|
|
278
|
+
const anyCase = new Set(cols.flatMap(c => {
|
|
279
|
+
const lower = c.qualifier?.toLowerCase();
|
|
280
|
+
return entityNames.filter(n => n.toLowerCase() === lower);
|
|
281
|
+
}));
|
|
282
|
+
return { exact: (0, sql_expr_utils_1.entitiesReached)(cols, entityNames), anyCase: [...anyCase] };
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Whether `transpileModel`, with the default translator, gives `node` a
|
|
286
|
+
* BIGQUERY text in a model whose entities are `entityNames`. False until
|
|
287
|
+
* `loadSqlEngine()` has loaded the engine.
|
|
288
|
+
*/
|
|
289
|
+
function translatesToBigQuery(node, entityNames) {
|
|
290
|
+
const source = sourceToTranspile(node, DEFAULT_TARGET);
|
|
291
|
+
const engine = (0, sql_parser_1.loadedSqlEngine)();
|
|
292
|
+
if (!source || !engine)
|
|
293
|
+
return false;
|
|
294
|
+
const { sql } = transpileWith(engine, { id: '', ...source }, polyglotDialect(DEFAULT_TARGET));
|
|
295
|
+
if (sql === undefined)
|
|
296
|
+
return false;
|
|
297
|
+
const reached = qualifiersReached(sql, DEFAULT_TARGET, entityNames);
|
|
298
|
+
return !reached || sameSet(reached.exact, reached.anyCase);
|
|
299
|
+
}
|
|
271
300
|
function sameSet(a, b) {
|
|
272
301
|
if (a.length !== b.length)
|
|
273
302
|
return false;
|
|
@@ -298,16 +327,6 @@ function polyglotDialect(token) {
|
|
|
298
327
|
return undefined;
|
|
299
328
|
return POLYGLOT_DIALECTS[token.toUpperCase()];
|
|
300
329
|
}
|
|
301
|
-
/**
|
|
302
|
-
* Returns true if the default translator reads SQL written in `dialect`
|
|
303
|
-
* (treating an empty string as `ANSI_SQL`).
|
|
304
|
-
*
|
|
305
|
-
* `transpileModel` leaves a text in any other dialect as written, with a
|
|
306
|
-
* warning.
|
|
307
|
-
*/
|
|
308
|
-
function transpilesFrom(dialect) {
|
|
309
|
-
return polyglotDialect(dialect || 'ANSI_SQL') !== undefined;
|
|
310
|
-
}
|
|
311
330
|
/**
|
|
312
331
|
* The default {@link SqlTranspiler}: transpiles via the embedded
|
|
313
332
|
* @polyglot-sql/sdk Rust/WASM engine. There is no external runtime dependency
|
|
@@ -335,37 +354,36 @@ const polyglotTranspiler = async (requests, target) => {
|
|
|
335
354
|
const reason = `SQL transpiler engine unavailable: ${errMessage(err)}`;
|
|
336
355
|
return requests.map(r => ({ id: r.id, error: reason }));
|
|
337
356
|
}
|
|
338
|
-
return requests.map(r =>
|
|
339
|
-
const read = polyglotDialect(r.dialect);
|
|
340
|
-
if (read === undefined)
|
|
341
|
-
return { id: r.id, error: `unsupported source dialect '${r.dialect}'` };
|
|
342
|
-
try {
|
|
343
|
-
const res = engine.transpile(r.expression, read, write);
|
|
344
|
-
if (!res.success || !res.sql || res.sql.length === 0)
|
|
345
|
-
return {
|
|
346
|
-
id: r.id,
|
|
347
|
-
error: res.error ?? 'transpilation produced no output'
|
|
348
|
-
};
|
|
349
|
-
// A field/metric is a single scalar expression, so the engine should
|
|
350
|
-
// return exactly one statement. More than one means the input was not a
|
|
351
|
-
// lone expression; taking [0] would silently drop the rest, so reject it.
|
|
352
|
-
if (res.sql.length > 1)
|
|
353
|
-
return {
|
|
354
|
-
id: r.id,
|
|
355
|
-
error: `expected a single expression but got ${res.sql.length} statements`
|
|
356
|
-
};
|
|
357
|
-
if (write !== 'bigquery')
|
|
358
|
-
return { id: r.id, sql: res.sql[0] };
|
|
359
|
-
const repaired = repairForBigQuery(engine, res.sql[0], read);
|
|
360
|
-
return 'error' in repaired ? { id: r.id, error: repaired.error } :
|
|
361
|
-
{ id: r.id, sql: repaired.sql };
|
|
362
|
-
}
|
|
363
|
-
catch (err) {
|
|
364
|
-
return { id: r.id, error: errMessage(err) };
|
|
365
|
-
}
|
|
366
|
-
});
|
|
357
|
+
return requests.map(r => transpileWith(engine, r, write));
|
|
367
358
|
};
|
|
368
359
|
exports.polyglotTranspiler = polyglotTranspiler;
|
|
360
|
+
// The engine's answer to `r`, with `write` as the engine's target dialect.
|
|
361
|
+
function transpileWith(engine, r, write) {
|
|
362
|
+
const read = polyglotDialect(r.dialect);
|
|
363
|
+
if (read === undefined)
|
|
364
|
+
return { id: r.id, error: `unsupported source dialect '${r.dialect}'` };
|
|
365
|
+
try {
|
|
366
|
+
const res = engine.transpile(r.expression, read, write);
|
|
367
|
+
if (!res.success || !res.sql || res.sql.length === 0)
|
|
368
|
+
return { id: r.id, error: res.error ?? 'transpilation produced no output' };
|
|
369
|
+
// A field/metric is a single scalar expression, so the engine should
|
|
370
|
+
// return exactly one statement. More than one means the input was not a
|
|
371
|
+
// lone expression; taking [0] would silently drop the rest, so reject it.
|
|
372
|
+
if (res.sql.length > 1)
|
|
373
|
+
return {
|
|
374
|
+
id: r.id,
|
|
375
|
+
error: `expected a single expression but got ${res.sql.length} statements`
|
|
376
|
+
};
|
|
377
|
+
if (write !== 'bigquery')
|
|
378
|
+
return { id: r.id, sql: res.sql[0] };
|
|
379
|
+
const repaired = repairForBigQuery(engine, res.sql[0], read);
|
|
380
|
+
return 'error' in repaired ? { id: r.id, error: repaired.error } :
|
|
381
|
+
{ id: r.id, sql: repaired.sql };
|
|
382
|
+
}
|
|
383
|
+
catch (err) {
|
|
384
|
+
return { id: r.id, error: errMessage(err) };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
369
387
|
function errMessage(err) {
|
|
370
388
|
return err instanceof Error ? err.message : String(err);
|
|
371
389
|
}
|
|
@@ -3,12 +3,13 @@ import { SpannerClient } from '../gcp/spanner';
|
|
|
3
3
|
import { ResolvedDeployment } from './deployment_target';
|
|
4
4
|
import { SemanticModel } from './ir';
|
|
5
5
|
import { LoadedModel } from './loader';
|
|
6
|
+
import { AvailabilityReport, DeclaredProfiles, DialectLoad, LocalCheckError, ProfileFiles } from './resolve_profiles';
|
|
6
7
|
export declare function validatePushRequirements(models: LoadedModel[], opts?: {
|
|
7
8
|
fieldsPruned?: boolean;
|
|
8
9
|
warnings?: string[];
|
|
9
10
|
v2Aspects?: boolean;
|
|
10
11
|
}): string[];
|
|
11
|
-
export declare function validateDeployments(model: SemanticModel, document: string): string[];
|
|
12
|
+
export declare function validateDeployments(model: SemanticModel, document: string, load?: DialectLoad): string[];
|
|
12
13
|
export declare function validateInlineDialects(model: SemanticModel, document: string): string[];
|
|
13
14
|
export declare function validateInheritedColumns(model: SemanticModel, document: string): string[];
|
|
14
15
|
export declare function inlineBindingWarnings(model: SemanticModel, document: string, namedProfiles: boolean): string[];
|
|
@@ -25,11 +26,45 @@ export declare function validateActions(model: SemanticModel, document: string,
|
|
|
25
26
|
export declare function validateInheritance(models: LoadedModel[]): string[];
|
|
26
27
|
export declare function validateExpressionReferences(models: LoadedModel[], fieldsPruned?: boolean, warnings?: string[], flaggedMetrics?: Set<string>): string[];
|
|
27
28
|
export declare function deploymentTableErrors(document: string, model: SemanticModel, deployment: ResolvedDeployment): string[];
|
|
29
|
+
export declare function spannerMetricsErrors(document: string, model: SemanticModel, deployment: ResolvedDeployment): string[];
|
|
30
|
+
export declare function kcV2Aspects(): boolean;
|
|
31
|
+
export interface LocalCheckScope {
|
|
32
|
+
docs: Array<{
|
|
33
|
+
name: string;
|
|
34
|
+
text: string;
|
|
35
|
+
}>;
|
|
36
|
+
loaded: {
|
|
37
|
+
models: LoadedModel[];
|
|
38
|
+
warnings: string[];
|
|
39
|
+
};
|
|
40
|
+
models: LoadedModel[];
|
|
41
|
+
profileDocs: ProfileFiles;
|
|
42
|
+
declaredBy: DeclaredProfiles;
|
|
43
|
+
load: DialectLoad;
|
|
44
|
+
}
|
|
45
|
+
export interface DeploymentPlan {
|
|
46
|
+
deployment: ResolvedDeployment;
|
|
47
|
+
at: string;
|
|
48
|
+
model: LoadedModel;
|
|
49
|
+
report: AvailabilityReport;
|
|
50
|
+
generated?: {
|
|
51
|
+
ddl: string[];
|
|
52
|
+
warnings: string[];
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export declare function withProfileFiles(base: LoadedModel, profileDocs: ProfileFiles): LoadedModel | {
|
|
56
|
+
errors: LocalCheckError[];
|
|
57
|
+
};
|
|
58
|
+
export declare function localChecks(scope: LocalCheckScope, warn: (w: string) => void, selected?: string[]): Promise<{
|
|
59
|
+
plans: DeploymentPlan[];
|
|
60
|
+
} | {
|
|
61
|
+
errors: LocalCheckError[];
|
|
62
|
+
}>;
|
|
63
|
+
export declare function pruningLines({ deployment, model, report: r }: DeploymentPlan): string[];
|
|
28
64
|
export declare function validateBigQueryDataSources(models: LoadedModel[], bq: BigQueryClient, defaultProject: string, graph?: {
|
|
29
65
|
at: string;
|
|
30
66
|
dataset: string;
|
|
31
67
|
location: string;
|
|
32
68
|
}): Promise<string[]>;
|
|
33
|
-
export declare function storeMessage(message: string | undefined, status: number): string;
|
|
34
69
|
export declare function validateSpannerActionStatements(models: LoadedModel[], spanner: SpannerClient): Promise<string[]>;
|
|
35
70
|
export declare function validateBigQueryActionStatements(models: LoadedModel[], bq: BigQueryClient, defaultProject: string): Promise<string[]>;
|