Skip to content

Commit

Permalink
Allow user to configure default MotherDuck database in postgresql.conf (
Browse files Browse the repository at this point in the history
#470)

Allow the user to configure a different default MotherDuck database from
`my_db` in `postgresql.conf`. This affords flexibility because there is
currently no user-facing API to change the internal "default" DB in
MotherDuck.

Fixes #459

---------

Co-authored-by: Jelte Fennema-Nio <[email protected]>
  • Loading branch information
naoyak and JelteF authored Dec 4, 2024
1 parent 6e49c69 commit dff3a16
Show file tree
Hide file tree
Showing 9 changed files with 70 additions and 12 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,12 @@ If you installed `pg_duckdb` in a different Postgres database than the default o
duckdb.motherduck_postgres_database = 'your_database_name'
```

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:

```ini
duckdb.motherduck_default_database = 'your_motherduck_database_name'
```

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:
```sql
CREATE TABLE orders(id bigint, item text, price NUMERIC(10, 2)) USING duckdb;
Expand All @@ -189,7 +195,7 @@ CREATE TABLE users_md_copy USING duckdb AS SELECT * FROM users;
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.

This is done in the following way:
1. Each schema in your default MotherDuck database are simply merged with the Postgres schemas with the same name.
1. Each schema in your default MotherDuck database (see above on how to specify which database is the default) are simply merged with the Postgres schemas with the same name.
2. Except for the `main` DuckDB schema in your default database, which is merged with the Postgres `public` schema.
3. Tables in other databases are put into dedicated DuckDB-only schemas. These schemas are of the form `ddb$<duckdb_db_name>$<duckdb_schema_name>` (including the literal `$` characters).
4. Except for the `main` schema in those other databases. That schema should be accessed using the shorter name `ddb$<db_name>` instead.
Expand Down
8 changes: 7 additions & 1 deletion docs/motherduck.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,13 @@ CREATE TABLE users_md_copy USING duckdb AS SELECT * FROM users;

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.

This is done in the following way:
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:

```ini
duckdb.motherduck_default_database = 'your_motherduck_database_name'
```

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

1. Each schema in your default MotherDuck database are simply merged with the Postgres schemas with the same name.
2. Except for the `main` DuckDB schema in your default database, which is merged with the Postgres `public` schema.
Expand Down
11 changes: 11 additions & 0 deletions docs/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ Default: `"postgres"`

Access: General

### `duckdb.motherduck_default_database`

Which MotherDuck database to use as the default database, i.e. the one that
gets merged with Postgres schemas instead of getting dedicated `ddb$` prefixed
schemas. The empty string means that pg_duckdb should use the default database
set by MotherDuck, which is currently always `my_db`.

Default: `""`

Access: Needs to be in the `postgresql.conf` file and requires a restart

## Security

### `duckdb.postgres_role`
Expand Down
1 change: 1 addition & 0 deletions include/pgduckdb/pgduckdb_guc.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ extern char *duckdb_motherduck_postgres_database;
extern int duckdb_motherduck_enabled;
extern char *duckdb_motherduck_token;
extern char *duckdb_postgres_role;
extern char *duckdb_motherduck_default_database;
3 changes: 3 additions & 0 deletions sql/pg_duckdb--0.1.0--0.2.0.sql
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,6 @@ CREATE PROCEDURE duckdb.recycle_ddb()
REVOKE ALL ON PROCEDURE duckdb.recycle_ddb() FROM PUBLIC;

ALTER TABLE duckdb.secrets ADD COLUMN scope TEXT;

ALTER TABLE duckdb.tables ADD COLUMN default_database TEXT NOT NULL DEFAULT 'my_db';
ALTER TABLE duckdb.tables ALTER COLUMN default_database DROP DEFAULT;
5 changes: 5 additions & 0 deletions src/pgduckdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ int duckdb_max_threads_per_postgres_scan = 1;
int duckdb_motherduck_enabled = MotherDuckEnabled::MOTHERDUCK_AUTO;
char *duckdb_motherduck_token = strdup("");
char *duckdb_motherduck_postgres_database = strdup("postgres");
char *duckdb_motherduck_default_database = strdup("");
char *duckdb_postgres_role = strdup("");

int duckdb_maximum_threads = -1;
Expand Down Expand Up @@ -164,4 +165,8 @@ DuckdbInitGUC(void) {

DefineCustomVariable("duckdb.motherduck_postgres_database", "Which database to enable MotherDuck support in",
&duckdb_motherduck_postgres_database);

DefineCustomVariable("duckdb.motherduck_default_database",
"Which database in MotherDuck to designate as default (in place of my_db)",
&duckdb_motherduck_default_database, PGC_POSTMASTER, GUC_SUPERUSER_ONLY);
}
10 changes: 7 additions & 3 deletions src/pgduckdb_background_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -682,9 +682,10 @@ SyncMotherDuckCatalogsWithPg_Cpp(bool drop_with_cascade) {
continue;
}

Oid arg_types[] = {TEXTOID, TEXTOID};
Oid arg_types[] = {TEXTOID, TEXTOID, TEXTOID};
Datum values[] = {CStringGetTextDatum(motherduck_db.c_str()),
CStringGetTextDatum(pgduckdb::current_motherduck_catalog_version)};
CStringGetTextDatum(pgduckdb::current_motherduck_catalog_version),
CStringGetTextDatum(default_db.c_str())};

