@malloy-publisher/server 0.10.4 → 0.10.6
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/admin-api-doc.yaml
CHANGED
|
@@ -234,7 +234,10 @@ paths:
|
|
|
234
234
|
the bytes are rewritten as the catalog's own Parquet, never registered
|
|
235
235
|
in place and never left as rows in the catalog database, so an
|
|
236
236
|
encrypted catalog stays encrypted. One DuckLake transaction:
|
|
237
|
-
a reader sees the old table or the new one.
|
|
237
|
+
a reader sees the old table or the new one. A file the reader refuses
|
|
238
|
+
(not the format it claims, a value it cannot convert) is `400
|
|
239
|
+
INVALID_REQUEST` carrying the reader's own message, with the object
|
|
240
|
+
named by its file name. `201` when the table is new,
|
|
238
241
|
`200` when it replaced one; with `If-None-Match: *` an existing table is
|
|
239
242
|
refused with `412 TABLE_EXISTS`. Synchronous: it answers when the table
|
|
240
243
|
is written.
|
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
|
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.6";
|
|
164835
164835
|
var init_package = () => {};
|
|
164836
164836
|
|
|
164837
164837
|
// src/version.ts
|
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.6";
|
|
164836
164836
|
var init_package = () => {};
|
|
164837
164837
|
|
|
164838
164838
|
// src/version.ts
|
|
@@ -268347,23 +268347,12 @@ var require_bcrypt_pbkdf = __commonJS((exports, module) => {
|
|
|
268347
268347
|
};
|
|
268348
268348
|
});
|
|
268349
268349
|
|
|
268350
|
-
// ../../node_modules/cpu-features/build/Release/cpufeatures.node
|
|
268351
|
-
var require_cpufeatures = __commonJS((exports, module) => {
|
|
268352
|
-
module.exports = __require("./cpufeatures-1yrn0vtw.node");
|
|
268353
|
-
});
|
|
268354
|
-
|
|
268355
|
-
// ../../node_modules/cpu-features/lib/index.js
|
|
268356
|
-
var require_lib5 = __commonJS((exports, module) => {
|
|
268357
|
-
var binding = require_cpufeatures();
|
|
268358
|
-
module.exports = binding.getCPUInfo;
|
|
268359
|
-
});
|
|
268360
|
-
|
|
268361
268350
|
// ../../node_modules/ssh2/lib/protocol/constants.js
|
|
268362
268351
|
var require_constants12 = __commonJS((exports, module) => {
|
|
268363
268352
|
var crypto3 = __require("crypto");
|
|
268364
268353
|
var cpuInfo;
|
|
268365
268354
|
try {
|
|
268366
|
-
cpuInfo =
|
|
268355
|
+
cpuInfo = (()=>{throw new Error("Cannot require module "+"cpu-features");})()();
|
|
268367
268356
|
} catch {}
|
|
268368
268357
|
var { bindingAvailable, CIPHER_INFO, MAC_INFO } = require_crypto();
|
|
268369
268358
|
var eddsaSupported = (() => {
|
|
@@ -268960,6 +268949,11 @@ var require_utils93 = __commonJS((exports, module) => {
|
|
|
268960
268949
|
};
|
|
268961
268950
|
});
|
|
268962
268951
|
|
|
268952
|
+
// ../../node_modules/ssh2/lib/protocol/crypto/build/Release/sshcrypto.node
|
|
268953
|
+
var require_sshcrypto = __commonJS((exports, module) => {
|
|
268954
|
+
module.exports = __require("./sshcrypto-a4xyg5e8.node");
|
|
268955
|
+
});
|
|
268956
|
+
|
|
268963
268957
|
// ../../node_modules/ssh2/lib/protocol/crypto/poly1305.js
|
|
268964
268958
|
var require_poly1305 = __commonJS((exports, module) => {
|
|
268965
268959
|
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 +269440,7 @@ var require_crypto = __commonJS((exports, module) => {
|
|
|
269446
269440
|
var ChaChaPolyDecipher;
|
|
269447
269441
|
var GenericDecipher;
|
|
269448
269442
|
try {
|
|
269449
|
-
binding = (
|
|
269443
|
+
binding = require_sshcrypto();
|
|
269450
269444
|
({
|
|
269451
269445
|
AESGCMCipher,
|
|
269452
269446
|
ChaChaPolyCipher,
|
|
@@ -283001,7 +282995,7 @@ var require_keygen = __commonJS((exports, module) => {
|
|
|
283001
282995
|
});
|
|
283002
282996
|
|
|
283003
282997
|
// ../../node_modules/ssh2/lib/index.js
|
|
283004
|
-
var
|
|
282998
|
+
var require_lib5 = __commonJS((exports, module) => {
|
|
283005
282999
|
var {
|
|
283006
283000
|
AgentProtocol,
|
|
283007
283001
|
BaseAgent,
|
|
@@ -283184,7 +283178,7 @@ function openSshProxy(ssh, target) {
|
|
|
283184
283178
|
var import_ssh2, ALLOW_UNVERIFIED_SSH_HOST_KEY_ENV = "PUBLISHER_ALLOW_UNVERIFIED_SSH_HOST_KEY", TRUTHY_ENV_VALUES, SSH_CONNECT_TIMEOUT_MS = 15000, SSH_KEEPALIVE_INTERVAL_MS = 15000;
|
|
283185
283179
|
var init_proxy = __esm(() => {
|
|
283186
283180
|
init_logger();
|
|
283187
|
-
import_ssh2 = __toESM(
|
|
283181
|
+
import_ssh2 = __toESM(require_lib5(), 1);
|
|
283188
283182
|
TRUTHY_ENV_VALUES = new Set(["1", "true", "yes", "on"]);
|
|
283189
283183
|
});
|
|
283190
283184
|
|
|
@@ -283780,7 +283774,7 @@ function validateMotherDuckShape(connection) {
|
|
|
283780
283774
|
throw new Error("MotherDuck access token is required.");
|
|
283781
283775
|
}
|
|
283782
283776
|
}
|
|
283783
|
-
function validateDuckLakeShape(connection) {
|
|
283777
|
+
function validateDuckLakeShape(connection, origin2) {
|
|
283784
283778
|
if (!connection.ducklakeConnection) {
|
|
283785
283779
|
throw new Error("DuckLake connection configuration is missing.");
|
|
283786
283780
|
}
|
|
@@ -283789,7 +283783,7 @@ function validateDuckLakeShape(connection) {
|
|
|
283789
283783
|
}
|
|
283790
283784
|
const storage = connection.ducklakeConnection.storage;
|
|
283791
283785
|
if (storage?.s3Connection) {
|
|
283792
|
-
validateS3ProviderShape(storage.s3Connection, connection.name);
|
|
283786
|
+
validateS3ProviderShape(storage.s3Connection, connection.name, origin2);
|
|
283793
283787
|
}
|
|
283794
283788
|
if (connection.ducklakeConnection.catalog.metadataSchema !== undefined) {
|
|
283795
283789
|
const schema = connection.ducklakeConnection.catalog.metadataSchema;
|
|
@@ -283866,7 +283860,7 @@ function validatePublisherShape(connection) {
|
|
|
283866
283860
|
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
283861
|
}
|
|
283868
283862
|
}
|
|
283869
|
-
function validateTypeShape(connection) {
|
|
283863
|
+
function validateTypeShape(connection, origin2) {
|
|
283870
283864
|
switch (connection.type) {
|
|
283871
283865
|
case "sqlserver":
|
|
283872
283866
|
return validateSqlServerShape(connection);
|
|
@@ -283877,7 +283871,7 @@ function validateTypeShape(connection) {
|
|
|
283877
283871
|
case "motherduck":
|
|
283878
283872
|
return validateMotherDuckShape(connection);
|
|
283879
283873
|
case "ducklake":
|
|
283880
|
-
return validateDuckLakeShape(connection);
|
|
283874
|
+
return validateDuckLakeShape(connection, origin2);
|
|
283881
283875
|
case "trino":
|
|
283882
283876
|
return validateTrinoShape(connection);
|
|
283883
283877
|
case "databricks":
|
|
@@ -283888,7 +283882,7 @@ function validateTypeShape(connection) {
|
|
|
283888
283882
|
return validatePublisherShape(connection);
|
|
283889
283883
|
}
|
|
283890
283884
|
}
|
|
283891
|
-
function validateConnectionShape(connection) {
|
|
283885
|
+
function validateConnectionShape(connection, origin2 = "connection") {
|
|
283892
283886
|
if (connection.proxy) {
|
|
283893
283887
|
validateProxyShape(connection, connection.proxy);
|
|
283894
283888
|
}
|
|
@@ -283901,7 +283895,7 @@ function validateConnectionShape(connection) {
|
|
|
283901
283895
|
if (connection.postgresConnection) {
|
|
283902
283896
|
postgresStatementTimeoutMs(connection.name ?? "", connection.postgresConnection);
|
|
283903
283897
|
}
|
|
283904
|
-
validateTypeShape(connection);
|
|
283898
|
+
validateTypeShape(connection, origin2);
|
|
283905
283899
|
}
|
|
283906
283900
|
function storageDestinationRoot(environmentPath) {
|
|
283907
283901
|
return path4.join(environmentPath, STORAGE_DESTINATIONS_DIR);
|
|
@@ -283950,7 +283944,7 @@ function validateStorageDestinations(destinations = []) {
|
|
|
283950
283944
|
continue;
|
|
283951
283945
|
}
|
|
283952
283946
|
try {
|
|
283953
|
-
validateConnectionShape(destination);
|
|
283947
|
+
validateConnectionShape(destination, "storage-destination");
|
|
283954
283948
|
const storage = destination.ducklakeConnection?.storage;
|
|
283955
283949
|
if (storage?.s3Connection) {
|
|
283956
283950
|
resolveCloudStorageCredentials({
|
|
@@ -284168,7 +284162,7 @@ function buildCoreEntry(connection, environmentPath) {
|
|
|
284168
284162
|
throw new Error(`Unsupported connection type: ${connection.type}`);
|
|
284169
284163
|
}
|
|
284170
284164
|
}
|
|
284171
|
-
function assembleEnvironmentConnections(connections = [], environmentPath = "") {
|
|
284165
|
+
function assembleEnvironmentConnections(connections = [], environmentPath = "", origin2 = "connection") {
|
|
284172
284166
|
const pojo = { connections: {} };
|
|
284173
284167
|
const metadata = new Map;
|
|
284174
284168
|
const apiConnections = [];
|
|
@@ -284185,7 +284179,7 @@ function assembleEnvironmentConnections(connections = [], environmentPath = "")
|
|
|
284185
284179
|
}
|
|
284186
284180
|
processedConnections.add(connection.name);
|
|
284187
284181
|
validateDuckdbApiSurface(connection);
|
|
284188
|
-
validateConnectionShape(connection);
|
|
284182
|
+
validateConnectionShape(connection, origin2);
|
|
284189
284183
|
warnOnConnectionQueryMetadata(connection);
|
|
284190
284184
|
const apiConnection = cloneApiConnection(connection);
|
|
284191
284185
|
apiConnection.attributes = getStaticConnectionAttributes(connection.type);
|
|
@@ -284256,7 +284250,7 @@ var init_connection_config = __esm(() => {
|
|
|
284256
284250
|
});
|
|
284257
284251
|
|
|
284258
284252
|
// ../../node_modules/webidl-conversions/lib/index.js
|
|
284259
|
-
var
|
|
284253
|
+
var require_lib6 = __commonJS((exports, module) => {
|
|
284260
284254
|
var conversions = {};
|
|
284261
284255
|
module.exports = conversions;
|
|
284262
284256
|
function sign(x2) {
|
|
@@ -285813,7 +285807,7 @@ var require_URL_impl = __commonJS((exports) => {
|
|
|
285813
285807
|
|
|
285814
285808
|
// ../../node_modules/whatwg-url/lib/URL.js
|
|
285815
285809
|
var require_URL = __commonJS((exports, module) => {
|
|
285816
|
-
var conversions =
|
|
285810
|
+
var conversions = require_lib6();
|
|
285817
285811
|
var utils = require_utils95();
|
|
285818
285812
|
var Impl = require_URL_impl();
|
|
285819
285813
|
var impl = utils.implSymbol;
|
|
@@ -286005,7 +285999,7 @@ var require_public_api2 = __commonJS((exports) => {
|
|
|
286005
285999
|
});
|
|
286006
286000
|
|
|
286007
286001
|
// ../../node_modules/node-fetch/lib/index.js
|
|
286008
|
-
var
|
|
286002
|
+
var require_lib7 = __commonJS((exports, module) => {
|
|
286009
286003
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
286010
286004
|
function _interopDefault(ex) {
|
|
286011
286005
|
return ex && typeof ex === "object" && "default" in ex ? ex["default"] : ex;
|
|
@@ -288478,7 +288472,7 @@ var require_gaxios2 = __commonJS((exports) => {
|
|
|
288478
288472
|
exports.Gaxios = undefined;
|
|
288479
288473
|
var extend_1 = __importDefault(require_extend());
|
|
288480
288474
|
var https_1 = __require("https");
|
|
288481
|
-
var node_fetch_1 = __importDefault(
|
|
288475
|
+
var node_fetch_1 = __importDefault(require_lib7());
|
|
288482
288476
|
var querystring_1 = __importDefault(__require("querystring"));
|
|
288483
288477
|
var is_stream_1 = __importDefault(require_is_stream());
|
|
288484
288478
|
var url_1 = __require("url");
|
|
@@ -295166,17 +295160,19 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295166
295160
|
const hasS3 = !!ducklakeConfig.storage?.s3Connection;
|
|
295167
295161
|
const hasGCS = !!ducklakeConfig.storage?.gcsConnection;
|
|
295168
295162
|
if (hasS3) {
|
|
295169
|
-
await
|
|
295170
|
-
|
|
295171
|
-
|
|
295172
|
-
|
|
295173
|
-
|
|
295174
|
-
|
|
295175
|
-
|
|
295176
|
-
|
|
295177
|
-
|
|
295178
|
-
|
|
295179
|
-
|
|
295163
|
+
await connection.runSQL("SET GLOBAL enable_global_s3_configuration=false");
|
|
295164
|
+
}
|
|
295165
|
+
const storage = hasS3 ? {
|
|
295166
|
+
name: `${dbName}_storage`,
|
|
295167
|
+
type: "s3",
|
|
295168
|
+
s3Connection: ducklakeConfig.storage.s3Connection
|
|
295169
|
+
} : hasGCS ? {
|
|
295170
|
+
name: `${dbName}_storage`,
|
|
295171
|
+
type: "gcs",
|
|
295172
|
+
gcsConnection: ducklakeConfig.storage.gcsConnection
|
|
295173
|
+
} : undefined;
|
|
295174
|
+
if (storage) {
|
|
295175
|
+
resolveCloudStorageCredentials(storage, options.origin ?? "connection");
|
|
295180
295176
|
}
|
|
295181
295177
|
const pg = ducklakeConfig.catalog.postgresConnection;
|
|
295182
295178
|
const pgConnString = buildPgConnectionString(pg, {
|
|
@@ -295189,8 +295185,8 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295189
295185
|
logger.debug(`Final escaped connection string: ${redactPgSecrets(escapedPgConnString)}`);
|
|
295190
295186
|
const dataPath = ducklakeConfig.storage?.bucketUrl;
|
|
295191
295187
|
const metadataSchema = ducklakeConfig.catalog.metadataSchema;
|
|
295192
|
-
const
|
|
295193
|
-
if (
|
|
295188
|
+
const preflight = await preflightDuckLakeCatalogFormat(connection, dbName, pgConnString, metadataSchema);
|
|
295189
|
+
if (preflight === "absent") {
|
|
295194
295190
|
if (options.skipIfAbsent) {
|
|
295195
295191
|
return false;
|
|
295196
295192
|
}
|
|
@@ -295215,14 +295211,44 @@ async function attachDuckLakeWithMode(connection, dbName, ducklakeConfig, option
|
|
|
295215
295211
|
throw error;
|
|
295216
295212
|
}
|
|
295217
295213
|
}
|
|
295214
|
+
if (storage) {
|
|
295215
|
+
try {
|
|
295216
|
+
const scope = await attachedDuckLakeScope(connection, dbName, {
|
|
295217
|
+
configuredDataPath: dataPath,
|
|
295218
|
+
metadataSchema,
|
|
295219
|
+
readOnly: options.readOnly,
|
|
295220
|
+
managed: options.managed === true && options.origin !== "storage-destination"
|
|
295221
|
+
});
|
|
295222
|
+
if (!scope) {
|
|
295223
|
+
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.`);
|
|
295224
|
+
}
|
|
295225
|
+
await attachCloudStorage(connection, storage, options.origin, scope);
|
|
295226
|
+
} catch (error) {
|
|
295227
|
+
await connection.runSQL(`DETACH ${quoteIdentifier(dbName, "duckdb")};`).catch(() => {});
|
|
295228
|
+
throw error;
|
|
295229
|
+
}
|
|
295230
|
+
}
|
|
295218
295231
|
return true;
|
|
295219
295232
|
}
|
|
295220
|
-
async function
|
|
295221
|
-
const
|
|
295233
|
+
async function attachedDuckLakeScope(connection, dbName, input) {
|
|
295234
|
+
const recordedDataPath = input.configuredDataPath ? undefined : (await recordedCatalogSettings(connection, dbName, input.metadataSchema)).data_path;
|
|
295235
|
+
const mainSchemaPath = input.managed && input.readOnly ? await recordedSchemaPath(connection, dbName, input.metadataSchema, "main") : undefined;
|
|
295236
|
+
return storageSecretScope({
|
|
295237
|
+
configuredDataPath: input.configuredDataPath,
|
|
295238
|
+
recordedDataPath,
|
|
295239
|
+
mainSchemaPath,
|
|
295240
|
+
readOnly: input.readOnly,
|
|
295241
|
+
managed: input.managed
|
|
295242
|
+
});
|
|
295243
|
+
}
|
|
295244
|
+
function metadataCatalogRef(alias, metadataSchema) {
|
|
295245
|
+
return [
|
|
295222
295246
|
`__ducklake_metadata_${alias}`,
|
|
295223
295247
|
...metadataSchema ? [metadataSchema] : []
|
|
295224
295248
|
].map((part) => quoteIdentifier(part, "duckdb")).join(".");
|
|
295225
|
-
|
|
295249
|
+
}
|
|
295250
|
+
async function recordedCatalogSettings(connection, alias, metadataSchema) {
|
|
295251
|
+
const rows = runSQLRows(await connection.runSQL(`SELECT key, value FROM ${metadataCatalogRef(alias, metadataSchema)}.ducklake_metadata WHERE scope IS NULL`));
|
|
295226
295252
|
const settings = {};
|
|
295227
295253
|
for (const row of rows) {
|
|
295228
295254
|
if (typeof row.key === "string" && row.value != null) {
|
|
@@ -295231,6 +295257,43 @@ async function recordedCatalogSettings(connection, alias, metadataSchema) {
|
|
|
295231
295257
|
}
|
|
295232
295258
|
return settings;
|
|
295233
295259
|
}
|
|
295260
|
+
async function recordedSchemaPath(connection, alias, metadataSchema, schemaName) {
|
|
295261
|
+
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];
|
|
295262
|
+
if (!row || typeof row.path !== "string")
|
|
295263
|
+
return;
|
|
295264
|
+
return { path: row.path, relative: row.path_is_relative !== false };
|
|
295265
|
+
}
|
|
295266
|
+
function stripTrailingSlashes(url3) {
|
|
295267
|
+
let end = url3.length;
|
|
295268
|
+
while (end > 0 && url3[end - 1] === "/")
|
|
295269
|
+
end--;
|
|
295270
|
+
return url3.slice(0, end);
|
|
295271
|
+
}
|
|
295272
|
+
function oneTrailingSlash(url3) {
|
|
295273
|
+
return `${stripTrailingSlashes(url3)}/`;
|
|
295274
|
+
}
|
|
295275
|
+
function isBucketlessUrl(url3) {
|
|
295276
|
+
const at = url3.indexOf("://");
|
|
295277
|
+
return at >= 0 && stripTrailingSlashes(url3.slice(at + 3)) === "";
|
|
295278
|
+
}
|
|
295279
|
+
function storageSecretScope(input) {
|
|
295280
|
+
const root = input.configuredDataPath || input.recordedDataPath;
|
|
295281
|
+
if (!root || isBucketlessUrl(root))
|
|
295282
|
+
return;
|
|
295283
|
+
if (input.managed && input.readOnly) {
|
|
295284
|
+
const main = input.mainSchemaPath;
|
|
295285
|
+
if (!main)
|
|
295286
|
+
return;
|
|
295287
|
+
if (!main.relative) {
|
|
295288
|
+
return isBucketlessUrl(main.path) ? undefined : oneTrailingSlash(main.path);
|
|
295289
|
+
}
|
|
295290
|
+
let start = 0;
|
|
295291
|
+
while (start < main.path.length && main.path[start] === "/")
|
|
295292
|
+
start++;
|
|
295293
|
+
return oneTrailingSlash(`${oneTrailingSlash(root)}${main.path.slice(start)}`);
|
|
295294
|
+
}
|
|
295295
|
+
return oneTrailingSlash(root);
|
|
295296
|
+
}
|
|
295234
295297
|
function dataPathClauses(dataPath, readOnly) {
|
|
295235
295298
|
if (dataPath) {
|
|
295236
295299
|
return `DATA_PATH '${escapeSQL(dataPath)}', OVERRIDE_DATA_PATH true`;
|
|
@@ -295250,13 +295313,16 @@ function firstAttachClauses(options) {
|
|
|
295250
295313
|
async function attachDuckLakeForCreate(connection, dbName, ducklakeConfig, settings) {
|
|
295251
295314
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295252
295315
|
readOnly: false,
|
|
295316
|
+
origin: "storage-destination",
|
|
295253
295317
|
encrypted: settings.encrypted,
|
|
295254
295318
|
dataInliningRowLimit: settings.dataInliningRowLimit
|
|
295255
295319
|
});
|
|
295256
295320
|
}
|
|
295257
|
-
async function attachDuckLake(connection, dbName, ducklakeConfig) {
|
|
295321
|
+
async function attachDuckLake(connection, dbName, ducklakeConfig, options = {}) {
|
|
295258
295322
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295259
|
-
readOnly: ducklakeConfig.readOnly !== false
|
|
295323
|
+
readOnly: ducklakeConfig.readOnly !== false,
|
|
295324
|
+
origin: options.origin,
|
|
295325
|
+
managed: options.managed
|
|
295260
295326
|
});
|
|
295261
295327
|
}
|
|
295262
295328
|
function managedSearchPathSql(dbName) {
|
|
@@ -295267,17 +295333,21 @@ async function setManagedSearchPath(connection, dbName) {
|
|
|
295267
295333
|
await connection.runSQL(managedSearchPathSql(dbName));
|
|
295268
295334
|
}
|
|
295269
295335
|
async function attachDuckLakeReadOnly(connection, dbName, ducklakeConfig) {
|
|
295270
|
-
await attachDuckLake(connection, dbName, ducklakeConfig
|
|
295336
|
+
await attachDuckLake(connection, dbName, ducklakeConfig, {
|
|
295337
|
+
origin: "storage-destination"
|
|
295338
|
+
});
|
|
295271
295339
|
}
|
|
295272
295340
|
async function attachDuckLakeReadOnlyIfPresent(connection, dbName, ducklakeConfig) {
|
|
295273
295341
|
return attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295274
295342
|
readOnly: true,
|
|
295275
|
-
skipIfAbsent: true
|
|
295343
|
+
skipIfAbsent: true,
|
|
295344
|
+
origin: "storage-destination"
|
|
295276
295345
|
});
|
|
295277
295346
|
}
|
|
295278
295347
|
async function attachDuckLakeReadWrite(connection, dbName, ducklakeConfig) {
|
|
295279
295348
|
await attachDuckLakeWithMode(connection, dbName, ducklakeConfig, {
|
|
295280
|
-
readOnly: false
|
|
295349
|
+
readOnly: false,
|
|
295350
|
+
origin: "storage-destination"
|
|
295281
295351
|
});
|
|
295282
295352
|
}
|
|
295283
295353
|
async function federateSourceForPassthrough(connection, sourceType, config, deps = {}) {
|
|
@@ -295481,13 +295551,15 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295481
295551
|
const isGCS = credentials.type === "gcs";
|
|
295482
295552
|
const escapedKeyId = escapeSQL(credentials.accessKeyId);
|
|
295483
295553
|
const escapedSecret = escapeSQL(credentials.secretAccessKey);
|
|
295554
|
+
const scopeClause = credentials.scope ? `SCOPE '${escapeSQL(credentials.scope)}'` : "";
|
|
295484
295555
|
let createSecretCommand;
|
|
295485
295556
|
if (isGCS) {
|
|
295486
295557
|
createSecretCommand = `
|
|
295487
295558
|
CREATE OR REPLACE SECRET ${secretName} (
|
|
295488
295559
|
TYPE gcs,
|
|
295489
295560
|
KEY_ID '${escapedKeyId}',
|
|
295490
|
-
SECRET '${escapedSecret}'
|
|
295561
|
+
SECRET '${escapedSecret}'${scopeClause ? `,
|
|
295562
|
+
${scopeClause}` : ""}
|
|
295491
295563
|
);
|
|
295492
295564
|
`;
|
|
295493
295565
|
} else {
|
|
@@ -295523,6 +295595,9 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295523
295595
|
if (credentials.provider !== "credential_chain" && credentials.sessionToken) {
|
|
295524
295596
|
clauses.push(`SESSION_TOKEN '${escapeSQL(credentials.sessionToken)}'`);
|
|
295525
295597
|
}
|
|
295598
|
+
if (scopeClause) {
|
|
295599
|
+
clauses.push(scopeClause);
|
|
295600
|
+
}
|
|
295526
295601
|
createSecretCommand = `
|
|
295527
295602
|
CREATE OR REPLACE SECRET ${secretName} (
|
|
295528
295603
|
${clauses.join(`,
|
|
@@ -295532,9 +295607,12 @@ function buildCloudStorageSecretSQL(secretName, credentials) {
|
|
|
295532
295607
|
}
|
|
295533
295608
|
return createSecretCommand;
|
|
295534
295609
|
}
|
|
295535
|
-
async function attachCloudStorage(connection, attachedDb) {
|
|
295610
|
+
async function attachCloudStorage(connection, attachedDb, origin2 = "connection", scope) {
|
|
295536
295611
|
const storageType = attachedDb.type?.toUpperCase() || "";
|
|
295537
|
-
const credentials =
|
|
295612
|
+
const credentials = {
|
|
295613
|
+
...resolveCloudStorageCredentials(attachedDb, origin2),
|
|
295614
|
+
scope
|
|
295615
|
+
};
|
|
295538
295616
|
await installAndLoadExtension(connection, "httpfs");
|
|
295539
295617
|
if (credentials.provider === "credential_chain") {
|
|
295540
295618
|
await installAndLoadExtension(connection, "aws");
|
|
@@ -295826,8 +295904,8 @@ function applyConnectionFingerprint(connection, metadata) {
|
|
|
295826
295904
|
function isDuckDBConnection(connection) {
|
|
295827
295905
|
return connection instanceof DuckDBConnection;
|
|
295828
295906
|
}
|
|
295829
|
-
function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false) {
|
|
295830
|
-
const assembled = assembleEnvironmentConnections(connections, environmentPath);
|
|
295907
|
+
function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", isUpdateConnectionRequest = false, { origin: origin2 = "connection" } = {}) {
|
|
295908
|
+
const assembled = assembleEnvironmentConnections(connections, environmentPath, origin2);
|
|
295831
295909
|
const duckLakeCache = new Map;
|
|
295832
295910
|
const snowflakeJwtCache = new Map;
|
|
295833
295911
|
const azureDuckDBCache = new Map;
|
|
@@ -295866,7 +295944,10 @@ function buildEnvironmentMalloyConfig(connections = [], environmentPath = "", is
|
|
|
295866
295944
|
}
|
|
295867
295945
|
const connection2 = await connectionPromise;
|
|
295868
295946
|
if (isUpdateConnectionRequest || !await isDatabaseAttached(connection2, name)) {
|
|
295869
|
-
await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection
|
|
295947
|
+
await attachDuckLake(connection2, name, metadata.apiConnection.ducklakeConnection, {
|
|
295948
|
+
origin: origin2,
|
|
295949
|
+
managed: metadata.apiConnection.managed === true
|
|
295950
|
+
});
|
|
295870
295951
|
if (metadata.apiConnection.managed === true) {
|
|
295871
295952
|
await setManagedSearchPath(connection2, name);
|
|
295872
295953
|
}
|
|
@@ -298447,7 +298528,7 @@ var require_stream_events = __commonJS((exports, module) => {
|
|
|
298447
298528
|
var require_src140 = __commonJS((exports) => {
|
|
298448
298529
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
298449
298530
|
exports.teenyRequest = exports.RequestError = undefined;
|
|
298450
|
-
var node_fetch_1 =
|
|
298531
|
+
var node_fetch_1 = require_lib7();
|
|
298451
298532
|
var stream_1 = __require("stream");
|
|
298452
298533
|
var uuid = require_dist10();
|
|
298453
298534
|
var agents_1 = require_agents();
|
|
@@ -349769,6 +349850,9 @@ function carriesAuthorSql(def) {
|
|
|
349769
349850
|
if (record.type === "sql_select" || record.node === "genericSQLExpr") {
|
|
349770
349851
|
return true;
|
|
349771
349852
|
}
|
|
349853
|
+
if (record.node === "function_call") {
|
|
349854
|
+
return Object.entries(record).some(([key, child]) => key !== "overload" && walk2(child, depth + 1));
|
|
349855
|
+
}
|
|
349772
349856
|
if (record.type === "table" && typeof record.tablePath === "string" && !CATALOG_TABLE_PATH.test(record.tablePath)) {
|
|
349773
349857
|
return true;
|
|
349774
349858
|
}
|
|
@@ -350112,7 +350196,7 @@ async function buildSameCatalogIntoStorage(params) {
|
|
|
350112
350196
|
const { destinationName, destinationConnection, physicalTableName } = params;
|
|
350113
350197
|
const alias = params.sourceConnection.name || destinationName;
|
|
350114
350198
|
if (params.sourceCarriesAuthorSql !== false) {
|
|
350115
|
-
throw new BadRequestError(`Cannot materialize a source that reads storage destination ` + `'${destinationName}' and carries SQL its author wrote (an .sql() source ` + `or a sql_* expression): a build on the catalog runs only Malloy's own SQL. ` + `
|
|
350199
|
+
throw new BadRequestError(`Cannot materialize a source that reads storage destination ` + `'${destinationName}' and carries SQL its author wrote (an .sql() source ` + `or a sql_* expression) or reads a file or URL as a table: a build on the catalog ` + `runs only Malloy's own SQL over the catalog's tables. Express it that way. ` + `Dropping storage= does not help when the source's own connection is the catalog.`);
|
|
350116
350200
|
}
|
|
350117
350201
|
const { session, dispose, workDir } = createIsolatedBuildSession(`build_${destinationName}`);
|
|
350118
350202
|
try {
|
|
@@ -370400,10 +370484,10 @@ class Environment {
|
|
|
370400
370484
|
if (this.destinations.length > 0) {
|
|
370401
370485
|
fs17.mkdirSync(destinationRoot, { recursive: true });
|
|
370402
370486
|
}
|
|
370403
|
-
this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot);
|
|
370487
|
+
this.destinationMalloyConfig = buildEnvironmentMalloyConfig(this.destinations, destinationRoot, false, { origin: "storage-destination" });
|
|
370404
370488
|
} catch (error) {
|
|
370405
370489
|
logger.error(`Failed to assemble storage destinations for environment ${this.environmentName}; serving without them`, { error });
|
|
370406
|
-
this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath));
|
|
370490
|
+
this.destinationMalloyConfig = buildEnvironmentMalloyConfig([], storageDestinationRoot(this.environmentPath), false, { origin: "storage-destination" });
|
|
370407
370491
|
}
|
|
370408
370492
|
if (previous && previous !== this.destinationMalloyConfig) {
|
|
370409
370493
|
this.retireConnectionGeneration(`environment ${this.environmentName} destinations`, () => previous.releaseConnections());
|
|
@@ -377082,7 +377166,7 @@ function readerSql(uri, format, options = {}) {
|
|
|
377082
377166
|
return `read_parquet(${path25})`;
|
|
377083
377167
|
}
|
|
377084
377168
|
function assertUnderDataPath(uri, dataPath) {
|
|
377085
|
-
const root = dataPath
|
|
377169
|
+
const root = stripTrailingSlashes(dataPath);
|
|
377086
377170
|
const outside = () => new BadRequestError(`The object must sit under the catalog's data path (${root}/).`);
|
|
377087
377171
|
if (!root || !uri.startsWith(`${root}/`))
|
|
377088
377172
|
throw outside();
|
|
@@ -377121,11 +377205,21 @@ async function loadStorageTable(params) {
|
|
|
377121
377205
|
if (params.format === "xlsx") {
|
|
377122
377206
|
await installAndLoadExtension(session, "excel");
|
|
377123
377207
|
}
|
|
377124
|
-
return await writeTableAs(session, params, reader);
|
|
377208
|
+
return await writeTableAs(session, params, reader, (error) => readerRefusal(error, params.uri));
|
|
377125
377209
|
} finally {
|
|
377126
377210
|
await dispose();
|
|
377127
377211
|
}
|
|
377128
377212
|
}
|
|
377213
|
+
function readerRefusal(error, uri) {
|
|
377214
|
+
if (!(error instanceof Error))
|
|
377215
|
+
return null;
|
|
377216
|
+
if (!/^(Invalid Input|Conversion) Error:/.test(error.message))
|
|
377217
|
+
return null;
|
|
377218
|
+
logInternalFailure("Load refused by the file reader", error, "warn");
|
|
377219
|
+
const fileName = path24.posix.basename(uri);
|
|
377220
|
+
const words2 = error.message.split(/\n\s*LINE \d+:/)[0].split(uri).join(fileName).trim();
|
|
377221
|
+
return new BadRequestError(words2);
|
|
377222
|
+
}
|
|
377129
377223
|
async function copyStorageTable(params) {
|
|
377130
377224
|
const { destinationName, destinationConnection, source } = params;
|
|
377131
377225
|
for (const conn of [destinationConnection, source.connection]) {
|
|
@@ -377158,7 +377252,7 @@ async function copyStorageTable(params) {
|
|
|
377158
377252
|
await dispose();
|
|
377159
377253
|
}
|
|
377160
377254
|
}
|
|
377161
|
-
async function writeTableAs(session, params, from) {
|
|
377255
|
+
async function writeTableAs(session, params, from, refused) {
|
|
377162
377256
|
const { destinationName, schemaName, tableName } = params;
|
|
377163
377257
|
await session.runSQL("SET ducklake_default_data_inlining_row_limit = 0");
|
|
377164
377258
|
const target = [destinationName, schemaName, tableName].map((part) => quoteIdentifier(part, "duckdb")).join(".");
|
|
@@ -377168,7 +377262,7 @@ async function writeTableAs(session, params, from) {
|
|
|
377168
377262
|
if (params.mode === "create" && error instanceof Error && /already exists/i.test(error.message)) {
|
|
377169
377263
|
throw new ConflictError(`Table '${schemaName}.${tableName}' already exists in '${destinationName}'.`);
|
|
377170
377264
|
}
|
|
377171
|
-
throw error;
|
|
377265
|
+
throw refused?.(error) ?? error;
|
|
377172
377266
|
}
|
|
377173
377267
|
await session.runSQL(`CALL ducklake_flush_inlined_data('${escapeSQL(destinationName)}', schema_name => '${escapeSQL(schemaName)}', table_name => '${escapeSQL(tableName)}')`);
|
|
377174
377268
|
const counted = rows(await session.runSQL(`SELECT count(*) AS n FROM ${target}`));
|
|
Binary file
|
package/package.json
CHANGED
|
Binary file
|