@malloy-publisher/server 0.10.3 → 0.10.5
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 +24 -11
- package/dist/cpufeatures-gknxfsk5.node +0 -0
- package/dist/instrumentation.mjs +1 -1
- package/dist/package_load_worker.mjs +81 -48
- package/dist/server.mjs +349 -119
- package/dist/sshcrypto-a4xyg5e8.node +0 -0
- package/package.json +1 -1
- package/dist/cpufeatures-1yrn0vtw.node +0 -0
package/dist/app/api-doc.yaml
CHANGED
|
@@ -6685,8 +6685,11 @@ components:
|
|
|
6685
6685
|
`sqlSource` answer 400 for anything else (another statement type, or
|
|
6686
6686
|
more than one statement), and `sqlTemporaryTable` answers 400 always.
|
|
6687
6687
|
This is a rule on statements, not on what a SELECT reads: that is
|
|
6688
|
-
bounded by the session's storage secret
|
|
6689
|
-
|
|
6688
|
+
bounded by the catalog role and by the session's storage secret,
|
|
6689
|
+
which on a managed connection attached read-only is scoped to the
|
|
6690
|
+
`main` schema's recorded path, so a SELECT reads only that schema's
|
|
6691
|
+
files; another schema's files, and any other object in the bucket,
|
|
6692
|
+
are unsigned (403). Absent or false is the pre-existing behavior.
|
|
6690
6693
|
fingerprint:
|
|
6691
6694
|
type: string
|
|
6692
6695
|
description: >
|
|
@@ -7157,7 +7160,11 @@ components:
|
|
|
7157
7160
|
every attach uses the data path the catalog recorded at its
|
|
7158
7161
|
creation, and no attach ever creates a catalog: the shape for a
|
|
7159
7162
|
catalog created elsewhere, through the admin API
|
|
7160
|
-
(admin-api-doc.yaml) or by another tool.
|
|
7163
|
+
(admin-api-doc.yaml) or by another tool. Either way the storage
|
|
7164
|
+
secret is scoped to the data path, the given one or the
|
|
7165
|
+
recorded one: a read of any other object through this
|
|
7166
|
+
connection is unsigned, and on S3 the host's AWS_* environment
|
|
7167
|
+
never signs it either. A catalog with neither is not attached.
|
|
7161
7168
|
s3Connection:
|
|
7162
7169
|
$ref: "#/components/schemas/S3Connection"
|
|
7163
7170
|
description: AWS S3 connection configuration for data storage
|
|
@@ -7990,17 +7997,18 @@ components:
|
|
|
7990
7997
|
BuildPlan:
|
|
7991
7998
|
type: object
|
|
7992
7999
|
description: |
|
|
7993
|
-
The package's persist build plan
|
|
7994
|
-
|
|
7995
|
-
identity/naming/realization.
|
|
7996
|
-
are intentionally omitted until they
|
|
8000
|
+
The package's persist build plan, from Malloy's build targets: the order
|
|
8001
|
+
its persist sources build in, the tables each reads, and the minimal
|
|
8002
|
+
per-source detail a caller needs to assign identity/naming/realization.
|
|
8003
|
+
Policy and connection capability are intentionally omitted until they
|
|
8004
|
+
carry real data.
|
|
7997
8005
|
required: [graphs, sources]
|
|
7998
8006
|
properties:
|
|
7999
8007
|
graphs:
|
|
8000
8008
|
type: array
|
|
8001
8009
|
description: >-
|
|
8002
8010
|
Dependency-ordered build graphs. One per connection per model, not one
|
|
8003
|
-
per connection: a model contributes a graph for each connection its
|
|
8011
|
+
per connection: a model contributes a graph for each connection its
|
|
8004
8012
|
persist sources use, and a package's plan concatenates every model's, so
|
|
8005
8013
|
the same source appears in the graph of every model that imports it.
|
|
8006
8014
|
items:
|
|
@@ -8143,8 +8151,9 @@ components:
|
|
|
8143
8151
|
nodes:
|
|
8144
8152
|
type: array
|
|
8145
8153
|
description:
|
|
8146
|
-
|
|
8147
|
-
|
|
8154
|
+
A single level holding every persist source of the graph, in build
|
|
8155
|
+
order. Each node's `dependsOn` names every source on every table it
|
|
8156
|
+
reads, so the edges are complete without walking anything.
|
|
8148
8157
|
items:
|
|
8149
8158
|
type: array
|
|
8150
8159
|
items:
|
|
@@ -8159,7 +8168,11 @@ components:
|
|
|
8159
8168
|
description: "sourceName@modelURL"
|
|
8160
8169
|
dependsOn:
|
|
8161
8170
|
type: array
|
|
8162
|
-
description:
|
|
8171
|
+
description: >-
|
|
8172
|
+
sourceIDs of the sources on the tables this source reads. Sources on
|
|
8173
|
+
one table (a source and its extensions) read none of each other. A
|
|
8174
|
+
source Malloy cannot plan, which is refused rather than built, lists
|
|
8175
|
+
none.
|
|
8163
8176
|
items:
|
|
8164
8177
|
type: string
|
|
8165
8178
|
|
|
Binary file
|
package/dist/instrumentation.mjs
CHANGED
|
@@ -164831,7 +164831,7 @@ var init_telemetry = __esm(() => {
|
|
|
164831
164831
|
});
|
|
164832
164832
|
|
|
164833
164833
|
// package.json
|
|
164834
|
-
var version = "0.10.
|
|
164834
|
+
var version = "0.10.5";
|
|
164835
164835
|
var init_package = () => {};
|
|
164836
164836
|
|
|
164837
164837
|
// src/version.ts
|
|
@@ -22216,7 +22216,7 @@ async function compileSynthesizedPreaggregation(args) {
|
|
|
22216
22216
|
const { runtime, importBaseURL } = await args.getRuntime(new Map([[synthesizedURL.href, text]]));
|
|
22217
22217
|
const materializer = runtime.loadModel(synthesizedURL, { importBaseURL });
|
|
22218
22218
|
const model = await materializer.getModel();
|
|
22219
|
-
return { plans, model, materializer, text };
|
|
22219
|
+
return { plans, model, materializer, runtime, text };
|
|
22220
22220
|
}
|
|
22221
22221
|
async function tryCompileSynthesizedPreaggregation(args) {
|
|
22222
22222
|
try {
|
|
@@ -23215,27 +23215,47 @@ function composeDeclaredQueryMetadata(layers) {
|
|
|
23215
23215
|
const resolved = Object.assign({}, ...ordered);
|
|
23216
23216
|
return Object.keys(resolved).length > 0 ? resolved : null;
|
|
23217
23217
|
}
|
|
23218
|
-
function
|
|
23219
|
-
|
|
23220
|
-
}
|
|
23221
|
-
|
|
23222
|
-
|
|
23223
|
-
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
for (const dep of node.dependsOn) {
|
|
23227
|
-
yield* visit(dep);
|
|
23218
|
+
function sourceBuildOf(build, unbuildable) {
|
|
23219
|
+
const order = [];
|
|
23220
|
+
const dependsOn = {};
|
|
23221
|
+
for (const target of build.targets) {
|
|
23222
|
+
const reads = target.dependsOn.flatMap((t) => t.sources.map((s) => s.sourceID));
|
|
23223
|
+
for (const source of target.sources) {
|
|
23224
|
+
order.push(source.sourceID);
|
|
23225
|
+
dependsOn[source.sourceID] = reads;
|
|
23228
23226
|
}
|
|
23229
|
-
|
|
23230
|
-
|
|
23227
|
+
}
|
|
23228
|
+
for (const { source } of unbuildable) {
|
|
23229
|
+
if (source.connectionName !== build.connectionName)
|
|
23230
|
+
continue;
|
|
23231
|
+
order.push(source.sourceID);
|
|
23232
|
+
dependsOn[source.sourceID] = [];
|
|
23233
|
+
}
|
|
23234
|
+
return { connectionName: build.connectionName, order, dependsOn };
|
|
23235
|
+
}
|
|
23236
|
+
function sourceBuildsOf(targets) {
|
|
23237
|
+
const builds = targets.connections.map((c) => sourceBuildOf(c, targets.unbuildable));
|
|
23238
|
+
const planned = new Set(builds.map((b) => b.connectionName));
|
|
23239
|
+
for (const { source } of targets.unbuildable) {
|
|
23240
|
+
if (planned.has(source.connectionName))
|
|
23241
|
+
continue;
|
|
23242
|
+
planned.add(source.connectionName);
|
|
23243
|
+
builds.push(sourceBuildOf({ connectionName: source.connectionName, targets: [] }, targets.unbuildable));
|
|
23244
|
+
}
|
|
23245
|
+
return builds;
|
|
23246
|
+
}
|
|
23247
|
+
function plannedSources(targets) {
|
|
23248
|
+
return [
|
|
23249
|
+
...targets.connections.flatMap((c) => c.targets.flatMap((t) => t.sources)),
|
|
23250
|
+
...targets.unbuildable.map((u) => u.source)
|
|
23251
|
+
];
|
|
23252
|
+
}
|
|
23253
|
+
function* sourcesInBuildOrder(build, sources) {
|
|
23254
|
+
for (const sourceID of build.order) {
|
|
23255
|
+
const source = sources[sourceID];
|
|
23231
23256
|
if (source)
|
|
23232
23257
|
yield source;
|
|
23233
23258
|
}
|
|
23234
|
-
for (const level of graph.nodes) {
|
|
23235
|
-
for (const node of level) {
|
|
23236
|
-
yield* visit(node);
|
|
23237
|
-
}
|
|
23238
|
-
}
|
|
23239
23259
|
}
|
|
23240
23260
|
function computeSourceEntityId(source, connectionDigests) {
|
|
23241
23261
|
return source.makeBuildId(connectionDigests[source.connectionName], addressPartitionLayout(source, source.getSQL()));
|
|
@@ -23324,7 +23344,7 @@ async function classifyPersistSourceGate(persistSource, modelDef, materializer,
|
|
|
23324
23344
|
}
|
|
23325
23345
|
function emptyBuildPlanParts() {
|
|
23326
23346
|
return {
|
|
23327
|
-
|
|
23347
|
+
builds: [],
|
|
23328
23348
|
sources: {},
|
|
23329
23349
|
sourceModelPaths: {},
|
|
23330
23350
|
droppedPersistSources: [],
|
|
@@ -23333,7 +23353,7 @@ function emptyBuildPlanParts() {
|
|
|
23333
23353
|
};
|
|
23334
23354
|
}
|
|
23335
23355
|
function mergeBuildPlanParts(into, from) {
|
|
23336
|
-
into.
|
|
23356
|
+
into.builds.push(...from.builds);
|
|
23337
23357
|
Object.assign(into.sources, from.sources);
|
|
23338
23358
|
Object.assign(into.sourceModelPaths, from.sourceModelPaths);
|
|
23339
23359
|
into.droppedPersistSources.push(...from.droppedPersistSources);
|
|
@@ -23341,7 +23361,14 @@ function mergeBuildPlanParts(into, from) {
|
|
|
23341
23361
|
Object.assign(into.sourceGateOutcomes, from.sourceGateOutcomes);
|
|
23342
23362
|
}
|
|
23343
23363
|
async function collectModelBuildPlan(parts, args) {
|
|
23344
|
-
const {
|
|
23364
|
+
const {
|
|
23365
|
+
modelPath,
|
|
23366
|
+
packagePath,
|
|
23367
|
+
materializer,
|
|
23368
|
+
malloyModel,
|
|
23369
|
+
runtime,
|
|
23370
|
+
getRuntime
|
|
23371
|
+
} = args;
|
|
23345
23372
|
const synthesized = args.synthesized !== undefined ? args.synthesized : await tryCompileSynthesizedPreaggregation({
|
|
23346
23373
|
packagePath,
|
|
23347
23374
|
modelPath,
|
|
@@ -23349,14 +23376,14 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23349
23376
|
contents: malloyModel._modelDef?.contents ?? {}
|
|
23350
23377
|
});
|
|
23351
23378
|
if (synthesized) {
|
|
23352
|
-
const
|
|
23353
|
-
|
|
23354
|
-
parts.graphs.push(...rollupPlan.graphs);
|
|
23379
|
+
const rollupTargets = await synthesized.runtime.getBuildTargets(synthesized.model);
|
|
23380
|
+
parts.builds.push(...sourceBuildsOf(rollupTargets));
|
|
23355
23381
|
const planByName = new Map(synthesized.plans.map((p) => [p.rollupSourceName, p]));
|
|
23356
23382
|
let rollupClassificationCtx;
|
|
23357
|
-
for (const
|
|
23358
|
-
if (!
|
|
23383
|
+
for (const source of plannedSources(rollupTargets)) {
|
|
23384
|
+
if (!planByName.has(source.name))
|
|
23359
23385
|
continue;
|
|
23386
|
+
const sourceID = source.sourceID;
|
|
23360
23387
|
parts.sources[sourceID] = source;
|
|
23361
23388
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
23362
23389
|
const rollup = planByName.get(source.name);
|
|
@@ -23375,23 +23402,25 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23375
23402
|
const modelTag = malloyModel.modelAnnotations.parseAsTag("!").tag;
|
|
23376
23403
|
if (!modelTag.has("experimental", "persistence"))
|
|
23377
23404
|
return;
|
|
23378
|
-
const
|
|
23379
|
-
for (const msg of
|
|
23405
|
+
const targets = await runtime.getBuildTargets(malloyModel);
|
|
23406
|
+
for (const msg of targets.tagParseLog) {
|
|
23380
23407
|
logger.warn("Persist annotation issue", {
|
|
23381
23408
|
modelPath,
|
|
23382
23409
|
message: msg.message,
|
|
23383
23410
|
severity: msg.severity
|
|
23384
23411
|
});
|
|
23385
23412
|
}
|
|
23386
|
-
const
|
|
23413
|
+
const planned = plannedSources(targets);
|
|
23414
|
+
const recognizedNames = new Set(planned.map((s) => s.name));
|
|
23387
23415
|
for (const name of detectDroppedPersistSources(malloyModel, recognizedNames)) {
|
|
23388
23416
|
parts.droppedPersistSources.push({ name, modelPath });
|
|
23389
23417
|
}
|
|
23390
|
-
if (
|
|
23418
|
+
if (planned.length === 0)
|
|
23391
23419
|
return;
|
|
23392
|
-
parts.
|
|
23420
|
+
parts.builds.push(...sourceBuildsOf(targets));
|
|
23393
23421
|
let classificationCtx;
|
|
23394
|
-
for (const
|
|
23422
|
+
for (const source of planned) {
|
|
23423
|
+
const sourceID = source.sourceID;
|
|
23395
23424
|
parts.sources[sourceID] = source;
|
|
23396
23425
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
23397
23426
|
if (!classificationCtx) {
|
|
@@ -23404,28 +23433,30 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
23404
23433
|
parts.sourceGateOutcomes[sourceID] = await classifyPersistSourceGate(source, classificationCtx.modelDef, materializer, classificationCtx.deps, modelPath);
|
|
23405
23434
|
}
|
|
23406
23435
|
}
|
|
23407
|
-
async function resolveConnectionDigests(connections,
|
|
23436
|
+
async function resolveConnectionDigests(connections, builds, onSkipped) {
|
|
23408
23437
|
const connectionDigests = {};
|
|
23409
|
-
for (const
|
|
23410
|
-
const conn = connections.get(
|
|
23438
|
+
for (const build of builds) {
|
|
23439
|
+
const conn = connections.get(build.connectionName);
|
|
23411
23440
|
if (!conn) {
|
|
23412
|
-
onSkipped(
|
|
23441
|
+
onSkipped(build.connectionName);
|
|
23413
23442
|
continue;
|
|
23414
23443
|
}
|
|
23415
|
-
if (!connectionDigests[
|
|
23416
|
-
connectionDigests[
|
|
23444
|
+
if (!connectionDigests[build.connectionName]) {
|
|
23445
|
+
connectionDigests[build.connectionName] = await conn.getDigest();
|
|
23417
23446
|
}
|
|
23418
23447
|
}
|
|
23419
23448
|
return connectionDigests;
|
|
23420
23449
|
}
|
|
23421
|
-
function deriveBuildPlan(
|
|
23450
|
+
function deriveBuildPlan(builds, sources, connectionDigests, sourceNames, sourceModelPaths, packageMaterialization, options) {
|
|
23422
23451
|
const include = sourceNames ? new Set(sourceNames) : null;
|
|
23423
|
-
const wireGraphs =
|
|
23424
|
-
connectionName:
|
|
23425
|
-
nodes:
|
|
23426
|
-
sourceID
|
|
23427
|
-
|
|
23428
|
-
|
|
23452
|
+
const wireGraphs = builds.map((build) => ({
|
|
23453
|
+
connectionName: build.connectionName,
|
|
23454
|
+
nodes: [
|
|
23455
|
+
build.order.map((sourceID) => ({
|
|
23456
|
+
sourceID,
|
|
23457
|
+
dependsOn: build.dependsOn[sourceID] ?? []
|
|
23458
|
+
}))
|
|
23459
|
+
]
|
|
23429
23460
|
}));
|
|
23430
23461
|
const wireSources = {};
|
|
23431
23462
|
const refusedSources = {};
|
|
@@ -23514,7 +23545,7 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames, source
|
|
|
23514
23545
|
function deriveBuildPlanOutcome(compiled, materializationConfig) {
|
|
23515
23546
|
const droppedPersistSources = compiled.droppedPersistSources ?? [];
|
|
23516
23547
|
const incrementalDeclarations = collectIncrementalDeclarations(compiled.sources);
|
|
23517
|
-
const plan = compiled.
|
|
23548
|
+
const plan = compiled.builds.length === 0 ? null : deriveBuildPlan(compiled.builds, compiled.sources, compiled.connectionDigests, undefined, compiled.sourceModelPaths, materializationConfig, {
|
|
23518
23549
|
preaggregatePlans: compiled.preaggregatePlans,
|
|
23519
23550
|
sourceGateOutcomes: compiled.sourceGateOutcomes
|
|
23520
23551
|
});
|
|
@@ -24474,6 +24505,7 @@ async function compileMalloyModel(job, malloyConfig, modelPath, load) {
|
|
|
24474
24505
|
modelPath,
|
|
24475
24506
|
materializer: mm,
|
|
24476
24507
|
model: compiled,
|
|
24508
|
+
runtime,
|
|
24477
24509
|
importBaseURL,
|
|
24478
24510
|
queries,
|
|
24479
24511
|
sources
|
|
@@ -24559,6 +24591,7 @@ async function prepareForMainThread(job, malloyConfig, load, model) {
|
|
|
24559
24591
|
packagePath: job.packagePath,
|
|
24560
24592
|
materializer,
|
|
24561
24593
|
malloyModel: model.model,
|
|
24594
|
+
runtime: model.runtime,
|
|
24562
24595
|
getRuntime: companionRuntime,
|
|
24563
24596
|
synthesized: companion
|
|
24564
24597
|
});
|
|
@@ -24798,8 +24831,8 @@ async function finishWorkerBuildPlan(malloyConfig, modelPaths, load, materializa
|
|
|
24798
24831
|
}
|
|
24799
24832
|
const connections = await resolvePackageConnections({
|
|
24800
24833
|
getMalloyConnection: (name) => malloyConfig.connections.lookupConnection(name)
|
|
24801
|
-
}, parts.
|
|
24802
|
-
const connectionDigests = await resolveConnectionDigests(connections, parts.
|
|
24834
|
+
}, parts.builds.map((b) => b.connectionName));
|
|
24835
|
+
const connectionDigests = await resolveConnectionDigests(connections, parts.builds, (name) => digestSkipped.push(name));
|
|
24803
24836
|
const outcome = deriveBuildPlanOutcome({ ...parts, connectionDigests }, materializationConfig);
|
|
24804
24837
|
return {
|
|
24805
24838
|
ok: true,
|
package/dist/server.mjs
CHANGED
|
@@ -164832,7 +164832,7 @@ var init_telemetry = __esm(() => {
|
|
|
164832
164832
|
});
|
|
164833
164833
|
|
|
164834
164834
|
// package.json
|
|
164835
|
-
var version = "0.10.
|
|
164835
|
+
var version = "0.10.5";
|
|
164836
164836
|
var init_package = () => {};
|
|
164837
164837
|
|
|
164838
164838
|
// src/version.ts
|
|
@@ -268349,7 +268349,7 @@ var require_bcrypt_pbkdf = __commonJS((exports, module) => {
|
|
|
268349
268349
|
|
|
268350
268350
|
// ../../node_modules/cpu-features/build/Release/cpufeatures.node
|
|
268351
268351
|
var require_cpufeatures = __commonJS((exports, module) => {
|
|
268352
|
-
module.exports = __require("./cpufeatures-
|
|
268352
|
+
module.exports = __require("./cpufeatures-gknxfsk5.node");
|
|
268353
268353
|
});
|
|
268354
268354
|
|
|
268355
268355
|
// ../../node_modules/cpu-features/lib/index.js
|
|
@@ -268960,6 +268960,11 @@ var require_utils93 = __commonJS((exports, module) => {
|
|
|
268960
268960
|
};
|
|
268961
268961
|
});
|
|
268962
268962
|
|
|
268963
|
+
// ../../node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
|
|
268964
|
+
var require_sshcrypto = __commonJS((exports, module) => {
|
|
268965
|
+
module.exports = __require("./sshcrypto-a4xyg5e8.node");
|
|
268966
|
+
});
|
|
268967
|
+
|
|
268963
268968
|
// ../../node_modules/ssh2/lib/protocol/crypto/poly1305.js
|
|
268964
268969
|
var require_poly1305 = __commonJS((exports, module) => {
|
|
268965
268970
|
var __dirname = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto", __filename = "/home/runner/work/publisher/publisher/node_modules/ssh2/lib/protocol/crypto/poly1305.js";
|
|
@@ -269446,7 +269451,7 @@ var require_crypto = __commonJS((exports, module) => {
|
|
|
269446
269451
|
var ChaChaPolyDecipher;
|
|
269447
269452
|
var GenericDecipher;
|
|
269448
269453
|
try {
|
|
269449
|
-
binding = (
|
|
269454
|
+
binding = require_sshcrypto();
|
|
269450
269455
|
({
|
|
269451
269456
|
AESGCMCipher,
|
|
269452
269457
|
ChaChaPolyCipher,
|
|
@@ -283780,7 +283785,7 @@ function validateMotherDuckShape(connection) {
|
|
|
283780
283785
|
throw new Error("MotherDuck access token is required.");
|
|
283781
283786
|
}
|
|
283782
283787
|
}
|
|
283783
|
-
function validateDuckLakeShape(connection) {
|
|
283788
|
+
function validateDuckLakeShape(connection, origin2) {
|
|
283784
283789
|
if (!connection.ducklakeConnection) {
|
|
283785
283790
|
throw new Error("DuckLake connection configuration is missing.");
|
|
283786
283791
|
}
|
|
@@ -283789,7 +283794,7 @@ function validateDuckLakeShape(connection) {
|
|
|
283789
283794
|
}
|
|
283790
283795
|
const storage = connection.ducklakeConnection.storage;
|
|
283791
283796
|
if (storage?.s3Connection) {
|
|
283792
|
-
validateS3ProviderShape(storage.s3Connection, connection.name);
|
|
283797
|
+
validateS3ProviderShape(storage.s3Connection, connection.name, origin2);
|
|
283793
283798
|
}
|
|
283794
283799
|
if (connection.ducklakeConnection.catalog.metadataSchema !== undefined) {
|
|
283795
283800
|
const schema = connection.ducklakeConnection.catalog.metadataSchema;
|
|
@@ -283866,7 +283871,7 @@ function validatePublisherShape(connection) {
|
|
|
283866
283871
|
throw new Error(`Invalid publisher connection '${connection.name}': connectionUri must use http or https (got '${parsedUri.protocol}'). ` + `Fix: set connectionUri to an absolute https URL like "https://…/connections/${connection.name}".`);
|
|
283867
283872
|
}
|
|
283868
283873
|
}
|
|
283869
|
-
function validateTypeShape(connection) {
|
|
283874
|
+
function validateTypeShape(connection, origin2) {
|
|
283870
283875
|
switch (connection.type) {
|
|
283871
283876
|
case "sqlserver":
|
|
283872
283877
|
return validateSqlServerShape(connection);
|
|
@@ -283877,7 +283882,7 @@ function validateTypeShape(connection) {
|
|
|
283877
283882
|
case "motherduck":
|
|
283878
283883
|
return validateMotherDuckShape(connection);
|
|
283879
283884
|
case "ducklake":
|
|
283880
|
-
return validateDuckLakeShape(connection);
|
|
283885
|
+
return validateDuckLakeShape(connection, origin2);
|
|
283881
283886
|
case "trino":
|
|
283882
283887
|
return validateTrinoShape(connection);
|
|
283883
283888
|
case "databricks":
|
|
@@ -283888,7 +283893,7 @@ function validateTypeShape(connection) {
|
|
|
283888
283893
|
return validatePublisherShape(connection);
|
|
283889
283894
|
}
|
|
283890
283895
|
}
|
|
283891
|
-
function validateConnectionShape(connection) {
|
|
283896
|
+
function validateConnectionShape(connection, origin2 = "connection") {
|
|
283892
283897
|
if (connection.proxy) {
|
|
283893
283898
|
validateProxyShape(connection, connection.proxy);
|
|
283894
283899
|
}
|
|
@@ -283901,7 +283906,7 @@ function validateConnectionShape(connection) {
|
|
|
283901
283906
|
if (connection.postgresConnection) {
|
|
283902
283907
|
postgresStatementTimeoutMs(connection.name ?? "", connection.postgresConnection);
|
|
283903
283908
|
}
|
|
283904
|
-
validateTypeShape(connection);
|
|
283909
|
+
validateTypeShape(connection, origin2);
|
|
283905
283910
|
}
|
|
283906
283911
|
function storageDestinationRoot(environmentPath) {
|
|
283907
283912
|
return path4.join(environmentPath, STORAGE_DESTINATIONS_DIR);
|
|
@@ -283950,7 +283955,7 @@ function validateStorageDestinations(destinations = []) {
|
|
|
283950
283955
|
continue;
|
|
283951
283956
|
}
|
|
283952
283957
|
try {
|
|
283953
|
-
validateConnectionShape(destination);
|
|
283958
|
+
validateConnectionShape(destination, "storage-destination");
|
|
283954
283959
|
const storage = destination.ducklakeConnection?.storage;
|
|
283955
283960
|
if (storage?.s3Connection) {
|
|
283956
283961
|
resolveCloudStorageCredentials({
|
|
@@ -284168,7 +284173,7 @@ function buildCoreEntry(connection, environmentPath) {
|
|
|
284168
284173
|
throw new Error(`Unsupported connection type: ${connection.type}`);
|
|
284169
284174
|
}
|
|
284170
284175
|
}
|
|
284171
|
-
function assembleEnvironmentConnections(connections = [], environmentPath = "") {
|
|
284176
|
+
function assembleEnvironmentConnections(connections = [], environmentPath = "", origin2 = "connection") {
|
|
284172
284177
|
const pojo = { connections: {} };
|
|
284173
284178
|
const metadata = new Map;
|
|
284174
284179
|
const apiConnections = [];
|
|
@@ -284185,7 +284190,7 @@ function assembleEnvironmentConnections(connections = [], environmentPath = "")
|
|
|
284185
284190
|
}
|
|
284186
284191
|
processedConnections.add(connection.name);
|
|
284187
284192
|
validateDuckdbApiSurface(connection);
|
|
284188
|
-
validateConnectionShape(connection);
|
|
284193
|
+
validateConnectionShape(connection, origin2);
|
|
284189
284194
|
warnOnConnectionQueryMetadata(connection);
|
|
284190
284195
|
const apiConnection = cloneApiConnection(connection);
|
|
284191
284196
|
apiConnection.attributes = getStaticConnectionAttributes(connection.type);
|
|
@@ -295166,17 +295171,19 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295166
295171
|
const hasS3 = !!ducklakeConfig.storage?.s3Connection;
|
|
295167
295172
|
const hasGCS = !!ducklakeConfig.storage?.gcsConnection;
|
|
295168
295173
|
if (hasS3) {
|
|
295169
|
-
await
|
|
295170
|
-
|
|
295171
|
-
|
|
295172
|
-
|
|
295173
|
-
|
|
295174
|
-
|
|
295175
|
-
|
|
295176
|
-
|
|
295177
|
-
|
|
295178
|
-
|
|
295179
|
-
|
|
295174
|
+
await connection.runSQL("SET GLOBAL enable_global_s3_configuration=false");
|
|
295175
|
+
}
|
|
295176
|
+
const storage = hasS3 ? {
|
|
295177
|
+
name: `${dbName}_storage`,
|
|
295178
|
+
type: "s3",
|
|
295179
|
+
s3Connection: ducklakeConfig.storage.s3Connection
|
|
295180
|
+
} : hasGCS ? {
|
|
295181
|
+
name: `${dbName}_storage`,
|
|
295182
|
+
type: "gcs",
|
|
295183
|
+
gcsConnection: ducklakeConfig.storage.gcsConnection
|
|
295184
|
+
} : undefined;
|
|
295185
|
+
if (storage) {
|
|
295186
|
+
resolveCloudStorageCredentials(storage, options.origin ?? "connection");
|
|
295180
295187
|
}
|
|
295181
295188
|
const pg = ducklakeConfig.catalog.postgresConnection;
|
|
295182
295189
|
const pgConnString = buildPgConnectionString(pg, {
|
|
@@ -295189,8 +295196,8 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295189
295196
|
logger.debug(`Final escaped connection string: ${redactPgSecrets(escapedPgConnString)}`);
|
|
295190
295197
|
const dataPath = ducklakeConfig.storage?.bucketUrl;
|
|
295191
295198
|
const metadataSchema = ducklakeConfig.catalog.metadataSchema;
|
|
295192
|
-
const
|
|
295193
|
-
if (
|
|
295199
|
+
const preflight = await preflightDuckLakeCatalogFormat(connection, dbName, pgConnString, metadataSchema);
|
|
295200
|
+
if (preflight === "absent") {
|
|
295194
295201
|
if (options.skipIfAbsent) {
|
|
295195
295202
|
return false;
|
|
295196
295203
|
}
|
|
@@ -295215,14 +295222,44 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295215
295222
|
throw error;
|
|
295216
295223
|
}
|
|
295217
295224
|
}
|
|
295225
|
+
if (storage) {
|
|
295226
|
+
try {
|
|
295227
|
+
const scope = await attachedDuckLakeScope(connection, dbName, {
|
|
295228
|
+
configuredDataPath: dataPath,
|
|
295229
|
+
metadataSchema,
|
|
295230
|
+
readOnly: options.readOnly,
|
|
295231
|
+
managed: options.managed === true && options.origin !== "storage-destination"
|
|
295232
|
+
});
|
|
295233
|
+
if (!scope) {
|
|
295234
|
+
throw new Error(`No storage secret could be scoped for DuckLake catalog '${dbName}': ` + `the config states no data path, and the catalog records no data ` + `path (or, for a managed view, no path for its main schema) to ` + `scope it to. The catalog was not attached.`);
|
|
295235
|
+
}
|
|
295236
|
+
await attachCloudStorage(connection, storage, options.origin, scope);
|
|
295237
|
+
} catch (error) {
|
|
295238
|
+
await connection.runSQL(`DETACH ${quoteIdentifier(dbName, "duckdb")};`).catch(() => {});
|
|
295239
|
+
throw error;
|
|
295240
|
+
}
|
|
295241
|
+
}
|
|
295218
295242
|
return true;
|
|
295219
295243
|
}
|
|
295220
|
-
async function
|
|
295221
|
-
const
|
|
295244
|
+
async function attachedDuckLakeScope(connection, dbName, input) {
|
|
295245
|
+
const recordedDataPath = input.configuredDataPath ? undefined : (await recordedCatalogSettings(connection, dbName, input.metadataSchema)).data_path;
|
|
295246
|
+
const mainSchemaPath = input.managed && input.readOnly ? await recordedSchemaPath(connection, dbName, input.metadataSchema, "main") : undefined;
|
|
295247
|
+
return storageSecretScope({
|
|
295248
|
+
configuredDataPath: input.configuredDataPath,
|
|
295249
|
+
recordedDataPath,
|
|
295250
|
+
mainSchemaPath,
|
|
295251
|
+
readOnly: input.readOnly,
|
|
295252
|
+
managed: input.managed
|
|
295253
|
+
});
|
|
295254
|
+
}
|
|
295255
|
+
function metadataCatalogRef(alias, metadataSchema) {
|
|
295256
|
+
return [
|
|
295222
295257
|
`__ducklake_metadata_${alias}`,
|
|
295223
295258
|
...metadataSchema ? [metadataSchema] : []
|
|
295224
295259
|
].map((part) => quoteIdentifier(part, "duckdb")).join(".");
|
|
295225
|
-
|
|
295260
|
+
}
|
|
295261
|
+
async function recordedCatalogSettings(connection, alias, metadataSchema) {
|
|
295262
|
+
const rows = runSQLRows(await connection.runSQL(`SELECT key, value FROM ${metadataCatalogRef(alias, metadataSchema)}.ducklake_metadata WHERE scope IS NULL`));
|
|
295226
295263
|
const settings = {};
|
|
295227
295264
|
for (const row of rows) {
|
|
295228
295265
|
if (typeof row.key === "string" && row.value != null) {
|
|
@@ -295231,6 +295268,43 @@ async function recordedCatalogSettings(connection, alias, metadataSchema) {
|
|
|
295231
295268
|
}
|
|
295232
295269
|
return settings;
|
|
295233
295270
|
}
|
|
295271
|
+
async function recordedSchemaPath(connection, alias, metadataSchema, schemaName) {
|
|
295272
|
+
const row = runSQLRows(await connection.runSQL(`SELECT path, path_is_relative FROM ${metadataCatalogRef(alias, metadataSchema)}.ducklake_schema ` + `WHERE schema_name = '${escapeSQL(schemaName)}' AND end_snapshot IS NULL LIMIT 1`))[0];
|
|
295273
|
+
if (!row || typeof row.path !== "string")
|
|
295274
|
+
return;
|
|
295275
|
+
return { path: row.path, relative: row.path_is_relative !== false };
|
|
295276
|
+
}
|
|
295277
|
+
function stripTrailingSlashes(url3) {
|
|
295278
|
+
let end = url3.length;
|
|
295279
|
+
while (end > 0 && url3[end - 1] === "/")
|
|
295280
|
+
end--;
|
|
295281
|
+
return url3.slice(0, end);
|
|
295282
|
+
}
|
|
295283
|
+
function oneTrailingSlash(url3) {
|
|
295284
|
+
return `${stripTrailingSlashes(url3)}/`;
|
|
295285
|
+
}
|
|
295286
|
+
function isBucketlessUrl(url3) {
|
|
295287
|
+
const at = url3.indexOf("://");
|
|
295288
|
+
return at >= 0 && stripTrailingSlashes(url3.slice(at + 3)) === "";
|
|
295289
|
+
}
|
|
295290
|
+
function storageSecretScope(input) {
|
|
295291
|
+
const root = input.configuredDataPath || input.recordedDataPath;
|
|
295292
|
+
if (!root || isBucketlessUrl(root))
|
|
295293
|
+
return;
|
|
295294
|
+
if (input.managed && input.readOnly) {
|
|
295295
|
+
const main = input.mainSchemaPath;
|
|
295296
|
+
if (!main)
|
|
295297
|
+
return;
|
|
295298
|
+
if (!main.relative) {
|
|
295299
|
+
return isBucketlessUrl(main.path) ? undefined : oneTrailingSlash(main.path);
|
|
295300
|
+
}
|
|
295301
|
+
let start = 0;
|
|
295302
|
+
while (start < main.path.length && main.path[start] === "/")
|
|
295303
|
+
start++;
|
|
295304
|
+
return oneTrailingSlash(`${oneTrailingSlash(root)}${main.path.slice(start)}`);
|
|
295305
|
+
}
|
|
295306
|
+
return oneTrailingSlash(root);
|
|
295307
|
+
}
|
|
295234
295308
|
function dataPathClauses(dataPath, readOnly) {
|
|
295235
295309
|
if (dataPath) {
|
|
295236
295310
|
return `DATA_PATH '${escapeSQL(dataPath)}', OVERRIDE_DATA_PATH true`;
|
|
@@ -295250,13 +295324,16 @@ function firstAttachClauses(options) {
|
|
|
295250
295324
|
async function attachDuckLakeForCreate(connection, dbName, ducklakeConfig, settings) {
|
|
295251
295325
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295252
295326
|
readOnly: false,
|
|
295327
|
+
origin: "storage-destination",
|
|
295253
295328
|
encrypted: settings.encrypted,
|
|
295254
295329
|
dataInliningRowLimit: settings.dataInliningRowLimit
|
|
295255
295330
|
});
|
|
295256
295331
|
}
|
|
295257
|
-
async function attachDuckLake(connection, dbName, ducklakeConfig) {
|
|
295332
|
+
async function attachDuckLake(connection, dbName, ducklakeConfig, options = {}) {
|
|
295258
295333
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295259
|
-
readOnly: ducklakeConfig.readOnly !== false
|
|
295334
|
+
readOnly: ducklakeConfig.readOnly !== false,
|
|
295335
|
+
origin: options.origin,
|
|
295336
|
+
managed: options.managed
|
|
295260
295337
|
});
|
|
295261
295338
|
}
|
|
295262
295339
|
function managedSearchPathSql(dbName) {
|
|
@@ -295267,17 +295344,21 @@ async function setManagedSearchPath(connection, dbName) {
|
|
|
295267
295344
|
await connection.runSQL(managedSearchPathSql(dbName));
|
|
295268
295345
|
}
|
|
295269
295346
|
async function attachDuckLakeReadOnly(connection, dbName, ducklakeConfig) {
|
|
295270
|
-
await attachDuckLake(connection, dbName, ducklakeConfig
|
|
295347
|
+
await attachDuckLake(connection, dbName, ducklakeConfig, {
|
|
295348
|
+
origin: "storage-destination"
|
|
295349
|
+
});
|
|
295271
295350
|
}
|
|
295272
295351
|
async function attachDuckLakeReadOnlyIfPresent(connection, dbName, ducklakeConfig) {
|
|
295273
295352
|
return attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295274
295353
|
readOnly: true,
|
|
295275
|
-
skipIfAbsent: true
|
|
295354
|
+
skipIfAbsent: true,
|
|
295355
|
+
origin: "storage-destination"
|
|
295276
295356
|
});
|
|
295277
295357
|
}
|
|
295278
295358
|
async function attachDuckLakeReadWrite(connection, dbName, ducklakeConfig) {
|
|
295279
295359
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295280
|
-
readOnly: false
|
|
295360
|
+
readOnly: false,
|
|
295361
|
+
origin: "storage-destination"
|
|
295281
295362
|
});
|
|
295282
295363
|
}
|
|
295283
295364
|
async function federateSourceForPassthrough(connection, sourceType, config, deps = {}) {
|
|
@@ -295481,13 +295562,15 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295481
295562
|
const isGCS = credentials.type === "gcs";
|
|
295482
295563
|
const escapedKeyId = escapeSQL(credentials.accessKeyId);
|
|
295483
295564
|
const escapedSecret = escapeSQL(credentials.secretAccessKey);
|
|
295565
|
+
const scopeClause = credentials.scope ? `SCOPE '${escapeSQL(credentials.scope)}'` : "";
|
|
295484
295566
|
let createSecretCommand;
|
|
295485
295567
|
if (isGCS) {
|
|
295486
295568
|
createSecretCommand = `
|
|
295487
295569
|
CREATE OR REPLACE SECRET ${secretName} (
|
|
295488
295570
|
TYPE gcs,
|
|
295489
295571
|
KEY_ID '${escapedKeyId}',
|
|
295490
|
-
SECRET '${escapedSecret}'
|
|
295572
|
+
SECRET '${escapedSecret}'${scopeClause ? `,
|
|
295573
|
+
${scopeClause}` : ""}
|
|
295491
295574
|
);
|
|
295492
295575
|
`;
|
|
295493
295576
|
} else {
|
|
@@ -295523,6 +295606,9 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295523
295606
|
if (credentials.provider !== "credential_chain" && credentials.sessionToken) {
|
|
295524
295607
|
clauses.push(`SESSION_TOKEN '${escapeSQL(credentials.sessionToken)}'`);
|
|
295525
295608
|
}
|
|
295609
|
+
if (scopeClause) {
|
|
295610
|
+
clauses.push(scopeClause);
|
|
295611
|
+
}
|
|
295526
295612
|
createSecretCommand = `
|
|
295527
295613
|
CREATE OR REPLACE SECRET ${secretName} (
|
|
295528
295614
|
${clauses.join(`,
|
|
@@ -295532,9 +295618,12 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295532
295618
|
}
|
|
295533
295619
|
return createSecretCommand;
|
|
295534
295620
|
}
|
|
295535
|
-
async function attachCloudStorage(connection, attachedDb) {
|
|
295621
|
+
async function attachCloudStorage(connection, attachedDb, origin2 = "connection", scope) {
|
|
295536
295622
|
const storageType = attachedDb.type?.toUpperCase() || "";
|
|
295537
|
-
const credentials =
|
|
295623
|
+
const credentials = {
|
|
295624
|
+
...resolveCloudStorageCredentials(attachedDb, origin2),
|
|
295625
|
+
scope
|
|
295626
|
+
};
|
|
295538
295627
|
await installAndLoadExtension(connection, "httpfs");
|
|
295539
295628
|
if (credentials.provider === "credential_chain") {
|
|
295540
295629
|
await installAndLoadExtension(connection, "aws");
|
|
@@ -295826,8 +295915,8 @@ function applyConnectionFingerprint(connection, metadata) {
|
|
|
295826
295915
|
function isDuckDBConnection(connection) {
|
|
295827
295916
|
return connection instanceof DuckDBConnection;
|
|
295828
295917
|
}
|
|
295829
|
-
function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false) {
|
|
295830
|
-
const assembled = assembleEnvironmentConnections(connections, environmentPath);
|
|
295918
|
+
function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false, { origin: origin2 = "connection" } = {}) {
|
|
295919
|
+
const assembled = assembleEnvironmentConnections(connections, environmentPath, origin2);
|
|
295831
295920
|
const duckLakeCache = new Map;
|
|
295832
295921
|
const snowflakeJwtCache = new Map;
|
|
295833
295922
|
const azureDuckDBCache = new Map;
|
|
@@ -295866,7 +295955,10 @@ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", is
|
|
|
295866
295955
|
}
|
|
295867
295956
|
const connection2 = await connectionPromise;
|
|
295868
295957
|
if (isUpdateConnectionRequest || !await isDatabaseAttached(connection2, name)) {
|
|
295869
|
-
await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection
|
|
295958
|
+
await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection, {
|
|
295959
|
+
origin: origin2,
|
|
295960
|
+
managed: metadata.apiConnection.managed === true
|
|
295961
|
+
});
|
|
295870
295962
|
if (metadata.apiConnection.managed === true) {
|
|
295871
295963
|
await setManagedSearchPath(connection2, name);
|
|
295872
295964
|
}
|
|
@@ -348115,7 +348207,7 @@ async function compileSynthesizedPreaggregation(args) {
|
|
|
348115
348207
|
const { runtime, importBaseURL } = await args.getRuntime(new Map([[synthesizedURL.href, text]]));
|
|
348116
348208
|
const materializer = runtime.loadModel(synthesizedURL, { importBaseURL });
|
|
348117
348209
|
const model = await materializer.getModel();
|
|
348118
|
-
return { plans, model, materializer, text };
|
|
348210
|
+
return { plans, model, materializer, runtime, text };
|
|
348119
348211
|
}
|
|
348120
348212
|
async function tryCompileSynthesizedPreaggregation(args) {
|
|
348121
348213
|
try {
|
|
@@ -348302,27 +348394,47 @@ function composeDeclaredQueryMetadata(layers) {
|
|
|
348302
348394
|
const resolved = Object.assign({}, ...ordered);
|
|
348303
348395
|
return Object.keys(resolved).length > 0 ? resolved : null;
|
|
348304
348396
|
}
|
|
348305
|
-
function
|
|
348306
|
-
|
|
348307
|
-
}
|
|
348308
|
-
|
|
348309
|
-
|
|
348310
|
-
|
|
348311
|
-
|
|
348312
|
-
|
|
348313
|
-
for (const dep of node.dependsOn) {
|
|
348314
|
-
yield* visit(dep);
|
|
348397
|
+
function sourceBuildOf(build, unbuildable) {
|
|
348398
|
+
const order = [];
|
|
348399
|
+
const dependsOn = {};
|
|
348400
|
+
for (const target of build.targets) {
|
|
348401
|
+
const reads = target.dependsOn.flatMap((t2) => t2.sources.map((s2) => s2.sourceID));
|
|
348402
|
+
for (const source of target.sources) {
|
|
348403
|
+
order.push(source.sourceID);
|
|
348404
|
+
dependsOn[source.sourceID] = reads;
|
|
348315
348405
|
}
|
|
348316
|
-
|
|
348317
|
-
|
|
348406
|
+
}
|
|
348407
|
+
for (const { source } of unbuildable) {
|
|
348408
|
+
if (source.connectionName !== build.connectionName)
|
|
348409
|
+
continue;
|
|
348410
|
+
order.push(source.sourceID);
|
|
348411
|
+
dependsOn[source.sourceID] = [];
|
|
348412
|
+
}
|
|
348413
|
+
return { connectionName: build.connectionName, order, dependsOn };
|
|
348414
|
+
}
|
|
348415
|
+
function sourceBuildsOf(targets) {
|
|
348416
|
+
const builds = targets.connections.map((c) => sourceBuildOf(c, targets.unbuildable));
|
|
348417
|
+
const planned = new Set(builds.map((b) => b.connectionName));
|
|
348418
|
+
for (const { source } of targets.unbuildable) {
|
|
348419
|
+
if (planned.has(source.connectionName))
|
|
348420
|
+
continue;
|
|
348421
|
+
planned.add(source.connectionName);
|
|
348422
|
+
builds.push(sourceBuildOf({ connectionName: source.connectionName, targets: [] }, targets.unbuildable));
|
|
348423
|
+
}
|
|
348424
|
+
return builds;
|
|
348425
|
+
}
|
|
348426
|
+
function plannedSources(targets) {
|
|
348427
|
+
return [
|
|
348428
|
+
...targets.connections.flatMap((c) => c.targets.flatMap((t2) => t2.sources)),
|
|
348429
|
+
...targets.unbuildable.map((u) => u.source)
|
|
348430
|
+
];
|
|
348431
|
+
}
|
|
348432
|
+
function* sourcesInBuildOrder(build, sources) {
|
|
348433
|
+
for (const sourceID of build.order) {
|
|
348434
|
+
const source = sources[sourceID];
|
|
348318
348435
|
if (source)
|
|
348319
348436
|
yield source;
|
|
348320
348437
|
}
|
|
348321
|
-
for (const level of graph.nodes) {
|
|
348322
|
-
for (const node of level) {
|
|
348323
|
-
yield* visit(node);
|
|
348324
|
-
}
|
|
348325
|
-
}
|
|
348326
348438
|
}
|
|
348327
348439
|
function computeSourceEntityId(source, connectionDigests) {
|
|
348328
348440
|
return source.makeBuildId(connectionDigests[source.connectionName], addressPartitionLayout(source, source.getSQL()));
|
|
@@ -348411,7 +348523,7 @@ async function classifyPersistSourceGate(persistSource, modelDef, materializer,
|
|
|
348411
348523
|
}
|
|
348412
348524
|
function emptyBuildPlanParts() {
|
|
348413
348525
|
return {
|
|
348414
|
-
|
|
348526
|
+
builds: [],
|
|
348415
348527
|
sources: {},
|
|
348416
348528
|
sourceModelPaths: {},
|
|
348417
348529
|
droppedPersistSources: [],
|
|
@@ -348420,7 +348532,7 @@ function emptyBuildPlanParts() {
|
|
|
348420
348532
|
};
|
|
348421
348533
|
}
|
|
348422
348534
|
function mergeBuildPlanParts(into, from) {
|
|
348423
|
-
into.
|
|
348535
|
+
into.builds.push(...from.builds);
|
|
348424
348536
|
Object.assign(into.sources, from.sources);
|
|
348425
348537
|
Object.assign(into.sourceModelPaths, from.sourceModelPaths);
|
|
348426
348538
|
into.droppedPersistSources.push(...from.droppedPersistSources);
|
|
@@ -348428,7 +348540,14 @@ function mergeBuildPlanParts(into, from) {
|
|
|
348428
348540
|
Object.assign(into.sourceGateOutcomes, from.sourceGateOutcomes);
|
|
348429
348541
|
}
|
|
348430
348542
|
async function collectModelBuildPlan(parts, args) {
|
|
348431
|
-
const {
|
|
348543
|
+
const {
|
|
348544
|
+
modelPath,
|
|
348545
|
+
packagePath,
|
|
348546
|
+
materializer,
|
|
348547
|
+
malloyModel,
|
|
348548
|
+
runtime,
|
|
348549
|
+
getRuntime
|
|
348550
|
+
} = args;
|
|
348432
348551
|
const synthesized = args.synthesized !== undefined ? args.synthesized : await tryCompileSynthesizedPreaggregation({
|
|
348433
348552
|
packagePath,
|
|
348434
348553
|
modelPath,
|
|
@@ -348436,14 +348555,14 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348436
348555
|
contents: malloyModel._modelDef?.contents ?? {}
|
|
348437
348556
|
});
|
|
348438
348557
|
if (synthesized) {
|
|
348439
|
-
const
|
|
348440
|
-
|
|
348441
|
-
parts.graphs.push(...rollupPlan.graphs);
|
|
348558
|
+
const rollupTargets = await synthesized.runtime.getBuildTargets(synthesized.model);
|
|
348559
|
+
parts.builds.push(...sourceBuildsOf(rollupTargets));
|
|
348442
348560
|
const planByName = new Map(synthesized.plans.map((p) => [p.rollupSourceName, p]));
|
|
348443
348561
|
let rollupClassificationCtx;
|
|
348444
|
-
for (const
|
|
348445
|
-
if (!
|
|
348562
|
+
for (const source of plannedSources(rollupTargets)) {
|
|
348563
|
+
if (!planByName.has(source.name))
|
|
348446
348564
|
continue;
|
|
348565
|
+
const sourceID = source.sourceID;
|
|
348447
348566
|
parts.sources[sourceID] = source;
|
|
348448
348567
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
348449
348568
|
const rollup = planByName.get(source.name);
|
|
@@ -348462,23 +348581,25 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348462
348581
|
const modelTag = malloyModel.modelAnnotations.parseAsTag("!").tag;
|
|
348463
348582
|
if (!modelTag.has("experimental", "persistence"))
|
|
348464
348583
|
return;
|
|
348465
|
-
const
|
|
348466
|
-
for (const msg of
|
|
348584
|
+
const targets = await runtime.getBuildTargets(malloyModel);
|
|
348585
|
+
for (const msg of targets.tagParseLog) {
|
|
348467
348586
|
logger.warn("Persist annotation issue", {
|
|
348468
348587
|
modelPath,
|
|
348469
348588
|
message: msg.message,
|
|
348470
348589
|
severity: msg.severity
|
|
348471
348590
|
});
|
|
348472
348591
|
}
|
|
348473
|
-
const
|
|
348592
|
+
const planned = plannedSources(targets);
|
|
348593
|
+
const recognizedNames = new Set(planned.map((s2) => s2.name));
|
|
348474
348594
|
for (const name of detectDroppedPersistSources(malloyModel, recognizedNames)) {
|
|
348475
348595
|
parts.droppedPersistSources.push({ name, modelPath });
|
|
348476
348596
|
}
|
|
348477
|
-
if (
|
|
348597
|
+
if (planned.length === 0)
|
|
348478
348598
|
return;
|
|
348479
|
-
parts.
|
|
348599
|
+
parts.builds.push(...sourceBuildsOf(targets));
|
|
348480
348600
|
let classificationCtx;
|
|
348481
|
-
for (const
|
|
348601
|
+
for (const source of planned) {
|
|
348602
|
+
const sourceID = source.sourceID;
|
|
348482
348603
|
parts.sources[sourceID] = source;
|
|
348483
348604
|
parts.sourceModelPaths[sourceID] = modelPath;
|
|
348484
348605
|
if (!classificationCtx) {
|
|
@@ -348491,28 +348612,30 @@ async function collectModelBuildPlan(parts, args) {
|
|
|
348491
348612
|
parts.sourceGateOutcomes[sourceID] = await classifyPersistSourceGate(source, classificationCtx.modelDef, materializer, classificationCtx.deps, modelPath);
|
|
348492
348613
|
}
|
|
348493
348614
|
}
|
|
348494
|
-
async function resolveConnectionDigests(connections,
|
|
348615
|
+
async function resolveConnectionDigests(connections, builds, onSkipped) {
|
|
348495
348616
|
const connectionDigests = {};
|
|
348496
|
-
for (const
|
|
348497
|
-
const conn = connections.get(
|
|
348617
|
+
for (const build of builds) {
|
|
348618
|
+
const conn = connections.get(build.connectionName);
|
|
348498
348619
|
if (!conn) {
|
|
348499
|
-
onSkipped(
|
|
348620
|
+
onSkipped(build.connectionName);
|
|
348500
348621
|
continue;
|
|
348501
348622
|
}
|
|
348502
|
-
if (!connectionDigests[
|
|
348503
|
-
connectionDigests[
|
|
348623
|
+
if (!connectionDigests[build.connectionName]) {
|
|
348624
|
+
connectionDigests[build.connectionName] = await conn.getDigest();
|
|
348504
348625
|
}
|
|
348505
348626
|
}
|
|
348506
348627
|
return connectionDigests;
|
|
348507
348628
|
}
|
|
348508
|
-
function deriveBuildPlan(
|
|
348629
|
+
function deriveBuildPlan(builds, sources, connectionDigests, sourceNames, sourceModelPaths, packageMaterialization, options) {
|
|
348509
348630
|
const include = sourceNames ? new Set(sourceNames) : null;
|
|
348510
|
-
const wireGraphs =
|
|
348511
|
-
connectionName:
|
|
348512
|
-
nodes:
|
|
348513
|
-
sourceID
|
|
348514
|
-
|
|
348515
|
-
|
|
348631
|
+
const wireGraphs = builds.map((build) => ({
|
|
348632
|
+
connectionName: build.connectionName,
|
|
348633
|
+
nodes: [
|
|
348634
|
+
build.order.map((sourceID) => ({
|
|
348635
|
+
sourceID,
|
|
348636
|
+
dependsOn: build.dependsOn[sourceID] ?? []
|
|
348637
|
+
}))
|
|
348638
|
+
]
|
|
348516
348639
|
}));
|
|
348517
348640
|
const wireSources = {};
|
|
348518
348641
|
const refusedSources = {};
|
|
@@ -348601,7 +348724,7 @@ function deriveBuildPlan(graphs, sources, connectionDigests, sourceNames, source
|
|
|
348601
348724
|
function deriveBuildPlanOutcome(compiled, materializationConfig) {
|
|
348602
348725
|
const droppedPersistSources = compiled.droppedPersistSources ?? [];
|
|
348603
348726
|
const incrementalDeclarations = collectIncrementalDeclarations(compiled.sources);
|
|
348604
|
-
const plan = compiled.
|
|
348727
|
+
const plan = compiled.builds.length === 0 ? null : deriveBuildPlan(compiled.builds, compiled.sources, compiled.connectionDigests, undefined, compiled.sourceModelPaths, materializationConfig, {
|
|
348605
348728
|
preaggregatePlans: compiled.preaggregatePlans,
|
|
348606
348729
|
sourceGateOutcomes: compiled.sourceGateOutcomes
|
|
348607
348730
|
});
|
|
@@ -350136,14 +350259,14 @@ async function buildDownstreamIntoStorage(params) {
|
|
|
350136
350259
|
message: `Chained build model did not compile over the rebound parents: ${errMessage(err)}`
|
|
350137
350260
|
});
|
|
350138
350261
|
}
|
|
350139
|
-
const
|
|
350140
|
-
|
|
350141
|
-
|
|
350142
|
-
|
|
350143
|
-
|
|
350144
|
-
|
|
350145
|
-
}
|
|
350262
|
+
const targets = await runtime.getBuildTargets(model, { virtualMap });
|
|
350263
|
+
const unbuildable = targets.unbuildable.find((u) => u.source.name === downstreamName);
|
|
350264
|
+
if (unbuildable) {
|
|
350265
|
+
throw new MaterializationEligibilityError({
|
|
350266
|
+
message: `Chained build model could not plan '${downstreamName}' over ` + `the rebound parents: ${unbuildable.problem.message}`
|
|
350267
|
+
});
|
|
350146
350268
|
}
|
|
350269
|
+
const downstream = plannedSources(targets).find((ps) => ps.name === downstreamName);
|
|
350147
350270
|
if (!downstream) {
|
|
350148
350271
|
throw new MaterializationEligibilityError({
|
|
350149
350272
|
message: `Chained build model did not yield a persist source named ` + `'${downstreamName}' (the downstream references something the ` + `rebound parents don't provide).`
|
|
@@ -359860,6 +359983,98 @@ var require_recursive_readdir = __commonJS((exports, module) => {
|
|
|
359860
359983
|
module.exports = readdir3;
|
|
359861
359984
|
});
|
|
359862
359985
|
|
|
359986
|
+
// src/service/preaggregation_coverage.ts
|
|
359987
|
+
function rollupMeasuresByMember(plans) {
|
|
359988
|
+
return new Map(plans.map((p2) => [
|
|
359989
|
+
p2.rollupSourceName,
|
|
359990
|
+
new Set(p2.measures.map((m3) => m3.name))
|
|
359991
|
+
]));
|
|
359992
|
+
}
|
|
359993
|
+
function rollupCoverageViolation(query, rollupMeasures) {
|
|
359994
|
+
const ir2 = query;
|
|
359995
|
+
const member = ir2?.compositeResolvedSourceDef?.as;
|
|
359996
|
+
if (!member)
|
|
359997
|
+
return;
|
|
359998
|
+
const merged = rollupMeasures.get(member);
|
|
359999
|
+
if (!merged)
|
|
360000
|
+
return;
|
|
360001
|
+
const memberFields = new Map((ir2?.compositeResolvedSourceDef?.fields ?? []).map((f5) => [
|
|
360002
|
+
f5.as ?? f5.name,
|
|
360003
|
+
f5
|
|
360004
|
+
]));
|
|
360005
|
+
const followed = new Set;
|
|
360006
|
+
let violation;
|
|
360007
|
+
const visitRef = (path16) => {
|
|
360008
|
+
if (!path16)
|
|
360009
|
+
return;
|
|
360010
|
+
if (path16.length !== 1) {
|
|
360011
|
+
violation = `reference ${path16.join(".")} reaches past the rollup's own fields`;
|
|
360012
|
+
return;
|
|
360013
|
+
}
|
|
360014
|
+
const name = path16[0];
|
|
360015
|
+
if (merged.has(name) || followed.has(name))
|
|
360016
|
+
return;
|
|
360017
|
+
followed.add(name);
|
|
360018
|
+
visitExpr(memberFields.get(name)?.e);
|
|
360019
|
+
};
|
|
360020
|
+
const visitExpr = (e2) => {
|
|
360021
|
+
if (violation || !e2 || typeof e2 !== "object")
|
|
360022
|
+
return;
|
|
360023
|
+
if (Array.isArray(e2)) {
|
|
360024
|
+
for (const item of e2)
|
|
360025
|
+
visitExpr(item);
|
|
360026
|
+
return;
|
|
360027
|
+
}
|
|
360028
|
+
const n = e2;
|
|
360029
|
+
if (n.node === "aggregate") {
|
|
360030
|
+
violation = `aggregate ${String(n.function)}() is computed over the rollup's rows, not from a stored partial`;
|
|
360031
|
+
return;
|
|
360032
|
+
}
|
|
360033
|
+
if (n.node === "function_call" && n.overload?.returnType?.expressionType === "aggregate") {
|
|
360034
|
+
violation = `aggregate function ${String(n.name)}() is computed over the rollup's rows, not from a stored partial`;
|
|
360035
|
+
return;
|
|
360036
|
+
}
|
|
360037
|
+
if (n.node === "field") {
|
|
360038
|
+
visitRef(n.path);
|
|
360039
|
+
return;
|
|
360040
|
+
}
|
|
360041
|
+
for (const [key, value] of Object.entries(n)) {
|
|
360042
|
+
if (key === "overload" || key === "at")
|
|
360043
|
+
continue;
|
|
360044
|
+
visitExpr(value);
|
|
360045
|
+
}
|
|
360046
|
+
};
|
|
360047
|
+
const visitStage = (stage) => {
|
|
360048
|
+
if (violation)
|
|
360049
|
+
return;
|
|
360050
|
+
if (stage?.type === "project" || stage?.type === "index") {
|
|
360051
|
+
violation = `${stage.type === "project" ? "a select:" : "an index:"} stage over the rollup returns one row per grain group, not the source's rows`;
|
|
360052
|
+
return;
|
|
360053
|
+
}
|
|
360054
|
+
for (const field of stage?.queryFields ?? []) {
|
|
360055
|
+
if (field.type === "fieldref")
|
|
360056
|
+
visitRef(field.path);
|
|
360057
|
+
else if (field.type === "turtle")
|
|
360058
|
+
visitStage(field.pipeline?.[0]);
|
|
360059
|
+
else
|
|
360060
|
+
visitExpr(field.e);
|
|
360061
|
+
}
|
|
360062
|
+
for (const filter2 of stage?.filterList ?? [])
|
|
360063
|
+
visitExpr(filter2.e);
|
|
360064
|
+
};
|
|
360065
|
+
visitStage(ir2?.pipeline?.[0]);
|
|
360066
|
+
return violation ? `${member}: ${violation}` : undefined;
|
|
360067
|
+
}
|
|
360068
|
+
var RollupCoverageError;
|
|
360069
|
+
var init_preaggregation_coverage = __esm(() => {
|
|
360070
|
+
RollupCoverageError = class RollupCoverageError extends Error {
|
|
360071
|
+
constructor(message) {
|
|
360072
|
+
super(message);
|
|
360073
|
+
this.name = "RollupCoverageError";
|
|
360074
|
+
}
|
|
360075
|
+
};
|
|
360076
|
+
});
|
|
360077
|
+
|
|
359863
360078
|
// src/service/render_tag_targets.ts
|
|
359864
360079
|
function renderTagTargets(queries, sources) {
|
|
359865
360080
|
const targets = [];
|
|
@@ -363064,6 +363279,11 @@ class StorageServeShape {
|
|
|
363064
363279
|
await this.host.ensureSharedQueryModel(this.serveShapeCache.materializer);
|
|
363065
363280
|
const runnable = this.serveShapeCache.materializer.loadRestrictedQuery(queryString);
|
|
363066
363281
|
const probeSQL = await runnable.getSQL({ virtualMap, givens });
|
|
363282
|
+
if (groups.length > 0) {
|
|
363283
|
+
const uncovered = rollupCoverageViolation((await runnable.getPreparedQuery())._query, rollupMeasuresByMember(this.host.rollupPlans()));
|
|
363284
|
+
if (uncovered)
|
|
363285
|
+
throw new RollupCoverageError(uncovered);
|
|
363286
|
+
}
|
|
363067
363287
|
const rollupPaths = [
|
|
363068
363288
|
...buildVirtualMap(groups.flatMap((g2) => g2.members))
|
|
363069
363289
|
].flatMap(([, byHandle]) => [...byHandle.values()]);
|
|
@@ -363264,6 +363484,7 @@ var init_model_serve_shape = __esm(() => {
|
|
|
363264
363484
|
init_materialization_metrics();
|
|
363265
363485
|
init_connection();
|
|
363266
363486
|
init_materialization_serve_transform();
|
|
363487
|
+
init_preaggregation_coverage();
|
|
363267
363488
|
init_preaggregation_serve_bindings();
|
|
363268
363489
|
staleBindingsWithheld = lazyCounter("publisher_storage_bindings_withheld_total", "Storage serve bindings withheld from a routed query because the freshness gate found them stale; each one is read live instead. Until now staleness was visible only as a later live read.");
|
|
363269
363490
|
});
|
|
@@ -363306,6 +363527,7 @@ var init_model = __esm(() => {
|
|
|
363306
363527
|
init_config();
|
|
363307
363528
|
init_preaggregation_synthesis();
|
|
363308
363529
|
init_preaggregation_compile();
|
|
363530
|
+
init_preaggregation_coverage();
|
|
363309
363531
|
init_render_tag_targets();
|
|
363310
363532
|
init_model_info();
|
|
363311
363533
|
init_logger();
|
|
@@ -365487,13 +365709,17 @@ run: ${sourceName ? `${quoteMalloyIdentifier2(sourceName)} -> ` : ""}${quoteMall
|
|
|
365487
365709
|
});
|
|
365488
365710
|
} catch (shapeErr) {
|
|
365489
365711
|
recordStorageServeRouting("live_fallback");
|
|
365490
|
-
|
|
365491
|
-
|
|
365492
|
-
|
|
365493
|
-
|
|
365494
|
-
|
|
365495
|
-
|
|
365496
|
-
|
|
365712
|
+
if (shapeErr instanceof RollupCoverageError) {
|
|
365713
|
+
logger.debug("storage rollup cannot answer this query; serving live", { modelPath: this.modelPath, error: shapeErr.message });
|
|
365714
|
+
} else {
|
|
365715
|
+
const { shapeSources, staleSources } = serveShapeDiagnostics(this.storageServe.serveBindings, this.storageServe.freshServeBindings(Date.now()));
|
|
365716
|
+
logger.info("storage serve-shape ineligible for this query; serving live", {
|
|
365717
|
+
modelPath: this.modelPath,
|
|
365718
|
+
error: shapeErr instanceof Error ? shapeErr.message : String(shapeErr),
|
|
365719
|
+
shapeSources,
|
|
365720
|
+
staleSources
|
|
365721
|
+
});
|
|
365722
|
+
}
|
|
365497
365723
|
}
|
|
365498
365724
|
}
|
|
365499
365725
|
const everyRollupIsStorageBound = this.preaggregateRollupPlans.length > 0 && this.preaggregateRollupPlans.every((plan) => !!plan.storage);
|
|
@@ -365504,10 +365730,13 @@ run: ${sourceName ? `${quoteMalloyIdentifier2(sourceName)} -> ` : ""}${quoteMall
|
|
|
365504
365730
|
givens: querySurfaceGivens,
|
|
365505
365731
|
buildManifest
|
|
365506
365732
|
});
|
|
365733
|
+
const uncovered = rollupCoverageViolation((await candidate.getPreparedQuery())._query, rollupMeasuresByMember(this.preaggregateRollupPlans));
|
|
365734
|
+
if (uncovered)
|
|
365735
|
+
throw new RollupCoverageError(uncovered);
|
|
365507
365736
|
runnable = candidate;
|
|
365508
365737
|
preaggRouted = true;
|
|
365509
365738
|
} catch (preaggErr) {
|
|
365510
|
-
logger.debug("query
|
|
365739
|
+
logger.debug("query cannot be served from the pre-aggregation model; serving live", {
|
|
365511
365740
|
modelPath: this.modelPath,
|
|
365512
365741
|
error: preaggErr instanceof Error ? preaggErr.message : String(preaggErr)
|
|
365513
365742
|
});
|
|
@@ -366140,18 +366369,19 @@ async function compilePackageBuildPlan(pkg, signal) {
|
|
|
366140
366369
|
packagePath: pkg.getPackagePath(),
|
|
366141
366370
|
materializer,
|
|
366142
366371
|
malloyModel,
|
|
366372
|
+
runtime,
|
|
366143
366373
|
getRuntime: (overlay) => Model.getModelRuntime(pkg.getPackagePath(), modelPath, pkg.getMalloyConfig(), { overlay })
|
|
366144
366374
|
});
|
|
366145
366375
|
}
|
|
366146
|
-
const connections = await resolvePackageConnections(pkg, parts.
|
|
366147
|
-
const connectionDigests = await resolveConnectionDigests(connections, parts.
|
|
366376
|
+
const connections = await resolvePackageConnections(pkg, parts.builds.map((b3) => b3.connectionName));
|
|
366377
|
+
const connectionDigests = await resolveConnectionDigests(connections, parts.builds, (connectionName) => {
|
|
366148
366378
|
recordConnectionDigestSkipped();
|
|
366149
366379
|
logger.warn("Skipping connection digest; connection did not resolve", {
|
|
366150
366380
|
connectionName
|
|
366151
366381
|
});
|
|
366152
366382
|
});
|
|
366153
366383
|
return {
|
|
366154
|
-
|
|
366384
|
+
builds: parts.builds,
|
|
366155
366385
|
sources: parts.sources,
|
|
366156
366386
|
connectionDigests,
|
|
366157
366387
|
connections,
|
|
@@ -370262,10 +370492,10 @@ class Environment {
|
|
|
370262
370492
|
if (this.destinations.length > 0) {
|
|
370263
370493
|
fs17.mkdirSync(destinationRoot, { recursive: true });
|
|
370264
370494
|
}
|
|
370265
|
-
this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot);
|
|
370495
|
+
this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot, false, { origin: "storage-destination" });
|
|
370266
370496
|
} catch (error) {
|
|
370267
370497
|
logger.error(`Failed to assemble storage destinations for environment ${this.environmentName}; serving without them`, { error });
|
|
370268
|
-
this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath));
|
|
370498
|
+
this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath), false, { origin: "storage-destination" });
|
|
370269
370499
|
}
|
|
370270
370500
|
if (previous && previous !== this.destinationMalloyConfig) {
|
|
370271
370501
|
this.retireConnectionGeneration(`environment ${this.environmentName} destinations`, () => previous.releaseConnections());
|
|
@@ -374591,11 +374821,11 @@ function resolveInstruction(index, persistSource, connectionDigests) {
|
|
|
374591
374821
|
return;
|
|
374592
374822
|
return { instruction, orchestrated: false, sourceEntityId };
|
|
374593
374823
|
}
|
|
374594
|
-
function assertNoTableCollisions(
|
|
374824
|
+
function assertNoTableCollisions(builds, sources, connectionDigests, index) {
|
|
374595
374825
|
const claimedBy = new Map;
|
|
374596
374826
|
const collisions = [];
|
|
374597
|
-
for (const
|
|
374598
|
-
for (const persistSource of
|
|
374827
|
+
for (const build of builds) {
|
|
374828
|
+
for (const persistSource of sourcesInBuildOrder(build, sources)) {
|
|
374599
374829
|
let address;
|
|
374600
374830
|
try {
|
|
374601
374831
|
address = computeSourceEntityId(persistSource, connectionDigests);
|
|
@@ -374605,7 +374835,7 @@ function assertNoTableCollisions(graphs, sources, connectionDigests, index) {
|
|
|
374605
374835
|
const instruction = index.bySourceID.get(persistSource.sourceID) ?? index.bySourceEntityId.get(address);
|
|
374606
374836
|
if (!instruction)
|
|
374607
374837
|
continue;
|
|
374608
|
-
const target = physicalTargetKey(instruction,
|
|
374838
|
+
const target = physicalTargetKey(instruction, build.connectionName);
|
|
374609
374839
|
const claim = claimedBy.get(target);
|
|
374610
374840
|
if (!claim) {
|
|
374611
374841
|
claimedBy.set(target, {
|
|
@@ -374896,8 +375126,8 @@ class MaterializationService {
|
|
|
374896
375126
|
const refusals = [];
|
|
374897
375127
|
const cleared = new Map;
|
|
374898
375128
|
const seen = new Set;
|
|
374899
|
-
for (const
|
|
374900
|
-
for (const persistSource of
|
|
375129
|
+
for (const build of compiled.builds) {
|
|
375130
|
+
for (const persistSource of sourcesInBuildOrder(build, compiled.sources)) {
|
|
374901
375131
|
if (include && !include.has(persistSource.name))
|
|
374902
375132
|
continue;
|
|
374903
375133
|
if (getPersistStorageMode() === "off" && declaredStorage(persistSource)) {
|
|
@@ -375153,7 +375383,7 @@ class MaterializationService {
|
|
|
375153
375383
|
return incrementalRunContext(compiled, run, this.repository);
|
|
375154
375384
|
}
|
|
375155
375385
|
async executeInstructedBuild(compiled, environment, instructions, seedEntries, signal, strict = false, owner, buildMetadata, incremental, priorRefusals = [], clearedTiers) {
|
|
375156
|
-
const {
|
|
375386
|
+
const { builds, sources, connectionDigests, connections } = compiled;
|
|
375157
375387
|
const index = indexInstructions(instructions);
|
|
375158
375388
|
const run = {
|
|
375159
375389
|
compiled,
|
|
@@ -375173,17 +375403,17 @@ class MaterializationService {
|
|
|
375173
375403
|
builtSources: [],
|
|
375174
375404
|
writtenTargets: new Map
|
|
375175
375405
|
};
|
|
375176
|
-
assertNoTableCollisions(
|
|
375406
|
+
assertNoTableCollisions(builds, sources, connectionDigests, index);
|
|
375177
375407
|
try {
|
|
375178
|
-
for (const
|
|
375179
|
-
const connection = connections.get(
|
|
375408
|
+
for (const build of builds) {
|
|
375409
|
+
const connection = connections.get(build.connectionName);
|
|
375180
375410
|
if (!connection) {
|
|
375181
|
-
throw new BadRequestError(`Connection '${
|
|
375411
|
+
throw new BadRequestError(`Connection '${build.connectionName}' not found`);
|
|
375182
375412
|
}
|
|
375183
|
-
for (const persistSource of
|
|
375413
|
+
for (const persistSource of sourcesInBuildOrder(build, sources)) {
|
|
375184
375414
|
if (signal.aborted)
|
|
375185
375415
|
throw new Error("Build cancelled");
|
|
375186
|
-
await this.buildInstructedSource(run, persistSource,
|
|
375416
|
+
await this.buildInstructedSource(run, persistSource, build.connectionName, connection);
|
|
375187
375417
|
}
|
|
375188
375418
|
}
|
|
375189
375419
|
if (run.builtSources.length === 0 && run.failedReasons.length > 0) {
|
|
@@ -376944,7 +377174,7 @@ function readerSql(uri, format, options = {}) {
|
|
|
376944
377174
|
return `read_parquet(${path25})`;
|
|
376945
377175
|
}
|
|
376946
377176
|
function assertUnderDataPath(uri, dataPath) {
|
|
376947
|
-
const root = dataPath
|
|
377177
|
+
const root = stripTrailingSlashes(dataPath);
|
|
376948
377178
|
const outside = () => new BadRequestError(`The object must sit under the catalog's data path (${root}/).`);
|
|
376949
377179
|
if (!root || !uri.startsWith(`${root}/`))
|
|
376950
377180
|
throw outside();
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|