/*
* We use a cursor here instead of plain SPI_execute, to put a limit on
Expand All @@ -695,7 +696,10 @@ SyncMotherDuckCatalogsWithPg_Cpp(bool drop_with_cascade) {
Portal deleted_tables_portal =
SPI_cursor_open_with_args(nullptr, R"(
SELECT relid::text FROM duckdb.tables
WHERE duckdb_db = $1 AND motherduck_catalog_version != $2
WHERE duckdb_db = $1 AND (
motherduck_catalog_version != $2 OR
default_database != $3
)
)",
lengthof(arg_types), arg_types, values, NULL, false, 0);

Expand Down
11 changes: 6 additions & 5 deletions src/pgduckdb_ddl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,20 +281,21 @@ DECLARE_PG_FUNCTION(duckdb_create_table_trigger) {
int sec_context;
const char *postgres_schema_name = get_namespace_name_or_temp(get_rel_namespace(relid));
const char *duckdb_db = (const char *)linitial(pgduckdb_db_and_schema(postgres_schema_name, true));
auto default_db = pgduckdb::DuckDBManager::Get().GetDefaultDBName();
GetUserIdAndSecContext(&saved_userid, &sec_context);
SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID, sec_context | SECURITY_LOCAL_USERID_CHANGE);

Oid arg_types[] = {OIDOID, TEXTOID, TEXTOID};
Datum values[] = {relid_datum, CStringGetTextDatum(duckdb_db), 0};
char nulls[] = {' ', ' ', 'n'};
Oid arg_types[] = {OIDOID, TEXTOID, TEXTOID, TEXTOID};
Datum values[] = {relid_datum, CStringGetTextDatum(duckdb_db), 0, CStringGetTextDatum(default_db.c_str())};
char nulls[] = {' ', ' ', 'n', ' '};

if (pgduckdb::doing_motherduck_sync) {
values[2] = CStringGetTextDatum(pgduckdb::current_motherduck_catalog_version);
nulls[2] = ' ';
}
ret = SPI_execute_with_args(R"(
INSERT INTO duckdb.tables (relid, duckdb_db, motherduck_catalog_version)
VALUES ($1, $2, $3)
INSERT INTO duckdb.tables (relid, duckdb_db, motherduck_catalog_version, default_database)
VALUES ($1, $2, $3, $4)
)",
lengthof(arg_types), arg_types, values, nulls, false, 0);

Expand Down
25 changes: 23 additions & 2 deletions src/pgduckdb_duckdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
extern "C" {
#include "postgres.h"
#include "catalog/namespace.h"
#include "lib/stringinfo.h"
#include "utils/lsyscache.h" // get_relname_relid
#include "utils/fmgrprotos.h" // pg_sequence_last_value
#include "common/file_perm.h"
Expand Down Expand Up @@ -77,6 +78,24 @@ CreateOrGetDirectoryPath(const char *directory_name) {
return duckdb_data_directory;
}

static char *
uri_escape(const char *str) {
StringInfoData buf;
initStringInfo(&buf);

while (*str) {
char c = *str++;
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '-' || c == '_' ||
c == '.' || c == '~') {
appendStringInfoChar(&buf, c);
} else {
appendStringInfo(&buf, "%%%02X", (unsigned char)c);
}
}

return buf.data;
}

namespace ddb {
bool
DidWrites() {
Expand Down Expand Up @@ -145,10 +164,12 @@ DuckDBManager::Initialize() {
* is not trivial, so for now we simply disable the web login.
*/
setenv("motherduck_disable_web_login", "1", 1);
duckdb_motherduck_default_database = uri_escape(duckdb_motherduck_default_database);
if (duckdb_motherduck_token[0] == '\0') {
connection_string = "md:";
connection_string = psprintf("md:%s", duckdb_motherduck_default_database);
} else {
connection_string = psprintf("md:?motherduck_token=%s", duckdb_motherduck_token);
connection_string =
psprintf("md:%s?motherduck_token=%s", duckdb_motherduck_default_database, duckdb_motherduck_token);
}
}

Expand Down

0 comments on commit dff3a16

Please sign in to comment.