Skip to content

Commit 2065ff1

Browse files
Y--JelteF
andauthored
Revamp MotherDuck settings and secret management (#668)
# Revamp MotherDuck settings and secret management This PR removes any GUC setting for MotherDuck and replaces it with a combination of `SERVER` and `USER MAPPING` for a newly implemented `pg_duckdb` Foreign Data Wrapper. ## New MotherDuck setup: Users may now define one (and at most one) `SERVER` for `motherduck`: ```sql CREATE SERVER <server name> FOREIGN DATA WRAPPER pg_duckdb TYPE 'motherduck' [OPTIONS ( [tables_owner_role 'some user'], -- the role bgw assigns to MD tables -- (by default server creator/owner) [default_database] -- Which MD DB is synced (default "my_db") )]; ``` Once the `SERVER` is created, for MotherDuck to be enabled, it needs at least one `USER MAPPING`: ```sql CREATE USER MAPPING FOR <PG user> SERVER <FDW server name> OPTIONS ( token '<some token>' ); ``` ## Removed GUC settings: The following GUC settings were thus removed and are now obsolete: - `motherduck_enabled`: now implied by a `SERVER` and `USER MAPPING` - `motherduck_token`: now defined in a `USER MAPPING` - `motherduck_postgres_database`: now implied by the database in which the `SERVER` & `USER MAPPING` are defined - `motherduck_default_database`: now defined as an option of the `motherduck` server ## Convenience function: In order to enable MotherDuck support easily, we also provide the following helper: ```sql -- Token will be read from environment if not provided -- Default database is `my_db` if not provided SELECT duckdb.enable_motherduck('<token>', '<default_motherduck_db>') ``` --------- Co-authored-by: Jelte Fennema-Nio <[email protected]>
1 parent 5113989 commit 2065ff1

28 files changed

+1078
-215
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ RUN apt-get update -qq && \
6363

6464
# Automatically enable pg_duckdb
6565
RUN echo "shared_preload_libraries='pg_duckdb'" >> /usr/share/postgresql/postgresql.conf.sample
66-
RUN echo "CREATE EXTENSION IF NOT EXISTS pg_duckdb;" >> /docker-entrypoint-initdb.d/0001-install-pg_duckdb.sql
66+
COPY --chown=postgres:postgres docker/init.d/ /docker-entrypoint-initdb.d/
6767

6868
COPY --from=builder /out /
6969
USER postgres

README.md

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ docker run -d -e POSTGRES_PASSWORD=duckdb pgduckdb/pgduckdb:16-main
7575
And with MotherDuck, you only need a [a MotherDuck access token][md-access-token] and then it is as simple as:
7676
```shell
7777
$ export MOTHERDUCK_TOKEN=<your personal MD token>
78-
$ docker run -d -e POSTGRES_PASSWORD=duckdb -e MOTHERDUCK_TOKEN pgduckdb/pgduckdb:16-main -c duckdb.motherduck_enabled=true
78+
$ docker run -d -e POSTGRES_PASSWORD=duckdb -e MOTHERDUCK_TOKEN pgduckdb/pgduckdb:16-main
7979
```
8080

8181
Or you can use the docker compose in this repo:
@@ -181,37 +181,26 @@ Note: writes to Azure are not yet supported, please see [the current discussion]
181181

182182
### Connect with MotherDuck
183183

184-
pg_duckdb also integrates with [MotherDuck][md]. To enable this support you first need to [generate an access token][md-access-token] and then add the following line to your `postgresql.conf` file:
184+
`pg_duckdb` also integrates with [MotherDuck][md].
185+
To enable this support you first need to [generate an access token][md-access-token].
186+
Then you can enable it by simply using the `enable_motherduck` convenience method:
185187

186-
```ini
187-
duckdb.motherduck_token = 'your_access_token'
188-
```
189-
190-
NOTE: If you don't want to store the token in your `postgresql.conf`file can also store the token in the `motherduck_token` environment variable and then explicitly enable MotherDuck support in your `postgresql.conf` file:
191-
192-
```ini
193-
duckdb.motherduck_enabled = true
194-
```
195-
196-
If you installed `pg_duckdb` in a different Postgres database than the default one named `postgres`, then you also need to add the following line to your `postgresql.conf` file:
197-
198-
```ini
199-
duckdb.motherduck_postgres_database = 'your_database_name'
188+
```sql
189+
-- If not provided, the token will be read from the `motherduck_token` environment variable
190+
-- If not provided, the default MD database name is `my_db`
191+
SELECT duckdb.enable_motherduck('<optional token>', '<optional MD database name>');
200192
```
201193

202-
The default MotherDuck database will be easiest to use (see below for details), by default this is `my_db`. If you want to specify which MotherDuck database is your default database, then you can also add the following line to your `postgresql.conf` file:
203-
204-
```ini
205-
duckdb.motherduck_default_database = 'your_motherduck_database_name'
206-
```
194+
Read more [here][md-docs] about MotherDuck integration.
207195

208-
After doing this (and possibly restarting Postgres). You can then you create tables in the MotherDuck database by using the `duckdb` [Table Access Method][tam] like this:
196+
You can now create tables in the MotherDuck database by using the `duckdb` [Table Access Method][tam] like this:
209197
```sql
210198
CREATE TABLE orders(id bigint, item text, price NUMERIC(10, 2)) USING duckdb;
211199
CREATE TABLE users_md_copy USING duckdb AS SELECT * FROM users;
212200
```
213201

214202
[tam]: https://www.postgresql.org/docs/current/tableam.html
203+
[md-docs]: docs/motherduck.md
215204

216205

217206
Any tables that you already had in MotherDuck are automatically available in Postgres. Since DuckDB and MotherDuck allow accessing multiple databases from a single connection and Postgres does not, we map database+schema in DuckDB to a schema name in Postgres.

docker/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ docker run -d -e POSTGRES_PASSWORD=duckdb pgduckdb/pgduckdb:16-main
3535
And with MotherDuck, it is as simple as:
3636
```shell
3737
$ export MOTHERDUCK_TOKEN=<your personal MD token>
38-
$ docker run -d -e POSTGRES_PASSWORD=duckdb -e MOTHERDUCK_TOKEN pgduckdb/pgduckdb:16-main -c duckdb.motherduck_enabled=true
38+
$ docker run -d -e POSTGRES_PASSWORD=duckdb -e MOTHERDUCK_TOKEN pgduckdb/pgduckdb:16-main
3939
```
4040

4141
You can also use docker compose from duckdb/pg_duckdb:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
CREATE EXTENSION IF NOT EXISTS pg_duckdb;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
-- This script is used to enable MotherDuck support if
2+
-- the token is provided in the environment variables.
3+
4+
\getenv lc_token motherduck_token
5+
\getenv uc_token MOTHERDUCK_TOKEN
6+
7+
\if :{?lc_token}
8+
SELECT duckdb.enable_motherduck(:'lc_token'::TEXT);
9+
\elif :{?uc_token}
10+
SELECT duckdb.enable_motherduck(:'uc_token'::TEXT);
11+
\endif

docs/motherduck.md

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,19 @@
22

33
## Connect with MotherDuck
44

5-
pg_duckdb also integrates with [MotherDuck][md]. To enable this support you first need to [generate an access token][md-access-token] and then add the following line to your `postgresql.conf` file:
5+
`pg_duckdb` integrates with [MotherDuck][md] natively.
6+
To enable this support you first need to [generate an access token][md-access-token].
7+
Then you can enable it by simply using the `enable_motherduck` convenience method:
68

7-
```ini
8-
duckdb.motherduck_token = 'your_access_token'
9-
```
10-
11-
NOTE: If you don't want to store the token in your `postgresql.conf`file can also store the token in the `motherduck_token` environment variable and then explicitly enable MotherDuck support in your `postgresql.conf` file:
12-
13-
```ini
14-
duckdb.motherduck_enabled = true
9+
```sql
10+
-- If not provided, the token will be read from the `motherduck_token` environment variable
11+
-- If not provided, the default MD database name is `my_db`
12+
SELECT duckdb.enable_motherduck('<optional token>', '<optional MD database name>');
1513
```
1614

17-
If you installed `pg_duckdb` in a different Postgres database than the default one named `postgres`, then you also need to add the following line to your `postgresql.conf` file:
15+
This convenience method creates a `motherduck` `SERVER` using the `pg_duckdb` Foreign Data Wrapper, which hosts the options for this integration. It also provides an `USER MAPPING` for the current user, which stores the provided MotherDuck token (if any).
1816

19-
```ini
20-
duckdb.motherduck_postgres_database = 'your_database_name'
21-
```
17+
You can refer to the [section](advanced-motherduck-configuration) below for more on the `SERVER` and `USER MAPPING` configuration.
2218

2319
### Non-supersuer configuration
2420

@@ -55,10 +51,25 @@ CREATE TABLE users_md_copy USING duckdb AS SELECT * FROM users;
5551

5652
Any tables that you already had in MotherDuck are automatically available in Postgres. Since DuckDB and MotherDuck allow accessing multiple databases from a single connection and Postgres does not, we map database+schema in DuckDB to a schema name in Postgres.
5753

58-
The default MotherDuck database will be easiest to use (see below for details), by default this is `my_db`. If you want to specify which MotherDuck database is your default database, then you can also add the following line to your `postgresql.conf` file:
54+
The default MotherDuck database will be easiest to use (see below for details), by default this is `my_db`.
5955

60-
```ini
61-
duckdb.motherduck_default_database = 'your_motherduck_database_name'
56+
## Advanced MotherDuck configuration
57+
58+
If you want to specify which MotherDuck database is your default database, then you need to configure MotherDuck using a `SERVER` and a `USER MAPPING` as such:
59+
60+
```sql
61+
CREATE SERVER motherduck
62+
TYPE 'motherduck'
63+
FOREIGN DATA WRAPPER duckdb
64+
OPTIONS (default_database '<your database>');
65+
66+
-- You may use `::FROM_ENV::` to have the token be read from the environment variable
67+
CREATE USER MAPPING FOR CURRENT_USER SERVER motherduck OPTIONS (token '<your token>')
68+
```
69+
70+
Note: with the `duckdb.enable_motherduck` convenience method above, you can simply do:
71+
```sql
72+
SELECT duckdb.enable_motherduck('<token>', '<default database>');
6273
```
6374

6475
The mapping of database+schema to schema name is then done in the following way:

docs/settings.md

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,45 +10,6 @@ Default: `false`
1010

1111
Access: General
1212

13-
## MotherDuck
14-
15-
MotherDuck support is optional, and can be enabled an configured using these settings. Check out our [MotherDuck documentation](motherduck.md) for more information.
16-
17-
### `duckdb.motherduck_enabled`
18-
19-
If MotherDuck support should be enabled. `auto` means enabled if the `duckdb.motherduck_token` is set.
20-
21-
Default: `MotherDuckEnabled::MOTHERDUCK_AUTO`
22-
23-
Access: Needs to be in the `postgresql.conf` file and requires a restart
24-
25-
### `duckdb.motherduck_token`
26-
27-
The token to use for MotherDuck
28-
29-
Default: `""`
30-
31-
Access: Needs to be in the `postgresql.conf` file and requires a restart
32-
33-
### `duckdb.motherduck_postgres_database`
34-
35-
Which database to enable MotherDuck support in
36-
37-
Default: `"postgres"`
38-
39-
Access: Needs to be in the `postgresql.conf` file and requires a restart
40-
41-
### `duckdb.motherduck_default_database`
42-
43-
Which MotherDuck database to use as the default database, i.e. the one that
44-
gets merged with Postgres schemas instead of getting dedicated `ddb$` prefixed
45-
schemas. The empty string means that pg_duckdb should use the default database
46-
set by MotherDuck, which is currently always `my_db`.
47-
48-
Default: `""`
49-
50-
Access: Needs to be in the `postgresql.conf` file and requires a restart
51-
5213
## Security
5314

5415
### `duckdb.postgres_role`
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#pragma once
2+
3+
#include <string.h>
4+
5+
inline bool
6+
AreStringEqual(const char *str1, const char *str2) {
7+
return strcmp(str1, str2) == 0;
8+
}
9+
10+
inline bool
11+
IsEmptyString(const char *str) {
12+
return AreStringEqual(str, "");
13+
}

include/pgduckdb/pgduckdb_fdw.hpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#pragma once
2+
3+
#include "pgduckdb/pg/declarations.hpp"
4+
5+
extern "C" {
6+
namespace pgduckdb {
7+
8+
Oid FindMotherDuckForeignServerOid();
9+
10+
/*
11+
12+
Returns the Oid of the USER MAPPING for the given user and server.
13+
Returns InvalidOid if no such mapping exists.
14+
15+
Note: we cannot use PG's `GetUserMapping` because:
16+
- it checks the global scope if not found
17+
- it throws an error if no mapping is not found
18+
19+
*/
20+
Oid FindUserMappingForUser(Oid user_oid, Oid server_oid);
21+
22+
/*
23+
24+
Returns the Oid of the `tables_owner_role` option defined in the `motherduck` SERVER
25+
if it exists, returns the SERVER's owner otherwise.
26+
27+
*/
28+
Oid GetMotherDuckPostgresRoleOid(Oid server_oid);
29+
30+
/*
31+
32+
Return the `default_database` setting defined in the `motherduck` SERVER
33+
if it exists, returns nullptr otherwise.
34+
35+
*/
36+
const char *FindMotherDuckDefaultDatabase();
37+
38+
/*
39+
40+
- if `pgduckdb::is_background_worker` then:
41+
> returns `token` option defined in the USER MAPPING of the owner of the SERVER if it exists
42+
> returns nullptr otherwise
43+
- if not `pgduckdb::is_background_worker` then:
44+
> returns `token` option defined in the USER MAPPING of the current user if it exists
45+
> returns nullptr otherwise
46+
47+
*/
48+
const char *FindMotherDuckToken();
49+
50+
const char *FindMotherDuckBackgroundCatalogRefreshInactivityTimeout();
51+
52+
} // namespace pgduckdb
53+
}

include/pgduckdb/pgduckdb_guc.h

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,5 @@ extern bool duckdb_allow_unsigned_extensions;
1212
extern bool duckdb_autoinstall_known_extensions;
1313
extern bool duckdb_autoload_known_extensions;
1414
extern int duckdb_max_workers_per_postgres_scan;
15-
extern char *duckdb_motherduck_postgres_database;
16-
extern int duckdb_motherduck_enabled;
17-
extern char *duckdb_motherduck_token;
1815
extern char *duckdb_postgres_role;
19-
extern char *duckdb_motherduck_default_database;
2016
extern char *duckdb_motherduck_session_hint;
21-
extern char *duckdb_motherduck_background_catalog_refresh_inactivity_timeout;

0 commit comments

Comments
 (0)