@malloy-publisher/server 0.0.209 → 0.0.211
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/app/api-doc.yaml +20 -3
- package/dist/app/assets/{EnvironmentPage-BRMCY9d8.js → EnvironmentPage-CuO6sty5.js} +1 -1
- package/dist/app/assets/HomePage-u3vxROIF.js +1 -0
- package/dist/app/assets/{MainPage-sZdUjUcu.js → MainPage-Bt2sYLbr.js} +2 -2
- package/dist/app/assets/{MaterializationsPage-C_jQQUCJ.js → MaterializationsPage-B1rj61zs.js} +1 -1
- package/dist/app/assets/{ModelPage-Bh62OIEq.js → ModelPage-BpvONvSR.js} +1 -1
- package/dist/app/assets/{PackagePage-DJW4xjLp.js → PackagePage-DHNcwboW.js} +1 -1
- package/dist/app/assets/{RouteError-Vi5yGs_F.js → RouteError-D1vhLJvr.js} +1 -1
- package/dist/app/assets/{WorkbookPage-BvJMi21d.js → WorkbookPage-CZmud-yI.js} +1 -1
- package/dist/app/assets/{core-BbW0t3RQ.es-L-mZcOk3.js → core-XtBSnW2U.es-DE88sVsZ.js} +1 -1
- package/dist/app/assets/{index-CCcHdeew.js → index-BQdOB6m9.js} +1 -1
- package/dist/app/assets/{index-DuqTjxM_.js → index-BlnQtDZj.js} +137 -137
- package/dist/app/assets/{index-CNFX-CGL.js → index-CYf2akGH.js} +1 -1
- package/dist/app/assets/{index.umd-GgEb4WfT.js → index.umd-BdQ0R4hx.js} +1 -1
- package/dist/app/index.html +1 -1
- package/dist/package_load_worker.mjs +3 -0
- package/dist/server.mjs +314 -306
- package/package.json +1 -1
- package/src/materialization_metrics.spec.ts +84 -0
- package/src/materialization_metrics.ts +78 -119
- package/src/service/build_plan.spec.ts +84 -0
- package/src/service/build_plan.ts +53 -2
- package/src/service/materialization_service.spec.ts +126 -8
- package/src/service/materialization_service.ts +96 -83
- package/src/service/materialization_test_fixtures.ts +2 -0
- package/src/service/model.ts +25 -25
- package/src/service/package.ts +19 -7
- package/src/service/package_worker_path.spec.ts +65 -55
- package/src/service/persist_annotation_validation.spec.ts +77 -0
- package/src/service/persist_annotation_validation.ts +54 -0
- package/src/service/quoting.spec.ts +50 -0
- package/src/service/quoting.ts +37 -16
- package/src/utils.ts +5 -0
- package/dist/app/assets/HomePage-DQzgkiMI.js +0 -1
package/dist/server.mjs
CHANGED
|
@@ -237699,97 +237699,52 @@ import { pathToFileURL as pathToFileURL2 } from "url";
|
|
|
237699
237699
|
|
|
237700
237700
|
// src/materialization_metrics.ts
|
|
237701
237701
|
var import_api4 = __toESM(require_src(), 1);
|
|
237702
|
-
var
|
|
237703
|
-
|
|
237704
|
-
|
|
237705
|
-
|
|
237706
|
-
|
|
237707
|
-
|
|
237708
|
-
|
|
237709
|
-
|
|
237710
|
-
|
|
237711
|
-
|
|
237712
|
-
|
|
237713
|
-
|
|
237714
|
-
|
|
237715
|
-
|
|
237716
|
-
|
|
237717
|
-
|
|
237718
|
-
|
|
237719
|
-
|
|
237720
|
-
|
|
237721
|
-
|
|
237722
|
-
runDuration = meter2.createHistogram("publisher_materialization_run_duration_ms", {
|
|
237723
|
-
description: "Wall-clock duration of a materialization build. Label: mode ('auto'|'orchestrated').",
|
|
237724
|
-
unit: "ms"
|
|
237725
|
-
});
|
|
237726
|
-
}
|
|
237727
|
-
return { counter: runCounter, duration: runDuration };
|
|
237728
|
-
}
|
|
237702
|
+
var resetHooks = [];
|
|
237703
|
+
function lazyCounter(name, description) {
|
|
237704
|
+
let instrument2 = null;
|
|
237705
|
+
resetHooks.push(() => instrument2 = null);
|
|
237706
|
+
return () => instrument2 ??= import_api4.metrics.getMeter("publisher").createCounter(name, { description });
|
|
237707
|
+
}
|
|
237708
|
+
function lazyHistogram(name, description, unit) {
|
|
237709
|
+
let instrument2 = null;
|
|
237710
|
+
resetHooks.push(() => instrument2 = null);
|
|
237711
|
+
return () => instrument2 ??= import_api4.metrics.getMeter("publisher").createHistogram(name, { description, unit });
|
|
237712
|
+
}
|
|
237713
|
+
var runCounter = lazyCounter("publisher_materialization_runs_total", "Materialization builds completed. Labels: mode ('auto'|'orchestrated'), outcome ('success'|'failed'|'cancelled').");
|
|
237714
|
+
var runDuration = lazyHistogram("publisher_materialization_run_duration_ms", "Wall-clock duration of a materialization build. Label: mode ('auto'|'orchestrated').", "ms");
|
|
237715
|
+
var sourcesCounter = lazyCounter("publisher_materialization_sources_total", "Persist sources processed by a materialization run. Label: outcome ('built'|'reused').");
|
|
237716
|
+
var buildPlanComputeDuration = lazyHistogram("publisher_materialization_build_plan_compute_duration_ms", "Wall-clock duration of compiling a package's build plan (Package.buildPlan).", "ms");
|
|
237717
|
+
var autoLoadCounter = lazyCounter("publisher_materialization_auto_load_total", "Auto-run manifest auto-load attempts. Label: outcome ('success'|'failure').");
|
|
237718
|
+
var connectionDigestSkipCounter = lazyCounter("publisher_materialization_connection_digest_skipped_total", "Connection digests skipped during build-plan compile because the connection did not resolve.");
|
|
237719
|
+
var manifestBindCounter = lazyCounter("publisher_materialization_manifest_bind_total", "Manifest bind attempts. Label: outcome ('success'|'failure'|'timeout').");
|
|
237720
|
+
var sourceBuildDuration = lazyHistogram("publisher_materialization_source_build_duration_ms", "Wall-clock duration of building a single persist source.", "ms");
|
|
237721
|
+
var dropTablesCounter = lazyCounter("publisher_materialization_drop_tables_total", "Physical tables dropped on delete. Label: outcome ('success'|'failure').");
|
|
237729
237722
|
function recordMaterializationRun(mode, outcome, durationMs) {
|
|
237730
|
-
|
|
237731
|
-
|
|
237732
|
-
duration.record(durationMs, { mode });
|
|
237723
|
+
runCounter().add(1, { mode, outcome });
|
|
237724
|
+
runDuration().record(durationMs, { mode });
|
|
237733
237725
|
}
|
|
237734
237726
|
function recordSourcesOutcome(outcome, count) {
|
|
237735
237727
|
if (count <= 0)
|
|
237736
237728
|
return;
|
|
237737
|
-
|
|
237738
|
-
sourcesCounter = import_api4.metrics.getMeter("publisher").createCounter("publisher_materialization_sources_total", {
|
|
237739
|
-
description: "Persist sources processed by a materialization run. Label: outcome ('built'|'reused')."
|
|
237740
|
-
});
|
|
237741
|
-
}
|
|
237742
|
-
sourcesCounter.add(count, { outcome });
|
|
237729
|
+
sourcesCounter().add(count, { outcome });
|
|
237743
237730
|
}
|
|
237744
237731
|
function recordBuildPlanComputeDuration(durationMs) {
|
|
237745
|
-
|
|
237746
|
-
buildPlanComputeDuration = import_api4.metrics.getMeter("publisher").createHistogram("publisher_materialization_build_plan_compute_duration_ms", {
|
|
237747
|
-
description: "Wall-clock duration of compiling a package's build plan (Package.buildPlan).",
|
|
237748
|
-
unit: "ms"
|
|
237749
|
-
});
|
|
237750
|
-
}
|
|
237751
|
-
buildPlanComputeDuration.record(durationMs);
|
|
237732
|
+
buildPlanComputeDuration().record(durationMs);
|
|
237752
237733
|
}
|
|
237753
237734
|
function recordAutoLoadOutcome(outcome) {
|
|
237754
|
-
|
|
237755
|
-
autoLoadCounter = import_api4.metrics.getMeter("publisher").createCounter("publisher_materialization_auto_load_total", {
|
|
237756
|
-
description: "Auto-run manifest auto-load attempts. Label: outcome ('success'|'failure')."
|
|
237757
|
-
});
|
|
237758
|
-
}
|
|
237759
|
-
autoLoadCounter.add(1, { outcome });
|
|
237735
|
+
autoLoadCounter().add(1, { outcome });
|
|
237760
237736
|
}
|
|
237761
237737
|
function recordConnectionDigestSkipped() {
|
|
237762
|
-
|
|
237763
|
-
connectionDigestSkipCounter = import_api4.metrics.getMeter("publisher").createCounter("publisher_materialization_connection_digest_skipped_total", {
|
|
237764
|
-
description: "Connection digests skipped during build-plan compile because the connection did not resolve."
|
|
237765
|
-
});
|
|
237766
|
-
}
|
|
237767
|
-
connectionDigestSkipCounter.add(1);
|
|
237738
|
+
connectionDigestSkipCounter().add(1);
|
|
237768
237739
|
}
|
|
237769
237740
|
function recordManifestBind(outcome) {
|
|
237770
|
-
|
|
237771
|
-
manifestBindCounter = import_api4.metrics.getMeter("publisher").createCounter("publisher_materialization_manifest_bind_total", {
|
|
237772
|
-
description: "Manifest bind attempts. Label: outcome ('success'|'failure'|'timeout')."
|
|
237773
|
-
});
|
|
237774
|
-
}
|
|
237775
|
-
manifestBindCounter.add(1, { outcome });
|
|
237741
|
+
manifestBindCounter().add(1, { outcome });
|
|
237776
237742
|
}
|
|
237777
237743
|
function recordSourceBuildDuration(durationMs) {
|
|
237778
|
-
|
|
237779
|
-
sourceBuildDuration = import_api4.metrics.getMeter("publisher").createHistogram("publisher_materialization_source_build_duration_ms", {
|
|
237780
|
-
description: "Wall-clock duration of building a single persist source.",
|
|
237781
|
-
unit: "ms"
|
|
237782
|
-
});
|
|
237783
|
-
}
|
|
237784
|
-
sourceBuildDuration.record(durationMs);
|
|
237744
|
+
sourceBuildDuration().record(durationMs);
|
|
237785
237745
|
}
|
|
237786
237746
|
function recordDropTables(outcome) {
|
|
237787
|
-
|
|
237788
|
-
dropTablesCounter = import_api4.metrics.getMeter("publisher").createCounter("publisher_materialization_drop_tables_total", {
|
|
237789
|
-
description: "Physical tables dropped on delete. Label: outcome ('success'|'failure')."
|
|
237790
|
-
});
|
|
237791
|
-
}
|
|
237792
|
-
dropTablesCounter.add(1, { outcome });
|
|
237747
|
+
dropTablesCounter().add(1, { outcome });
|
|
237793
237748
|
}
|
|
237794
237749
|
|
|
237795
237750
|
// src/utils.ts
|
|
@@ -237808,6 +237763,9 @@ var URL_READER = {
|
|
|
237808
237763
|
function ignoreDotfiles(file) {
|
|
237809
237764
|
return path5.basename(file).startsWith(".");
|
|
237810
237765
|
}
|
|
237766
|
+
function errMessage(err) {
|
|
237767
|
+
return err instanceof Error ? err.message : String(err);
|
|
237768
|
+
}
|
|
237811
237769
|
|
|
237812
237770
|
// src/service/manifest_loader.ts
|
|
237813
237771
|
init_logger();
|
|
@@ -237889,10 +237847,10 @@ import {
|
|
|
237889
237847
|
} from "@malloydata/malloy";
|
|
237890
237848
|
|
|
237891
237849
|
// src/service/build_plan.ts
|
|
237850
|
+
init_constants();
|
|
237892
237851
|
init_logger();
|
|
237893
237852
|
|
|
237894
237853
|
// src/service/model.ts
|
|
237895
|
-
init_package_load_pool();
|
|
237896
237854
|
var import_api5 = __toESM(require_src(), 1);
|
|
237897
237855
|
import {
|
|
237898
237856
|
Annotations as Annotations2,
|
|
@@ -237963,6 +237921,150 @@ class HackyDataStylesAccumulator {
|
|
|
237963
237921
|
// src/service/model.ts
|
|
237964
237922
|
init_errors();
|
|
237965
237923
|
init_logger();
|
|
237924
|
+
init_package_load_pool();
|
|
237925
|
+
|
|
237926
|
+
// src/service/annotations.ts
|
|
237927
|
+
import { Annotations } from "@malloydata/malloy";
|
|
237928
|
+
function isReservedRoute(route) {
|
|
237929
|
+
return route === "" || !/[\p{L}\p{N}]/u.test(route);
|
|
237930
|
+
}
|
|
237931
|
+
function modelAnnotations(modelDef) {
|
|
237932
|
+
const registry = modelDef.modelAnnotations ?? {};
|
|
237933
|
+
const visited = new Set;
|
|
237934
|
+
const order = [];
|
|
237935
|
+
const visit = (id) => {
|
|
237936
|
+
if (visited.has(id))
|
|
237937
|
+
return;
|
|
237938
|
+
visited.add(id);
|
|
237939
|
+
const entry = registry[id];
|
|
237940
|
+
if (!entry)
|
|
237941
|
+
return;
|
|
237942
|
+
for (const dep of entry.inheritsFrom)
|
|
237943
|
+
visit(dep);
|
|
237944
|
+
order.push(id);
|
|
237945
|
+
};
|
|
237946
|
+
visit(modelDef.modelID);
|
|
237947
|
+
let folded;
|
|
237948
|
+
for (const id of order) {
|
|
237949
|
+
const own = registry[id].ownNotes;
|
|
237950
|
+
if (!own.notes?.length && !own.blockNotes?.length)
|
|
237951
|
+
continue;
|
|
237952
|
+
folded = {
|
|
237953
|
+
notes: own.notes,
|
|
237954
|
+
blockNotes: own.blockNotes,
|
|
237955
|
+
inherits: folded
|
|
237956
|
+
};
|
|
237957
|
+
}
|
|
237958
|
+
return folded ?? {};
|
|
237959
|
+
}
|
|
237960
|
+
function annotationTexts(annote) {
|
|
237961
|
+
const texts = new Annotations(annote).texts();
|
|
237962
|
+
return texts.length > 0 ? texts : undefined;
|
|
237963
|
+
}
|
|
237964
|
+
|
|
237965
|
+
// src/service/authorize.ts
|
|
237966
|
+
init_errors();
|
|
237967
|
+
var SOURCE_PREFIX = "#(authorize)";
|
|
237968
|
+
var FILE_PREFIX = "##(authorize)";
|
|
237969
|
+
function buildAuthorizeProbe(exprs) {
|
|
237970
|
+
const selects = exprs.map((expr, i) => `__auth_${i} is (${expr})`).join(`
|
|
237971
|
+
`);
|
|
237972
|
+
return `run: duckdb.sql("SELECT 1 AS __authorize_probe_row") -> {
|
|
237973
|
+
select:
|
|
237974
|
+
${selects}
|
|
237975
|
+
limit: 1
|
|
237976
|
+
}`;
|
|
237977
|
+
}
|
|
237978
|
+
function isProbeTrue(cell) {
|
|
237979
|
+
return cell === true || cell === 1 || cell === "true";
|
|
237980
|
+
}
|
|
237981
|
+
async function evaluateAuthorize(executor, exprs, givens) {
|
|
237982
|
+
for (const expr of exprs) {
|
|
237983
|
+
try {
|
|
237984
|
+
const result = await executor.loadQuery(buildAuthorizeProbe([expr])).run({ rowLimit: 1, givens });
|
|
237985
|
+
const row = result?.data?.value?.[0];
|
|
237986
|
+
if (row && isProbeTrue(row.__auth_0)) {
|
|
237987
|
+
return true;
|
|
237988
|
+
}
|
|
237989
|
+
} catch {
|
|
237990
|
+
continue;
|
|
237991
|
+
}
|
|
237992
|
+
}
|
|
237993
|
+
return false;
|
|
237994
|
+
}
|
|
237995
|
+
async function validateAuthorizeProbes(compiler, sources) {
|
|
237996
|
+
for (const source of sources) {
|
|
237997
|
+
const exprs = source.authorize;
|
|
237998
|
+
if (!exprs || exprs.length === 0)
|
|
237999
|
+
continue;
|
|
238000
|
+
try {
|
|
238001
|
+
await compiler.loadQuery(buildAuthorizeProbe(exprs)).getPreparedQuery();
|
|
238002
|
+
} catch (err) {
|
|
238003
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
238004
|
+
throw new ModelCompilationError({
|
|
238005
|
+
message: `Invalid #(authorize) annotation on source "${source.name ?? "(unnamed)"}" [${exprs.join(" | ")}]: ${detail}`
|
|
238006
|
+
});
|
|
238007
|
+
}
|
|
238008
|
+
}
|
|
238009
|
+
}
|
|
238010
|
+
function parseAuthorizeAnnotation(annotation) {
|
|
238011
|
+
const trimmed2 = annotation.trim();
|
|
238012
|
+
let body;
|
|
238013
|
+
if (trimmed2.startsWith(FILE_PREFIX)) {
|
|
238014
|
+
body = trimmed2.slice(FILE_PREFIX.length).trim();
|
|
238015
|
+
} else if (trimmed2.startsWith(SOURCE_PREFIX)) {
|
|
238016
|
+
body = trimmed2.slice(SOURCE_PREFIX.length).trim();
|
|
238017
|
+
} else {
|
|
238018
|
+
return null;
|
|
238019
|
+
}
|
|
238020
|
+
return unwrapQuotedExpression(body);
|
|
238021
|
+
}
|
|
238022
|
+
function collectAuthorizeExprs(annotations) {
|
|
238023
|
+
const exprs = [];
|
|
238024
|
+
for (const annotation of annotations) {
|
|
238025
|
+
const expr = parseAuthorizeAnnotation(annotation);
|
|
238026
|
+
if (expr !== null) {
|
|
238027
|
+
exprs.push(expr);
|
|
238028
|
+
}
|
|
238029
|
+
}
|
|
238030
|
+
return exprs;
|
|
238031
|
+
}
|
|
238032
|
+
function unwrapQuotedExpression(body) {
|
|
238033
|
+
if (body.length < 2 || body[0] !== '"') {
|
|
238034
|
+
throw new Error(`authorize annotation expression must be a double-quoted string, got: ${body || "(empty)"}`);
|
|
238035
|
+
}
|
|
238036
|
+
let expr = "";
|
|
238037
|
+
let i = 1;
|
|
238038
|
+
let closed = false;
|
|
238039
|
+
for (;i < body.length; i++) {
|
|
238040
|
+
const ch = body[i];
|
|
238041
|
+
if (ch === "\\" && i + 1 < body.length) {
|
|
238042
|
+
const next = body[i + 1];
|
|
238043
|
+
if (next === '"' || next === "\\") {
|
|
238044
|
+
expr += next;
|
|
238045
|
+
i++;
|
|
238046
|
+
continue;
|
|
238047
|
+
}
|
|
238048
|
+
}
|
|
238049
|
+
if (ch === '"') {
|
|
238050
|
+
closed = true;
|
|
238051
|
+
i++;
|
|
238052
|
+
break;
|
|
238053
|
+
}
|
|
238054
|
+
expr += ch;
|
|
238055
|
+
}
|
|
238056
|
+
if (!closed) {
|
|
238057
|
+
throw new Error(`authorize annotation has mismatched quotes: ${body}`);
|
|
238058
|
+
}
|
|
238059
|
+
const rest = body.slice(i).trim();
|
|
238060
|
+
if (rest.length > 0) {
|
|
238061
|
+
throw new Error(`authorize annotation has unexpected content after the expression: ${rest}`);
|
|
238062
|
+
}
|
|
238063
|
+
if (expr.trim().length === 0) {
|
|
238064
|
+
throw new Error("authorize annotation has an empty expression body");
|
|
238065
|
+
}
|
|
238066
|
+
return expr;
|
|
238067
|
+
}
|
|
237966
238068
|
|
|
237967
238069
|
// src/service/filter.ts
|
|
237968
238070
|
var VALID_FILTER_TYPES = new Set([
|
|
@@ -238155,149 +238257,6 @@ function tokenize(input) {
|
|
|
238155
238257
|
return tokens;
|
|
238156
238258
|
}
|
|
238157
238259
|
|
|
238158
|
-
// src/service/authorize.ts
|
|
238159
|
-
init_errors();
|
|
238160
|
-
var SOURCE_PREFIX = "#(authorize)";
|
|
238161
|
-
var FILE_PREFIX = "##(authorize)";
|
|
238162
|
-
function buildAuthorizeProbe(exprs) {
|
|
238163
|
-
const selects = exprs.map((expr, i) => `__auth_${i} is (${expr})`).join(`
|
|
238164
|
-
`);
|
|
238165
|
-
return `run: duckdb.sql("SELECT 1 AS __authorize_probe_row") -> {
|
|
238166
|
-
select:
|
|
238167
|
-
${selects}
|
|
238168
|
-
limit: 1
|
|
238169
|
-
}`;
|
|
238170
|
-
}
|
|
238171
|
-
function isProbeTrue(cell) {
|
|
238172
|
-
return cell === true || cell === 1 || cell === "true";
|
|
238173
|
-
}
|
|
238174
|
-
async function evaluateAuthorize(executor, exprs, givens) {
|
|
238175
|
-
for (const expr of exprs) {
|
|
238176
|
-
try {
|
|
238177
|
-
const result = await executor.loadQuery(buildAuthorizeProbe([expr])).run({ rowLimit: 1, givens });
|
|
238178
|
-
const row = result?.data?.value?.[0];
|
|
238179
|
-
if (row && isProbeTrue(row.__auth_0)) {
|
|
238180
|
-
return true;
|
|
238181
|
-
}
|
|
238182
|
-
} catch {
|
|
238183
|
-
continue;
|
|
238184
|
-
}
|
|
238185
|
-
}
|
|
238186
|
-
return false;
|
|
238187
|
-
}
|
|
238188
|
-
async function validateAuthorizeProbes(compiler, sources) {
|
|
238189
|
-
for (const source of sources) {
|
|
238190
|
-
const exprs = source.authorize;
|
|
238191
|
-
if (!exprs || exprs.length === 0)
|
|
238192
|
-
continue;
|
|
238193
|
-
try {
|
|
238194
|
-
await compiler.loadQuery(buildAuthorizeProbe(exprs)).getPreparedQuery();
|
|
238195
|
-
} catch (err) {
|
|
238196
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
238197
|
-
throw new ModelCompilationError({
|
|
238198
|
-
message: `Invalid #(authorize) annotation on source "${source.name ?? "(unnamed)"}" [${exprs.join(" | ")}]: ${detail}`
|
|
238199
|
-
});
|
|
238200
|
-
}
|
|
238201
|
-
}
|
|
238202
|
-
}
|
|
238203
|
-
function parseAuthorizeAnnotation(annotation) {
|
|
238204
|
-
const trimmed2 = annotation.trim();
|
|
238205
|
-
let body;
|
|
238206
|
-
if (trimmed2.startsWith(FILE_PREFIX)) {
|
|
238207
|
-
body = trimmed2.slice(FILE_PREFIX.length).trim();
|
|
238208
|
-
} else if (trimmed2.startsWith(SOURCE_PREFIX)) {
|
|
238209
|
-
body = trimmed2.slice(SOURCE_PREFIX.length).trim();
|
|
238210
|
-
} else {
|
|
238211
|
-
return null;
|
|
238212
|
-
}
|
|
238213
|
-
return unwrapQuotedExpression(body);
|
|
238214
|
-
}
|
|
238215
|
-
function collectAuthorizeExprs(annotations) {
|
|
238216
|
-
const exprs = [];
|
|
238217
|
-
for (const annotation of annotations) {
|
|
238218
|
-
const expr = parseAuthorizeAnnotation(annotation);
|
|
238219
|
-
if (expr !== null) {
|
|
238220
|
-
exprs.push(expr);
|
|
238221
|
-
}
|
|
238222
|
-
}
|
|
238223
|
-
return exprs;
|
|
238224
|
-
}
|
|
238225
|
-
function unwrapQuotedExpression(body) {
|
|
238226
|
-
if (body.length < 2 || body[0] !== '"') {
|
|
238227
|
-
throw new Error(`authorize annotation expression must be a double-quoted string, got: ${body || "(empty)"}`);
|
|
238228
|
-
}
|
|
238229
|
-
let expr = "";
|
|
238230
|
-
let i = 1;
|
|
238231
|
-
let closed = false;
|
|
238232
|
-
for (;i < body.length; i++) {
|
|
238233
|
-
const ch = body[i];
|
|
238234
|
-
if (ch === "\\" && i + 1 < body.length) {
|
|
238235
|
-
const next = body[i + 1];
|
|
238236
|
-
if (next === '"' || next === "\\") {
|
|
238237
|
-
expr += next;
|
|
238238
|
-
i++;
|
|
238239
|
-
continue;
|
|
238240
|
-
}
|
|
238241
|
-
}
|
|
238242
|
-
if (ch === '"') {
|
|
238243
|
-
closed = true;
|
|
238244
|
-
i++;
|
|
238245
|
-
break;
|
|
238246
|
-
}
|
|
238247
|
-
expr += ch;
|
|
238248
|
-
}
|
|
238249
|
-
if (!closed) {
|
|
238250
|
-
throw new Error(`authorize annotation has mismatched quotes: ${body}`);
|
|
238251
|
-
}
|
|
238252
|
-
const rest = body.slice(i).trim();
|
|
238253
|
-
if (rest.length > 0) {
|
|
238254
|
-
throw new Error(`authorize annotation has unexpected content after the expression: ${rest}`);
|
|
238255
|
-
}
|
|
238256
|
-
if (expr.trim().length === 0) {
|
|
238257
|
-
throw new Error("authorize annotation has an empty expression body");
|
|
238258
|
-
}
|
|
238259
|
-
return expr;
|
|
238260
|
-
}
|
|
238261
|
-
|
|
238262
|
-
// src/service/annotations.ts
|
|
238263
|
-
import { Annotations } from "@malloydata/malloy";
|
|
238264
|
-
function isReservedRoute(route) {
|
|
238265
|
-
return route === "" || !/[\p{L}\p{N}]/u.test(route);
|
|
238266
|
-
}
|
|
238267
|
-
function modelAnnotations(modelDef) {
|
|
238268
|
-
const registry = modelDef.modelAnnotations ?? {};
|
|
238269
|
-
const visited = new Set;
|
|
238270
|
-
const order = [];
|
|
238271
|
-
const visit = (id) => {
|
|
238272
|
-
if (visited.has(id))
|
|
238273
|
-
return;
|
|
238274
|
-
visited.add(id);
|
|
238275
|
-
const entry = registry[id];
|
|
238276
|
-
if (!entry)
|
|
238277
|
-
return;
|
|
238278
|
-
for (const dep of entry.inheritsFrom)
|
|
238279
|
-
visit(dep);
|
|
238280
|
-
order.push(id);
|
|
238281
|
-
};
|
|
238282
|
-
visit(modelDef.modelID);
|
|
238283
|
-
let folded;
|
|
238284
|
-
for (const id of order) {
|
|
238285
|
-
const own = registry[id].ownNotes;
|
|
238286
|
-
if (!own.notes?.length && !own.blockNotes?.length)
|
|
238287
|
-
continue;
|
|
238288
|
-
folded = {
|
|
238289
|
-
notes: own.notes,
|
|
238290
|
-
blockNotes: own.blockNotes,
|
|
238291
|
-
inherits: folded
|
|
238292
|
-
};
|
|
238293
|
-
}
|
|
238294
|
-
return folded ?? {};
|
|
238295
|
-
}
|
|
238296
|
-
function annotationTexts(annote) {
|
|
238297
|
-
const texts = new Annotations(annote).texts();
|
|
238298
|
-
return texts.length > 0 ? texts : undefined;
|
|
238299
|
-
}
|
|
238300
|
-
|
|
238301
238260
|
// src/service/given.ts
|
|
238302
238261
|
function malloyGivenToApi(given) {
|
|
238303
238262
|
const type = given.type;
|
|
@@ -238310,6 +238269,25 @@ function malloyGivenToApi(given) {
|
|
|
238310
238269
|
};
|
|
238311
238270
|
}
|
|
238312
238271
|
|
|
238272
|
+
// src/service/model_limits.ts
|
|
238273
|
+
init_errors();
|
|
238274
|
+
function resolveModelQueryRowLimit(userLimit, { defaultLimit, maxRows }) {
|
|
238275
|
+
const requested = userLimit && userLimit > 0 ? userLimit : defaultLimit;
|
|
238276
|
+
if (maxRows <= 0)
|
|
238277
|
+
return requested;
|
|
238278
|
+
return Math.min(requested, maxRows + 1);
|
|
238279
|
+
}
|
|
238280
|
+
function assertWithinModelResponseLimits(rowCount, serializedBytes, { maxRows, maxBytes }, source) {
|
|
238281
|
+
if (maxRows > 0 && rowCount > maxRows) {
|
|
238282
|
+
recordQueryCapExceeded("rows", source);
|
|
238283
|
+
throw new PayloadTooLargeError(`Query returned more than ${maxRows} rows. Refine the query (add a LIMIT or more selective WHERE) or raise PUBLISHER_MAX_QUERY_ROWS.`);
|
|
238284
|
+
}
|
|
238285
|
+
if (maxBytes > 0 && serializedBytes > maxBytes) {
|
|
238286
|
+
recordQueryCapExceeded("bytes", source);
|
|
238287
|
+
throw new PayloadTooLargeError(`Query response exceeded ${maxBytes} bytes (was ${serializedBytes}). Project fewer columns, add a LIMIT, or raise PUBLISHER_MAX_RESPONSE_BYTES.`);
|
|
238288
|
+
}
|
|
238289
|
+
}
|
|
238290
|
+
|
|
238313
238291
|
// src/service/source_extraction.ts
|
|
238314
238292
|
import {
|
|
238315
238293
|
isSourceDef
|
|
@@ -238388,25 +238366,6 @@ function extractQueriesFromModelDef(modelDef) {
|
|
|
238388
238366
|
}));
|
|
238389
238367
|
}
|
|
238390
238368
|
|
|
238391
|
-
// src/service/model_limits.ts
|
|
238392
|
-
init_errors();
|
|
238393
|
-
function resolveModelQueryRowLimit(userLimit, { defaultLimit, maxRows }) {
|
|
238394
|
-
const requested = userLimit && userLimit > 0 ? userLimit : defaultLimit;
|
|
238395
|
-
if (maxRows <= 0)
|
|
238396
|
-
return requested;
|
|
238397
|
-
return Math.min(requested, maxRows + 1);
|
|
238398
|
-
}
|
|
238399
|
-
function assertWithinModelResponseLimits(rowCount, serializedBytes, { maxRows, maxBytes }, source) {
|
|
238400
|
-
if (maxRows > 0 && rowCount > maxRows) {
|
|
238401
|
-
recordQueryCapExceeded("rows", source);
|
|
238402
|
-
throw new PayloadTooLargeError(`Query returned more than ${maxRows} rows. Refine the query (add a LIMIT or more selective WHERE) or raise PUBLISHER_MAX_QUERY_ROWS.`);
|
|
238403
|
-
}
|
|
238404
|
-
if (maxBytes > 0 && serializedBytes > maxBytes) {
|
|
238405
|
-
recordQueryCapExceeded("bytes", source);
|
|
238406
|
-
throw new PayloadTooLargeError(`Query response exceeded ${maxBytes} bytes (was ${serializedBytes}). Project fewer columns, add a LIMIT, or raise PUBLISHER_MAX_RESPONSE_BYTES.`);
|
|
238407
|
-
}
|
|
238408
|
-
}
|
|
238409
|
-
|
|
238410
238369
|
// src/service/model.ts
|
|
238411
238370
|
var MALLOY_VERSION = createRequire2(import.meta.url)("@malloydata/malloy/package.json").version;
|
|
238412
238371
|
function quoteMalloyIdentifier(name) {
|
|
@@ -238788,9 +238747,7 @@ class Model {
|
|
|
238788
238747
|
}
|
|
238789
238748
|
const errors2 = validateRenderTags2(result).filter((log) => log.severity === "error");
|
|
238790
238749
|
if (errors2.length > 0) {
|
|
238791
|
-
|
|
238792
|
-
message: `Invalid renderer configuration on '${target.label}': ${errors2.map((e) => e.message).join("; ")}`
|
|
238793
|
-
});
|
|
238750
|
+
logger.warn(`Invalid renderer configuration on '${target.label}': ${errors2.map((e) => e.message).join("; ")}`);
|
|
238794
238751
|
}
|
|
238795
238752
|
}
|
|
238796
238753
|
}
|
|
@@ -239309,14 +239266,35 @@ function deriveColumns(persistSource) {
|
|
|
239309
239266
|
} catch (err) {
|
|
239310
239267
|
logger.warn("Failed to derive columns for persist source", {
|
|
239311
239268
|
sourceID: persistSource.sourceID,
|
|
239312
|
-
error:
|
|
239269
|
+
error: errMessage(err)
|
|
239313
239270
|
});
|
|
239314
239271
|
return [];
|
|
239315
239272
|
}
|
|
239316
239273
|
}
|
|
239274
|
+
function deriveAnnotationFields(persistSource) {
|
|
239275
|
+
const out = {};
|
|
239276
|
+
try {
|
|
239277
|
+
const tag = persistSource.annotations.parseAsTag("@").tag;
|
|
239278
|
+
for (const [key, value] of tag.entries()) {
|
|
239279
|
+
const text = value.text();
|
|
239280
|
+
if (text !== undefined)
|
|
239281
|
+
out[key] = text;
|
|
239282
|
+
}
|
|
239283
|
+
} catch {}
|
|
239284
|
+
return out;
|
|
239285
|
+
}
|
|
239317
239286
|
function flattenDependsOn(node) {
|
|
239318
239287
|
return node.dependsOn.map((d) => d.sourceID);
|
|
239319
239288
|
}
|
|
239289
|
+
function* iterGraphSources(graph, sources) {
|
|
239290
|
+
for (const level of graph.nodes) {
|
|
239291
|
+
for (const node of level) {
|
|
239292
|
+
const source = sources[node.sourceID];
|
|
239293
|
+
if (source)
|
|
239294
|
+
yield source;
|
|
239295
|
+
}
|
|
239296
|
+
}
|
|
239297
|
+
}
|
|
239320
239298
|
function computeBuildId(source, connectionDigests) {
|
|
239321
239299
|
return source.makeBuildId(connectionDigests[source.connectionName], source.getSQL());
|
|
239322
239300
|
}
|
|
@@ -239331,7 +239309,7 @@ async function resolvePackageConnections(pkg, names) {
|
|
|
239331
239309
|
map.set(name, await pkg.getMalloyConnection(name));
|
|
239332
239310
|
} catch (err) {
|
|
239333
239311
|
logger.warn(`Failed to resolve connection ${name}`, {
|
|
239334
|
-
error:
|
|
239312
|
+
error: errMessage(err)
|
|
239335
239313
|
});
|
|
239336
239314
|
}
|
|
239337
239315
|
}
|
|
@@ -239341,6 +239319,8 @@ async function compilePackageBuildPlan(pkg, signal) {
|
|
|
239341
239319
|
const allGraphs = [];
|
|
239342
239320
|
const allSources = {};
|
|
239343
239321
|
for (const modelPath of pkg.getModelPaths()) {
|
|
239322
|
+
if (!modelPath.endsWith(MODEL_FILE_SUFFIX))
|
|
239323
|
+
continue;
|
|
239344
239324
|
if (signal?.aborted)
|
|
239345
239325
|
throw new Error("Build cancelled");
|
|
239346
239326
|
const { runtime, modelURL, importBaseURL } = await Model.getModelRuntime(pkg.getPackagePath(), modelPath, pkg.getMalloyConfig());
|
|
@@ -239402,7 +239382,8 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames) {
|
|
|
239402
239382
|
dialect: source.dialectName,
|
|
239403
239383
|
buildId: computeBuildId(source, connectionDigests),
|
|
239404
239384
|
sql: source.getSQL(),
|
|
239405
|
-
columns: deriveColumns(source)
|
|
239385
|
+
columns: deriveColumns(source),
|
|
239386
|
+
annotationFields: deriveAnnotationFields(source)
|
|
239406
239387
|
};
|
|
239407
239388
|
}
|
|
239408
239389
|
return { graphs: wireGraphs, sources: wireSources };
|
|
@@ -239415,6 +239396,27 @@ async function computePackageBuildPlan(pkg, signal) {
|
|
|
239415
239396
|
return deriveBuildPlan(compiled.graphs, compiled.sources, compiled.connectionDigests);
|
|
239416
239397
|
}
|
|
239417
239398
|
|
|
239399
|
+
// src/service/persist_annotation_validation.ts
|
|
239400
|
+
init_errors();
|
|
239401
|
+
var PERSIST_LINE_PATTERN = /^\s*#@\s+persist\b/;
|
|
239402
|
+
var UNQUOTED_NAME_PATTERN = /\bname\s*=\s*(?!["'])/;
|
|
239403
|
+
function assertPersistNamesQuoted(modelSource, modelPath) {
|
|
239404
|
+
const offenders = [];
|
|
239405
|
+
for (const rawLine of modelSource.split(`
|
|
239406
|
+
`)) {
|
|
239407
|
+
if (!PERSIST_LINE_PATTERN.test(rawLine))
|
|
239408
|
+
continue;
|
|
239409
|
+
if (UNQUOTED_NAME_PATTERN.test(rawLine)) {
|
|
239410
|
+
offenders.push(rawLine.trim());
|
|
239411
|
+
}
|
|
239412
|
+
}
|
|
239413
|
+
if (offenders.length > 0) {
|
|
239414
|
+
throw new ModelCompilationError({
|
|
239415
|
+
message: `${modelPath}: persist annotation name must be quoted. Write a quoted ` + `value like name="engaged_events" (or a dialect table path such as ` + `name="my_dataset.engaged_events"), not a bare value — an unquoted ` + `persist name is dropped from the build plan, so the source would ` + `publish but never materialize. Offending annotation(s): ` + `${offenders.join("; ")}.`
|
|
239416
|
+
});
|
|
239417
|
+
}
|
|
239418
|
+
}
|
|
239419
|
+
|
|
239418
239420
|
// src/service/package.ts
|
|
239419
239421
|
class Package {
|
|
239420
239422
|
environmentName;
|
|
@@ -239558,6 +239560,10 @@ class Package {
|
|
|
239558
239560
|
}
|
|
239559
239561
|
const model = Model.fromSerialized(packageName, packagePath, malloyConfig, sm);
|
|
239560
239562
|
await model.validateRenderTags();
|
|
239563
|
+
if (sm.modelPath.endsWith(MODEL_FILE_SUFFIX)) {
|
|
239564
|
+
const modelSource = await fs6.readFile(path7.join(packagePath, sm.modelPath), "utf-8");
|
|
239565
|
+
assertPersistNamesQuoted(modelSource, sm.modelPath);
|
|
239566
|
+
}
|
|
239561
239567
|
models.set(sm.modelPath, model);
|
|
239562
239568
|
}
|
|
239563
239569
|
const endTime = performance.now();
|
|
@@ -239578,7 +239584,7 @@ class Package {
|
|
|
239578
239584
|
} catch (err) {
|
|
239579
239585
|
logger.warn(`Failed to compute build plan for package ${packageName}`, {
|
|
239580
239586
|
packageName,
|
|
239581
|
-
error:
|
|
239587
|
+
error: errMessage(err)
|
|
239582
239588
|
});
|
|
239583
239589
|
}
|
|
239584
239590
|
const invalidMsg = pkg.formatInvalidExplores();
|
|
@@ -248571,15 +248577,19 @@ init_logger();
|
|
|
248571
248577
|
import { Manifest } from "@malloydata/malloy";
|
|
248572
248578
|
|
|
248573
248579
|
// src/service/quoting.ts
|
|
248574
|
-
function
|
|
248580
|
+
function bareTableName(tableName) {
|
|
248575
248581
|
const lastDot = tableName.lastIndexOf(".");
|
|
248576
|
-
|
|
248577
|
-
|
|
248578
|
-
|
|
248579
|
-
|
|
248580
|
-
|
|
248582
|
+
return lastDot >= 0 ? tableName.substring(lastDot + 1) : tableName;
|
|
248583
|
+
}
|
|
248584
|
+
var BACKTICK_DIALECTS = new Set(["standardsql", "mysql", "databricks"]);
|
|
248585
|
+
function quoteIdentifier(identifier, dialect) {
|
|
248586
|
+
if (BACKTICK_DIALECTS.has(dialect)) {
|
|
248587
|
+
return "`" + identifier.replace(/`/g, "``") + "`";
|
|
248581
248588
|
}
|
|
248582
|
-
return
|
|
248589
|
+
return '"' + identifier.replace(/"/g, '""') + '"';
|
|
248590
|
+
}
|
|
248591
|
+
function quoteTablePath(tableName, dialect) {
|
|
248592
|
+
return tableName.split(".").map((segment) => quoteIdentifier(segment, dialect)).join(".");
|
|
248583
248593
|
}
|
|
248584
248594
|
|
|
248585
248595
|
// src/service/resolve_environment.ts
|
|
@@ -248598,11 +248608,7 @@ function stagingSuffix(buildId) {
|
|
|
248598
248608
|
return `_${buildId.substring(0, STAGING_BUILD_ID_LEN)}`;
|
|
248599
248609
|
}
|
|
248600
248610
|
function selfAssignTableName(persistSource) {
|
|
248601
|
-
|
|
248602
|
-
return persistSource.annotations.parseAsTag("@").tag.text("name") || persistSource.name;
|
|
248603
|
-
} catch {
|
|
248604
|
-
return persistSource.name;
|
|
248605
|
-
}
|
|
248611
|
+
return deriveAnnotationFields(persistSource).name || persistSource.name;
|
|
248606
248612
|
}
|
|
248607
248613
|
function outcomeFor(_err, signal) {
|
|
248608
248614
|
return signal?.aborted ? "cancelled" : "failed";
|
|
@@ -248670,7 +248676,7 @@ class MaterializationService {
|
|
|
248670
248676
|
}
|
|
248671
248677
|
const active2 = await this.repository.getActiveMaterialization(environmentId, packageName);
|
|
248672
248678
|
if (active2) {
|
|
248673
|
-
throw
|
|
248679
|
+
throw this.activeConflict(packageName, active2.id);
|
|
248674
248680
|
}
|
|
248675
248681
|
const forceRefresh = options.forceRefresh ?? false;
|
|
248676
248682
|
const metadata = {
|
|
@@ -248684,7 +248690,7 @@ class MaterializationService {
|
|
|
248684
248690
|
} catch (err) {
|
|
248685
248691
|
if (err instanceof DuplicateActiveMaterializationError) {
|
|
248686
248692
|
const winner = await this.repository.getActiveMaterialization(environmentId, packageName);
|
|
248687
|
-
throw
|
|
248693
|
+
throw this.activeConflict(packageName, winner?.id);
|
|
248688
248694
|
}
|
|
248689
248695
|
throw err;
|
|
248690
248696
|
}
|
|
@@ -248705,6 +248711,9 @@ class MaterializationService {
|
|
|
248705
248711
|
});
|
|
248706
248712
|
const startedAt = Date.now();
|
|
248707
248713
|
try {
|
|
248714
|
+
await this.repository.updateMaterialization(id, {
|
|
248715
|
+
startedAt: new Date(startedAt)
|
|
248716
|
+
});
|
|
248708
248717
|
const environmentId = await this.resolveEnvironmentId(environmentName);
|
|
248709
248718
|
const environment = await this.environmentStore.getEnvironment(environmentName, false);
|
|
248710
248719
|
const pkg = await environment.getPackage(packageName, false);
|
|
@@ -248755,29 +248764,24 @@ class MaterializationService {
|
|
|
248755
248764
|
const carried = {};
|
|
248756
248765
|
const seen = new Set;
|
|
248757
248766
|
for (const graph of compiled.graphs) {
|
|
248758
|
-
for (const
|
|
248759
|
-
|
|
248760
|
-
|
|
248761
|
-
|
|
248762
|
-
|
|
248763
|
-
|
|
248764
|
-
|
|
248765
|
-
|
|
248766
|
-
|
|
248767
|
-
|
|
248768
|
-
|
|
248769
|
-
const prior = priorEntries[buildId];
|
|
248770
|
-
if (prior && prior.physicalTableName) {
|
|
248771
|
-
carried[buildId] = prior;
|
|
248772
|
-
continue;
|
|
248773
|
-
}
|
|
248774
|
-
instructions.push({
|
|
248775
|
-
buildId,
|
|
248776
|
-
materializedTableId: `local-${buildId.substring(0, STAGING_BUILD_ID_LEN)}`,
|
|
248777
|
-
physicalTableName: selfAssignTableName(persistSource),
|
|
248778
|
-
realization: "COPY"
|
|
248779
|
-
});
|
|
248767
|
+
for (const persistSource of iterGraphSources(graph, compiled.sources)) {
|
|
248768
|
+
if (include && !include.has(persistSource.name))
|
|
248769
|
+
continue;
|
|
248770
|
+
const buildId = computeBuildId(persistSource, compiled.connectionDigests);
|
|
248771
|
+
if (seen.has(buildId))
|
|
248772
|
+
continue;
|
|
248773
|
+
seen.add(buildId);
|
|
248774
|
+
const prior = priorEntries[buildId];
|
|
248775
|
+
if (prior && prior.physicalTableName) {
|
|
248776
|
+
carried[buildId] = prior;
|
|
248777
|
+
continue;
|
|
248780
248778
|
}
|
|
248779
|
+
instructions.push({
|
|
248780
|
+
buildId,
|
|
248781
|
+
materializedTableId: `local-${buildId.substring(0, STAGING_BUILD_ID_LEN)}`,
|
|
248782
|
+
physicalTableName: selfAssignTableName(persistSource),
|
|
248783
|
+
realization: "COPY"
|
|
248784
|
+
});
|
|
248781
248785
|
}
|
|
248782
248786
|
}
|
|
248783
248787
|
return { instructions, carried };
|
|
@@ -248851,20 +248855,15 @@ class MaterializationService {
|
|
|
248851
248855
|
if (!connection) {
|
|
248852
248856
|
throw new BadRequestError(`Connection '${graph.connectionName}' not found`);
|
|
248853
248857
|
}
|
|
248854
|
-
for (const
|
|
248855
|
-
|
|
248856
|
-
|
|
248857
|
-
|
|
248858
|
-
|
|
248859
|
-
|
|
248860
|
-
|
|
248861
|
-
|
|
248862
|
-
|
|
248863
|
-
if (!instruction)
|
|
248864
|
-
continue;
|
|
248865
|
-
const entry = await this.buildOneSource(persistSource, instruction, connection, connectionDigests, manifest);
|
|
248866
|
-
entries[buildId] = entry;
|
|
248867
|
-
}
|
|
248858
|
+
for (const persistSource of iterGraphSources(graph, sources)) {
|
|
248859
|
+
if (signal.aborted)
|
|
248860
|
+
throw new Error("Build cancelled");
|
|
248861
|
+
const buildId = computeBuildId(persistSource, connectionDigests);
|
|
248862
|
+
const instruction = byBuildId.get(buildId);
|
|
248863
|
+
if (!instruction)
|
|
248864
|
+
continue;
|
|
248865
|
+
const entry = await this.buildOneSource(persistSource, instruction, connection, connectionDigests, manifest);
|
|
248866
|
+
entries[buildId] = entry;
|
|
248868
248867
|
}
|
|
248869
248868
|
}
|
|
248870
248869
|
return entries;
|
|
@@ -248876,22 +248875,26 @@ class MaterializationService {
|
|
|
248876
248875
|
buildManifest: manifest.buildManifest,
|
|
248877
248876
|
connectionDigests
|
|
248878
248877
|
});
|
|
248879
|
-
const
|
|
248878
|
+
const bareName = bareTableName(physicalTableName);
|
|
248880
248879
|
const stagingTableName = `${physicalTableName}${stagingSuffix(buildId)}`;
|
|
248880
|
+
const dialect = persistSource.dialectName;
|
|
248881
|
+
const quotedStaging = quoteTablePath(stagingTableName, dialect);
|
|
248882
|
+
const quotedPhysical = quoteTablePath(physicalTableName, dialect);
|
|
248883
|
+
const quotedBareName = quoteIdentifier(bareName, dialect);
|
|
248881
248884
|
const startTime = performance.now();
|
|
248882
|
-
await connection.runSQL(`DROP TABLE IF EXISTS ${
|
|
248885
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quotedStaging}`);
|
|
248883
248886
|
try {
|
|
248884
|
-
await connection.runSQL(`CREATE TABLE ${
|
|
248885
|
-
await connection.runSQL(`DROP TABLE IF EXISTS ${
|
|
248886
|
-
await connection.runSQL(`ALTER TABLE ${
|
|
248887
|
+
await connection.runSQL(`CREATE TABLE ${quotedStaging} AS (${buildSQL})`);
|
|
248888
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quotedPhysical}`);
|
|
248889
|
+
await connection.runSQL(`ALTER TABLE ${quotedStaging} RENAME TO ${quotedBareName}`);
|
|
248887
248890
|
} catch (err) {
|
|
248888
248891
|
try {
|
|
248889
|
-
await connection.runSQL(`DROP TABLE IF EXISTS ${
|
|
248892
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quotedStaging}`);
|
|
248890
248893
|
} catch (cleanupErr) {
|
|
248891
248894
|
logger.warn("Failed to clean up staging table after a failed build; physical leak", {
|
|
248892
248895
|
stagingTableName,
|
|
248893
248896
|
connectionName: persistSource.connectionName,
|
|
248894
|
-
cleanupError:
|
|
248897
|
+
cleanupError: errMessage(cleanupErr)
|
|
248895
248898
|
});
|
|
248896
248899
|
}
|
|
248897
248900
|
throw err;
|
|
@@ -248971,8 +248974,9 @@ class MaterializationService {
|
|
|
248971
248974
|
connection = await pkg.getMalloyConnection(connectionName);
|
|
248972
248975
|
connectionCache.set(connectionName, connection);
|
|
248973
248976
|
}
|
|
248974
|
-
|
|
248975
|
-
await connection.runSQL(`DROP TABLE IF EXISTS ${physicalTableName
|
|
248977
|
+
const dialect = connection.dialectName;
|
|
248978
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quoteTablePath(physicalTableName, dialect)}`);
|
|
248979
|
+
await connection.runSQL(`DROP TABLE IF EXISTS ${quoteTablePath(`${physicalTableName}${stagingSuffix(entry.buildId)}`, dialect)}`);
|
|
248976
248980
|
recordDropTables("success");
|
|
248977
248981
|
logger.info("Dropped materialized table on delete", {
|
|
248978
248982
|
materializationId: m.id,
|
|
@@ -248985,7 +248989,7 @@ class MaterializationService {
|
|
|
248985
248989
|
materializationId: m.id,
|
|
248986
248990
|
physicalTableName,
|
|
248987
248991
|
connectionName,
|
|
248988
|
-
error:
|
|
248992
|
+
error: errMessage(err)
|
|
248989
248993
|
});
|
|
248990
248994
|
}
|
|
248991
248995
|
}
|
|
@@ -249003,6 +249007,10 @@ class MaterializationService {
|
|
|
249003
249007
|
metadata
|
|
249004
249008
|
});
|
|
249005
249009
|
}
|
|
249010
|
+
activeConflict(packageName, activeId) {
|
|
249011
|
+
const suffix = activeId ? ` (${activeId})` : "";
|
|
249012
|
+
return new MaterializationConflictError(`Package ${packageName} already has an active materialization${suffix}`);
|
|
249013
|
+
}
|
|
249006
249014
|
recordRun(mode, outcome, startedAtMs) {
|
|
249007
249015
|
recordMaterializationRun(mode, outcome, Date.now() - startedAtMs);
|
|
249008
249016
|
}
|
|
@@ -249010,7 +249018,7 @@ class MaterializationService {
|
|
|
249010
249018
|
const abortController = new AbortController;
|
|
249011
249019
|
this.runningAbortControllers.set(id, abortController);
|
|
249012
249020
|
run(abortController.signal).catch(async (err) => {
|
|
249013
|
-
const message =
|
|
249021
|
+
const message = errMessage(err);
|
|
249014
249022
|
const next = abortController.signal.aborted ? "CANCELLED" : "FAILED";
|
|
249015
249023
|
try {
|
|
249016
249024
|
await this.repository.updateMaterialization(id, {
|
|
@@ -249022,7 +249030,7 @@ class MaterializationService {
|
|
|
249022
249030
|
logger.error("Failed to record materialization failure", {
|
|
249023
249031
|
materializationId: id,
|
|
249024
249032
|
originalError: message,
|
|
249025
|
-
transitionError:
|
|
249033
|
+
transitionError: errMessage(transitionErr)
|
|
249026
249034
|
});
|
|
249027
249035
|
}
|
|
249028
249036
|
}).finally(() => {
|