@sqlanvil/cli 1.8.3 → 1.9.0
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/bundle.js +359 -1
- package/package.json +1 -1
package/bundle.js
CHANGED
|
@@ -42996,6 +42996,24 @@ from (${query}) as insertions`;
|
|
|
42996
42996
|
`) AS\n${exp.query}`;
|
|
42997
42997
|
return [sqlanvil.ExecutionTask.create({ type: "statement", statement })];
|
|
42998
42998
|
}
|
|
42999
|
+
validationStubSql(table) {
|
|
43000
|
+
const target = this.resolveTarget(table.target);
|
|
43001
|
+
if (table.enumType === sqlanvil.TableType.VIEW) {
|
|
43002
|
+
return `create view ${target} as ${table.query}`;
|
|
43003
|
+
}
|
|
43004
|
+
return `create table ${target} as select * from (${table.query}) limit 0`;
|
|
43005
|
+
}
|
|
43006
|
+
createSchemaSql(schema) {
|
|
43007
|
+
return `create schema if not exists ${this.qualifiedSchema(schema)}`;
|
|
43008
|
+
}
|
|
43009
|
+
dropSchemaCascadeSql(schema) {
|
|
43010
|
+
return `drop schema if exists ${this.qualifiedSchema(schema)} cascade`;
|
|
43011
|
+
}
|
|
43012
|
+
qualifiedSchema(schema) {
|
|
43013
|
+
return this.project.defaultDatabase
|
|
43014
|
+
? `\`${this.project.defaultDatabase}.${schema}\``
|
|
43015
|
+
: `\`${schema}\``;
|
|
43016
|
+
}
|
|
42999
43017
|
buildIncrementalSchemaChangeTasks(tasks, table) {
|
|
43000
43018
|
const uniqueId = this.uniqueIdGenerator();
|
|
43001
43019
|
const emptyTempTableTarget = Object.assign(Object.assign({}, table.target), { name: `${table.target.name}_sa_temp_${uniqueId}_empty` });
|
|
@@ -43206,6 +43224,19 @@ class MysqlExecutionSql {
|
|
|
43206
43224
|
createExportTasks(exp) {
|
|
43207
43225
|
throw new Error("type: \"export\" is not supported on MySQL/MariaDB yet.");
|
|
43208
43226
|
}
|
|
43227
|
+
validationStubSql(table) {
|
|
43228
|
+
const target = this.resolveTarget(table.target);
|
|
43229
|
+
if (table.enumType === sqlanvil.TableType.VIEW) {
|
|
43230
|
+
return `create or replace view ${target} as ${table.query}`;
|
|
43231
|
+
}
|
|
43232
|
+
return `create table ${target} as select * from (${table.query}) as _sa_stub limit 0`;
|
|
43233
|
+
}
|
|
43234
|
+
createSchemaSql(schema) {
|
|
43235
|
+
return `create database if not exists \`${schema}\``;
|
|
43236
|
+
}
|
|
43237
|
+
dropSchemaCascadeSql(schema) {
|
|
43238
|
+
return `drop database if exists \`${schema}\``;
|
|
43239
|
+
}
|
|
43209
43240
|
publishTasks(table, runConfig, tableMetadata) {
|
|
43210
43241
|
const tasks = new Tasks();
|
|
43211
43242
|
const target = this.resolveTarget(table.target);
|
|
@@ -43370,6 +43401,19 @@ class PostgresExecutionSql {
|
|
|
43370
43401
|
const kind = type === sqlanvil.TableMetadata.Type.VIEW ? "view" : "table";
|
|
43371
43402
|
return `drop ${kind} if exists ${this.resolveTarget(target)} cascade`;
|
|
43372
43403
|
}
|
|
43404
|
+
validationStubSql(table) {
|
|
43405
|
+
const target = this.resolveTarget(table.target);
|
|
43406
|
+
if (table.enumType === sqlanvil.TableType.VIEW) {
|
|
43407
|
+
return `create view ${target} as ${table.query}`;
|
|
43408
|
+
}
|
|
43409
|
+
return `create table ${target} as ${table.query} with no data`;
|
|
43410
|
+
}
|
|
43411
|
+
createSchemaSql(schema) {
|
|
43412
|
+
return `create schema if not exists "${schema}"`;
|
|
43413
|
+
}
|
|
43414
|
+
dropSchemaCascadeSql(schema) {
|
|
43415
|
+
return `drop schema if exists "${schema}" cascade`;
|
|
43416
|
+
}
|
|
43373
43417
|
createExportTasks(exp) {
|
|
43374
43418
|
return [sqlanvil.ExecutionTask.create({ type: "export", statement: exp.query })];
|
|
43375
43419
|
}
|
|
@@ -43682,6 +43726,15 @@ class ExecutionSql {
|
|
|
43682
43726
|
dropIfExists(target, type) {
|
|
43683
43727
|
return this.delegate.dropIfExists(target, type);
|
|
43684
43728
|
}
|
|
43729
|
+
validationStubSql(table) {
|
|
43730
|
+
return this.delegate.validationStubSql(table);
|
|
43731
|
+
}
|
|
43732
|
+
createSchemaSql(schema) {
|
|
43733
|
+
return this.delegate.createSchemaSql(schema);
|
|
43734
|
+
}
|
|
43735
|
+
dropSchemaCascadeSql(schema) {
|
|
43736
|
+
return this.delegate.dropSchemaCascadeSql(schema);
|
|
43737
|
+
}
|
|
43685
43738
|
}
|
|
43686
43739
|
function collectEvaluationQueries(queryOrAction, concatenate, queryModifier = (q) => q) {
|
|
43687
43740
|
const validationQueries = new Array();
|
|
@@ -43739,7 +43792,7 @@ function collectEvaluationQueries(queryOrAction, concatenate, queryModifier = (q
|
|
|
43739
43792
|
.filter(validationQuery => !!validationQuery.query);
|
|
43740
43793
|
}
|
|
43741
43794
|
|
|
43742
|
-
const version = "1.
|
|
43795
|
+
const version = "1.9.0";
|
|
43743
43796
|
const dataformVersion = "3.0.60";
|
|
43744
43797
|
|
|
43745
43798
|
async function build(compiledGraph, runConfig, dbadapter) {
|
|
@@ -45151,6 +45204,183 @@ function resolveCredentials(envCredentials, cliCredentials, defaultFilename) {
|
|
|
45151
45204
|
return envCredentials || defaultFilename;
|
|
45152
45205
|
}
|
|
45153
45206
|
|
|
45207
|
+
function targetKey(target) {
|
|
45208
|
+
return [target.database, target.schema, target.name].filter(Boolean).join(".");
|
|
45209
|
+
}
|
|
45210
|
+
function topoOrder(nodes) {
|
|
45211
|
+
const byKey = new Map(nodes.map(n => [n.key, n]));
|
|
45212
|
+
const indegree = new Map();
|
|
45213
|
+
const dependents = new Map();
|
|
45214
|
+
for (const n of nodes) {
|
|
45215
|
+
const inGraphDeps = Array.from(new Set(n.dependencyKeys.filter(d => byKey.has(d) && d !== n.key)));
|
|
45216
|
+
indegree.set(n.key, inGraphDeps.length);
|
|
45217
|
+
for (const dep of inGraphDeps) {
|
|
45218
|
+
dependents.set(dep, (dependents.get(dep) || []).concat(n.key));
|
|
45219
|
+
}
|
|
45220
|
+
}
|
|
45221
|
+
const ready = nodes
|
|
45222
|
+
.filter(n => (indegree.get(n.key) || 0) === 0)
|
|
45223
|
+
.map(n => n.key)
|
|
45224
|
+
.sort();
|
|
45225
|
+
const ordered = [];
|
|
45226
|
+
const emitted = new Set();
|
|
45227
|
+
while (ready.length) {
|
|
45228
|
+
const key = ready.shift();
|
|
45229
|
+
if (emitted.has(key)) {
|
|
45230
|
+
continue;
|
|
45231
|
+
}
|
|
45232
|
+
emitted.add(key);
|
|
45233
|
+
ordered.push(byKey.get(key));
|
|
45234
|
+
for (const dep of (dependents.get(key) || []).slice().sort()) {
|
|
45235
|
+
indegree.set(dep, (indegree.get(dep) || 0) - 1);
|
|
45236
|
+
if ((indegree.get(dep) || 0) <= 0 && !emitted.has(dep)) {
|
|
45237
|
+
ready.push(dep);
|
|
45238
|
+
ready.sort();
|
|
45239
|
+
}
|
|
45240
|
+
}
|
|
45241
|
+
}
|
|
45242
|
+
if (ordered.length < nodes.length) {
|
|
45243
|
+
for (const n of nodes.slice().sort((a, b) => a.key.localeCompare(b.key))) {
|
|
45244
|
+
if (!emitted.has(n.key)) {
|
|
45245
|
+
emitted.add(n.key);
|
|
45246
|
+
ordered.push(n);
|
|
45247
|
+
}
|
|
45248
|
+
}
|
|
45249
|
+
}
|
|
45250
|
+
return ordered;
|
|
45251
|
+
}
|
|
45252
|
+
function dependencyBlocked(dependencyKeys, statusByKey) {
|
|
45253
|
+
return dependencyKeys.some(dep => {
|
|
45254
|
+
const status = statusByKey.get(dep);
|
|
45255
|
+
return status !== undefined && status !== "PASS";
|
|
45256
|
+
});
|
|
45257
|
+
}
|
|
45258
|
+
const VALIDATE_SHADOW_PREFIX = "sqlanvil_validate_";
|
|
45259
|
+
function validateShadowSuffix(nowMs) {
|
|
45260
|
+
return `${VALIDATE_SHADOW_PREFIX}${nowMs}`;
|
|
45261
|
+
}
|
|
45262
|
+
function parseShadowTimestamp(schemaName) {
|
|
45263
|
+
const match = schemaName.match(/sqlanvil_validate_(\d+)/);
|
|
45264
|
+
return match ? Number(match[1]) : null;
|
|
45265
|
+
}
|
|
45266
|
+
function shadowSchemasToSweep(schemaNames, nowMs, maxAgeMs) {
|
|
45267
|
+
return schemaNames.filter(name => {
|
|
45268
|
+
const ts = parseShadowTimestamp(name);
|
|
45269
|
+
return ts !== null && nowMs - ts > maxAgeMs;
|
|
45270
|
+
});
|
|
45271
|
+
}
|
|
45272
|
+
|
|
45273
|
+
const SHADOW_MAX_AGE_MS = 60 * 60 * 1000;
|
|
45274
|
+
async function sweepOrphanShadows(deps, nowMs, maxAgeMs = SHADOW_MAX_AGE_MS) {
|
|
45275
|
+
try {
|
|
45276
|
+
const schemas = await deps.listSchemas();
|
|
45277
|
+
for (const schema of shadowSchemasToSweep(schemas, nowMs, maxAgeMs)) {
|
|
45278
|
+
try {
|
|
45279
|
+
await deps.execute(deps.dropSchemaCascadeSql(schema));
|
|
45280
|
+
}
|
|
45281
|
+
catch (e) {
|
|
45282
|
+
}
|
|
45283
|
+
}
|
|
45284
|
+
}
|
|
45285
|
+
catch (e) {
|
|
45286
|
+
}
|
|
45287
|
+
}
|
|
45288
|
+
function tableType(enumType) {
|
|
45289
|
+
switch (enumType) {
|
|
45290
|
+
case sqlanvil.TableType.VIEW:
|
|
45291
|
+
return "view";
|
|
45292
|
+
case sqlanvil.TableType.INCREMENTAL:
|
|
45293
|
+
return "incremental";
|
|
45294
|
+
default:
|
|
45295
|
+
return "table";
|
|
45296
|
+
}
|
|
45297
|
+
}
|
|
45298
|
+
function depKeys(deps) {
|
|
45299
|
+
return (deps || []).map(targetKey);
|
|
45300
|
+
}
|
|
45301
|
+
async function validate(compiledGraph, deps, options = {}) {
|
|
45302
|
+
const nodes = [];
|
|
45303
|
+
for (const table of compiledGraph.tables || []) {
|
|
45304
|
+
nodes.push({
|
|
45305
|
+
key: targetKey(table.target),
|
|
45306
|
+
dependencyKeys: depKeys(table.dependencyTargets),
|
|
45307
|
+
kind: "table",
|
|
45308
|
+
type: tableType(table.enumType),
|
|
45309
|
+
target: table.target,
|
|
45310
|
+
table,
|
|
45311
|
+
action: table
|
|
45312
|
+
});
|
|
45313
|
+
}
|
|
45314
|
+
for (const assertion of compiledGraph.assertions || []) {
|
|
45315
|
+
nodes.push({
|
|
45316
|
+
key: targetKey(assertion.target),
|
|
45317
|
+
dependencyKeys: depKeys(assertion.dependencyTargets),
|
|
45318
|
+
kind: "assertion",
|
|
45319
|
+
type: "assertion",
|
|
45320
|
+
target: assertion.target,
|
|
45321
|
+
action: assertion
|
|
45322
|
+
});
|
|
45323
|
+
}
|
|
45324
|
+
for (const operation of compiledGraph.operations || []) {
|
|
45325
|
+
if (!operation.target) {
|
|
45326
|
+
continue;
|
|
45327
|
+
}
|
|
45328
|
+
nodes.push({
|
|
45329
|
+
key: targetKey(operation.target),
|
|
45330
|
+
dependencyKeys: depKeys(operation.dependencyTargets),
|
|
45331
|
+
kind: "operation",
|
|
45332
|
+
type: "operation",
|
|
45333
|
+
target: operation.target,
|
|
45334
|
+
action: undefined
|
|
45335
|
+
});
|
|
45336
|
+
}
|
|
45337
|
+
const ordered = topoOrder(nodes);
|
|
45338
|
+
const shadowSchemas = Array.from(new Set(nodes.filter(n => n.kind === "table").map(n => n.target.schema)));
|
|
45339
|
+
const statusByKey = new Map();
|
|
45340
|
+
const results = [];
|
|
45341
|
+
try {
|
|
45342
|
+
for (const schema of shadowSchemas) {
|
|
45343
|
+
await deps.execute(deps.createSchemaSql(schema));
|
|
45344
|
+
}
|
|
45345
|
+
for (const node of ordered) {
|
|
45346
|
+
if (node.kind === "operation") {
|
|
45347
|
+
statusByKey.set(node.key, "SKIPPED");
|
|
45348
|
+
results.push({ target: node.target, type: node.type, status: "SKIPPED", errors: [] });
|
|
45349
|
+
continue;
|
|
45350
|
+
}
|
|
45351
|
+
if (dependencyBlocked(node.dependencyKeys, statusByKey)) {
|
|
45352
|
+
statusByKey.set(node.key, "BLOCKED");
|
|
45353
|
+
results.push({ target: node.target, type: node.type, status: "BLOCKED", errors: [] });
|
|
45354
|
+
continue;
|
|
45355
|
+
}
|
|
45356
|
+
const evaluations = await deps.evaluate(node.action);
|
|
45357
|
+
const failed = evaluations.some(e => e.status === sqlanvil.QueryEvaluation.QueryEvaluationStatus.FAILURE);
|
|
45358
|
+
const status = failed ? "FAILURE" : "PASS";
|
|
45359
|
+
statusByKey.set(node.key, status);
|
|
45360
|
+
results.push({ target: node.target, type: node.type, status, errors: evaluations });
|
|
45361
|
+
if (status === "PASS" && node.kind === "table") {
|
|
45362
|
+
try {
|
|
45363
|
+
await deps.execute(deps.validationStubSql(node.table));
|
|
45364
|
+
}
|
|
45365
|
+
catch (e) {
|
|
45366
|
+
}
|
|
45367
|
+
}
|
|
45368
|
+
}
|
|
45369
|
+
}
|
|
45370
|
+
finally {
|
|
45371
|
+
if (!options.keepShadow) {
|
|
45372
|
+
for (const schema of shadowSchemas.slice().reverse()) {
|
|
45373
|
+
try {
|
|
45374
|
+
await deps.execute(deps.dropSchemaCascadeSql(schema));
|
|
45375
|
+
}
|
|
45376
|
+
catch (e) {
|
|
45377
|
+
}
|
|
45378
|
+
}
|
|
45379
|
+
}
|
|
45380
|
+
}
|
|
45381
|
+
return results;
|
|
45382
|
+
}
|
|
45383
|
+
|
|
45154
45384
|
function parsePostgresEvalError(_query, error) {
|
|
45155
45385
|
return sqlanvil.QueryEvaluationError.create({
|
|
45156
45386
|
message: (error === null || error === void 0 ? void 0 : error.message) ? String(error.message) : String(error)
|
|
@@ -47734,6 +47964,11 @@ const timeoutOption = option("timeout", {
|
|
|
47734
47964
|
default: null,
|
|
47735
47965
|
coerce: (rawTimeoutString) => rawTimeoutString ? parseDuration__default["default"](rawTimeoutString) : null
|
|
47736
47966
|
});
|
|
47967
|
+
const keepShadowOption = option("keep-shadow", {
|
|
47968
|
+
describe: "If set, `validate` leaves its temporary shadow schema(s) in place instead of dropping them " +
|
|
47969
|
+
"(debugging aid).",
|
|
47970
|
+
type: "boolean"
|
|
47971
|
+
});
|
|
47737
47972
|
const jobPrefixOption = option("job-prefix", {
|
|
47738
47973
|
describe: "Adds an additional prefix in the form of `sqlanvil-${jobPrefix}-`.",
|
|
47739
47974
|
type: "string",
|
|
@@ -47795,6 +48030,108 @@ function credentialsPathWithEnvironment(projectDir, argv) {
|
|
|
47795
48030
|
const chosen = resolveCredentials(envCredentials, argv[credentialsOption.name], CREDENTIALS_FILENAME);
|
|
47796
48031
|
return getCredentialsPath(projectDir, chosen);
|
|
47797
48032
|
}
|
|
48033
|
+
function printValidationResults(results, json) {
|
|
48034
|
+
const failures = results.filter(r => r.status === "FAILURE");
|
|
48035
|
+
const blocked = results.filter(r => r.status === "BLOCKED");
|
|
48036
|
+
const passed = results.filter(r => r.status === "PASS");
|
|
48037
|
+
const skipped = results.filter(r => r.status === "SKIPPED");
|
|
48038
|
+
if (json) {
|
|
48039
|
+
print(prettyJsonStringify(results));
|
|
48040
|
+
}
|
|
48041
|
+
else {
|
|
48042
|
+
for (const result of results) {
|
|
48043
|
+
const label = targetAsReadableString(result.target);
|
|
48044
|
+
if (result.status === "PASS") {
|
|
48045
|
+
printSuccess(` PASS ${label}`);
|
|
48046
|
+
}
|
|
48047
|
+
else if (result.status === "SKIPPED") {
|
|
48048
|
+
print(` SKIP ${label} (${result.type} — not validated)`);
|
|
48049
|
+
}
|
|
48050
|
+
else if (result.status === "BLOCKED") {
|
|
48051
|
+
printError(` BLOCK ${label} — blocked by an upstream failure`);
|
|
48052
|
+
}
|
|
48053
|
+
else {
|
|
48054
|
+
printError(` FAIL ${label}`);
|
|
48055
|
+
result.errors
|
|
48056
|
+
.filter(e => e.status === sqlanvil.QueryEvaluation.QueryEvaluationStatus.FAILURE)
|
|
48057
|
+
.forEach(e => {
|
|
48058
|
+
var _a;
|
|
48059
|
+
const loc = ((_a = e.error) === null || _a === void 0 ? void 0 : _a.errorLocation) ? ` (line ${e.error.errorLocation.line}, col ${e.error.errorLocation.column})`
|
|
48060
|
+
: "";
|
|
48061
|
+
printError(` ${(e.error && e.error.message) || "validation failed"}${loc}`);
|
|
48062
|
+
});
|
|
48063
|
+
}
|
|
48064
|
+
}
|
|
48065
|
+
print(`\n${passed.length} passed, ${failures.length} failed, ${blocked.length} blocked` +
|
|
48066
|
+
(skipped.length ? `, ${skipped.length} skipped` : ""));
|
|
48067
|
+
}
|
|
48068
|
+
return failures.length > 0 || blocked.length > 0 ? 1 : 0;
|
|
48069
|
+
}
|
|
48070
|
+
async function runValidate(argv) {
|
|
48071
|
+
const projectDir = argv[projectDirOption.name];
|
|
48072
|
+
if (!argv[jsonOutputOption.name]) {
|
|
48073
|
+
print("Compiling...\n");
|
|
48074
|
+
}
|
|
48075
|
+
const baseOverride = projectConfigOverrideWithEnvironment(projectDir, argv);
|
|
48076
|
+
const shadowSuffix = validateShadowSuffix(Date.now());
|
|
48077
|
+
const compiledGraph = await compile({
|
|
48078
|
+
projectDir,
|
|
48079
|
+
projectConfigOverride: Object.assign(Object.assign({}, baseOverride), { schemaSuffix: [baseOverride.schemaSuffix, shadowSuffix].filter(Boolean).join("_") }),
|
|
48080
|
+
timeoutMillis: argv[timeoutOption.name] || undefined
|
|
48081
|
+
});
|
|
48082
|
+
if (compiledGraphHasErrors(compiledGraph)) {
|
|
48083
|
+
printCompiledGraphErrors(compiledGraph.graphErrors, argv[quietCompileOption.name]);
|
|
48084
|
+
return 1;
|
|
48085
|
+
}
|
|
48086
|
+
if (!argv[jsonOutputOption.name]) {
|
|
48087
|
+
printSuccess("Compiled successfully.\n");
|
|
48088
|
+
}
|
|
48089
|
+
const warehouse = (compiledGraph.projectConfig.warehouse || "bigquery").toLowerCase();
|
|
48090
|
+
const readCredentials = read(credentialsPathWithEnvironment(projectDir, argv), warehouse);
|
|
48091
|
+
let dbadapter;
|
|
48092
|
+
if (warehouse === "supabase") {
|
|
48093
|
+
dbadapter = await SupabaseDbAdapter.create(readCredentials);
|
|
48094
|
+
}
|
|
48095
|
+
else if (warehouse === "mysql") {
|
|
48096
|
+
dbadapter = await MySqlDbAdapter.create(readCredentials);
|
|
48097
|
+
}
|
|
48098
|
+
else if (warehouse === "bigquery") {
|
|
48099
|
+
dbadapter = new BigQueryDbAdapter(readCredentials);
|
|
48100
|
+
}
|
|
48101
|
+
else {
|
|
48102
|
+
dbadapter = await PostgresDbAdapter.create(readCredentials);
|
|
48103
|
+
}
|
|
48104
|
+
const prunedGraph = prune(compiledGraph, {
|
|
48105
|
+
actions: argv[actionsOption.name],
|
|
48106
|
+
includeDependencies: argv[includeDepsOption.name],
|
|
48107
|
+
includeDependents: argv[includeDependentsOption.name],
|
|
48108
|
+
tags: argv[tagsOption.name]
|
|
48109
|
+
});
|
|
48110
|
+
const executionSql = new ExecutionSql(compiledGraph.projectConfig, dataformVersion);
|
|
48111
|
+
const shadowSchemas = Array.from(new Set((prunedGraph.tables || []).map(table => table.target.schema)));
|
|
48112
|
+
process.on("SIGINT", () => {
|
|
48113
|
+
Promise.all(shadowSchemas.map(schema => dbadapter.execute(executionSql.dropSchemaCascadeSql(schema)).catch(() => undefined))).then(() => process.exit(1));
|
|
48114
|
+
});
|
|
48115
|
+
const deps = {
|
|
48116
|
+
evaluate: action => dbadapter.evaluate(action.enumType !== undefined
|
|
48117
|
+
? sqlanvil.Table.create(action)
|
|
48118
|
+
: sqlanvil.Assertion.create(action)),
|
|
48119
|
+
execute: sql => dbadapter.execute(sql).then(() => undefined),
|
|
48120
|
+
validationStubSql: table => executionSql.validationStubSql(table),
|
|
48121
|
+
createSchemaSql: schema => executionSql.createSchemaSql(schema),
|
|
48122
|
+
dropSchemaCascadeSql: schema => executionSql.dropSchemaCascadeSql(schema),
|
|
48123
|
+
listSchemas: async () => {
|
|
48124
|
+
const result = await dbadapter.execute("select schema_name as name from information_schema.schemata");
|
|
48125
|
+
return ((result && result.rows) || []).map((row) => row.name);
|
|
48126
|
+
}
|
|
48127
|
+
};
|
|
48128
|
+
await sweepOrphanShadows(deps, Date.now());
|
|
48129
|
+
if (!argv[jsonOutputOption.name]) {
|
|
48130
|
+
print("Validating...\n");
|
|
48131
|
+
}
|
|
48132
|
+
const results = await validate(prunedGraph, deps, { keepShadow: argv[keepShadowOption.name] });
|
|
48133
|
+
return printValidationResults(results, argv[jsonOutputOption.name]);
|
|
48134
|
+
}
|
|
47798
48135
|
function runCli() {
|
|
47799
48136
|
const builtYargs = createYargsCli({
|
|
47800
48137
|
commands: [
|
|
@@ -48075,6 +48412,24 @@ function runCli() {
|
|
|
48075
48412
|
return testResults.every(testResult => testResult.successful) ? 0 : 1;
|
|
48076
48413
|
}
|
|
48077
48414
|
},
|
|
48415
|
+
{
|
|
48416
|
+
format: `validate [${projectDirMustExistOption.name}]`,
|
|
48417
|
+
description: "Validate the project's SQL against the warehouse planner (EXPLAIN/dry-run) without " +
|
|
48418
|
+
"executing. Postgres/Supabase/MySQL only.",
|
|
48419
|
+
positionalOptions: [projectDirMustExistOption],
|
|
48420
|
+
options: [
|
|
48421
|
+
actionsOption,
|
|
48422
|
+
tagsOption,
|
|
48423
|
+
includeDepsOption,
|
|
48424
|
+
includeDependentsOption,
|
|
48425
|
+
credentialsOption,
|
|
48426
|
+
jsonOutputOption,
|
|
48427
|
+
timeoutOption,
|
|
48428
|
+
keepShadowOption,
|
|
48429
|
+
...ProjectConfigOptions.allYargsOptions
|
|
48430
|
+
],
|
|
48431
|
+
processFn: async (argv) => runValidate(argv)
|
|
48432
|
+
},
|
|
48078
48433
|
{
|
|
48079
48434
|
format: `run [${projectDirMustExistOption.name}]`,
|
|
48080
48435
|
description: "Run the sqlanvil project.",
|
|
@@ -48127,6 +48482,9 @@ function runCli() {
|
|
|
48127
48482
|
printSuccess("Compiled successfully.\n");
|
|
48128
48483
|
}
|
|
48129
48484
|
const warehouse = compiledGraph.projectConfig.warehouse || "bigquery";
|
|
48485
|
+
if (argv[dryRunOptionName] && warehouse.toLowerCase() !== "bigquery") {
|
|
48486
|
+
return runValidate(argv);
|
|
48487
|
+
}
|
|
48130
48488
|
const readCredentials = read(credentialsPathWithEnvironment(argv[projectDirOption.name], argv), warehouse);
|
|
48131
48489
|
let dbadapter;
|
|
48132
48490
|
if (warehouse.toLowerCase() === "supabase") {
|