Skip to content

Commit 276fadf

Browse files
feat(duckdb-driver): Add support for installing and loading DuckDB Community Extensions
1 parent d9bc147 commit 276fadf

File tree

4 files changed

+55
-34
lines changed

4 files changed

+55
-34
lines changed

docs/pages/product/configuration/data-sources/duckdb.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ deployment][ref-demo-deployment] in Cube Cloud.
7474
| `CUBEJS_DB_DUCKDB_S3_URL_STYLE` | To choose the S3 URL style(vhost or path) | 'vhost' or 'path' |||
7575
| `CUBEJS_DB_DUCKDB_S3_SESSION_TOKEN` | The token for the S3 session | A valid Session Token |||
7676
| `CUBEJS_DB_DUCKDB_EXTENSIONS` | A comma-separated list of DuckDB extensions to install and load | A comma-separated list of DuckDB extensions |||
77-
77+
| `CUBEJS_DB_DUCKDB_COMMUNITY_EXTENSIONS` | A comma-separated list of DuckDB community extensions to install and load | A comma-separated list of DuckDB community extensions |||
7878
## Pre-Aggregation Feature Support
7979

8080
### count_distinct_approx
@@ -131,4 +131,4 @@ connections are made over HTTPS.
131131
/product/caching/using-pre-aggregations#pre-aggregation-build-strategies
132132
[ref-schema-ref-types-formats-countdistinctapprox]: /reference/data-model/types-and-formats#count_distinct_approx
133133
[self-preaggs-batching]: #batching
134-
[ref-demo-deployment]: /product/deployment/cloud/deployments#demo-deployments
134+
[ref-demo-deployment]: /product/deployment/cloud/deployments#demo-deployments

docs/pages/reference/configuration/environment-variables.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,14 @@ A comma-separated list of DuckDB extensions to install and load.
332332
| ------------------------------------------- | ---------------------- | --------------------- |
333333
| A comma-separated list of DuckDB extensions | N/A | N/A |
334334

335+
## `CUBEJS_DB_DUCKDB_COMMUNITY_EXTENSIONS`
336+
337+
A comma-separated list of DuckDB community extensions to install and load.
338+
339+
| Possible Values | Default in Development | Default in Production |
340+
| ----------------------------------------------------- | ---------------------- | --------------------- |
341+
| A comma-separated list of DuckDB community extensions | N/A | N/A |
342+
335343
## `CUBEJS_DB_ELASTIC_APIKEY_ID`
336344

337345
The [ID of the API key from elastic.co][elastic-docs-api-keys]. Required when

packages/cubejs-backend-shared/src/env.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,6 +1626,19 @@ const variables: Record<string, (...args: any) => any> = {
16261626
}
16271627
return [];
16281628
},
1629+
duckdbCommunityExtensions: ({
1630+
dataSource
1631+
}: {
1632+
dataSource: string,
1633+
}) => {
1634+
const extensions = process.env[
1635+
keyByDataSource('CUBEJS_DB_DUCKDB_COMMUNITY_EXTENSIONS', dataSource)
1636+
];
1637+
if (extensions) {
1638+
return extensions.split(',').map(e => e.trim());
1639+
}
1640+
return [];
1641+
},
16291642
/** ***************************************************************
16301643
* Presto Driver *
16311644
**************************************************************** */

packages/cubejs-duckdb-driver/src/DuckDBDriver.ts

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,35 @@ export class DuckDBDriver extends BaseDriver implements DriverInterface {
5454

5555
return super.toGenericType(columnType.toLowerCase());
5656
}
57+
private async installAndLoadExtensions(extensions: string[], execAsync: (sql: string, ...params: any[]) => Promise<void>, repository: string = '',): Promise<void> {
58+
// repository for official extensions is empty.
59+
repository = repository ? ` FROM ${repository}` : '';
60+
for (const extension of extensions) {
61+
try {
62+
await execAsync(`INSTALL ${extension}${repository}`);
63+
} catch (e) {
64+
if (this.logger) {
65+
console.error(`DuckDB - error on installing ${extension}`, { e });
66+
}
67+
// DuckDB will lose connection_ref on connection on error, this will lead to broken connection object
68+
throw e;
69+
}
5770

71+
try {
72+
await execAsync(`LOAD ${extension}`);
73+
} catch (e) {
74+
if (this.logger) {
75+
console.error(`DuckDB - error on loading ${extension}`, { e });
76+
}
77+
// DuckDB will lose connection_ref on connection on error, this will lead to broken connection object
78+
throw e;
79+
}
80+
}
81+
}
5882
protected async init(): Promise<InitPromise> {
5983
const token = getEnv('duckdbMotherDuckToken', this.config);
6084
const dbPath = getEnv('duckdbDatabasePath', this.config);
61-
85+
6286
// Determine the database URL based on the provided db_path or token
6387
let dbUrl: string;
6488
if (dbPath) {
@@ -119,7 +143,7 @@ export class DuckDBDriver extends BaseDriver implements DriverInterface {
119143
value: getEnv('duckdbS3SessionToken', this.config),
120144
}
121145
];
122-
146+
123147
for (const { key, value } of configuration) {
124148
if (value) {
125149
try {
@@ -134,35 +158,11 @@ export class DuckDBDriver extends BaseDriver implements DriverInterface {
134158
}
135159
}
136160

137-
// Install & load extensions if configured in env variable.
138-
const extensions = getEnv('duckdbExtensions', this.config);
139-
for (const extension of extensions) {
140-
try {
141-
await execAsync(`INSTALL ${extension}`);
142-
} catch (e) {
143-
if (this.logger) {
144-
console.error(`DuckDB - error on installing ${extension}`, {
145-
e
146-
});
147-
}
148-
149-
// DuckDB will lose connection_ref on connection on error, this will lead to broken connection object
150-
throw e;
151-
}
152-
153-
try {
154-
await execAsync(`LOAD ${extension}`);
155-
} catch (e) {
156-
if (this.logger) {
157-
console.error(`DuckDB - error on loading ${extension}`, {
158-
e
159-
});
160-
}
161-
162-
// DuckDB will lose connection_ref on connection on error, this will lead to broken connection object
163-
throw e;
164-
}
165-
}
161+
// Install & load extensions if configured in env variable.
162+
const officialExtensions = getEnv('duckdbExtensions', this.config);
163+
await this.installAndLoadExtensions(officialExtensions, execAsync);
164+
const communityExtensions = getEnv('duckdbCommunityExtensions', this.config);
165+
await this.installAndLoadExtensions(communityExtensions, execAsync, 'community');
166166

167167
if (this.config.initSql) {
168168
try {
@@ -175,7 +175,7 @@ export class DuckDBDriver extends BaseDriver implements DriverInterface {
175175
}
176176
}
177177
}
178-
178+
179179
return {
180180
defaultConnection,
181181
db

0 commit comments

Comments
 (0)