Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync after each insert/update/delete of duckdb.secrets table #253

Merged
merged 2 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions include/pgduckdb/pgduckdb_duckdb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,34 @@ namespace pgduckdb {

class DuckDBManager {
public:
static inline const DuckDBManager &
static inline DuckDBManager &
Get() {
static DuckDBManager instance;
return instance;
}

inline duckdb::DuckDB &
GetDatabase() const {
GetDatabase() {
if(CheckSecretsSeq()) {
auto connection = duckdb::make_uniq<duckdb::Connection>(*database);
auto &context = *connection->context;
DropSecrets(context);
LoadSecrets(context);
}
return *database;
}

private:
DuckDBManager();
void InitializeDatabase();

bool CheckSecretsSeq();
void LoadSecrets(duckdb::ClientContext &);
void DropSecrets(duckdb::ClientContext &);
void LoadExtensions(duckdb::ClientContext &);
void LoadFunctions(duckdb::ClientContext &);

int secret_table_num_rows;
int secret_table_current_seq;
duckdb::unique_ptr<duckdb::DuckDB> database;
};

Expand Down
15 changes: 15 additions & 0 deletions sql/pg_duckdb--0.0.1.sql
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ $func$;
CREATE SCHEMA duckdb;
SET search_path TO duckdb;

CREATE SEQUENCE secrets_table_seq START WITH 1 INCREMENT BY 1;
SELECT setval('secrets_table_seq', 1);

CREATE TABLE secrets (
type TEXT NOT NULL,
id TEXT NOT NULL,
Expand All @@ -120,6 +123,18 @@ CREATE TABLE secrets (
CONSTRAINT type_constraint CHECK (type IN ('S3', 'GCS', 'R2'))
);

CREATE OR REPLACE FUNCTION duckdb_update_secrets_table_seq()
RETURNS TRIGGER AS
$$
BEGIN
PERFORM nextval('duckdb.secrets_table_seq');
RETURN NEW;
END;
$$ LANGUAGE PLpgSQL;

CREATE TRIGGER secrets_table_seq_tr AFTER INSERT OR UPDATE OR DELETE ON secrets
EXECUTE FUNCTION duckdb_update_secrets_table_seq();

CREATE OR REPLACE FUNCTION duckdb_secret_r2_check()
RETURNS TRIGGER AS
$$
Expand Down
40 changes: 37 additions & 3 deletions src/pgduckdb_duckdb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
#include "duckdb/catalog/catalog_search_path.hpp"
#include "duckdb/main/extension_install_info.hpp"

extern "C" {
#include "postgres.h"
#include "catalog/namespace.h"
#include "utils/lsyscache.h"
#include "utils/fmgrprotos.h"
}

#include "pgduckdb/pgduckdb_options.hpp"
#include "pgduckdb/pgduckdb_duckdb.hpp"
#include "pgduckdb/scan/postgres_scan.hpp"
Expand Down Expand Up @@ -69,7 +76,7 @@ GetExtensionDirectory() {
return duckdb_extension_directory;
}

DuckDBManager::DuckDBManager() {
DuckDBManager::DuckDBManager() : secret_table_num_rows(0), secret_table_current_seq(0) {
elog(DEBUG2, "(PGDuckDB/DuckDBManager) Creating DuckDB instance");

duckdb::DBConfig config;
Expand All @@ -88,7 +95,6 @@ DuckDBManager::DuckDBManager() {
auto &context = *connection->context;
context.Query("ATTACH DATABASE 'pgduckdb' (TYPE pgduckdb)", false);
LoadFunctions(context);
LoadSecrets(context);
LoadExtensions(context);
}

Expand All @@ -105,6 +111,19 @@ DuckDBManager::LoadFunctions(duckdb::ClientContext &context) {
context.transaction.Commit();
}

bool
DuckDBManager::CheckSecretsSeq() {
Oid duckdb_namespace = get_namespace_oid("duckdb", false);
Oid secret_table_seq_oid = get_relname_relid("secrets_table_seq", duckdb_namespace);
int64 seq =
PostgresFunctionGuard<int64>(DirectFunctionCall1Coll, pg_sequence_last_value, InvalidOid, secret_table_seq_oid);
if (secret_table_current_seq < seq) {
secret_table_current_seq = seq;
return true;
}
return false;
}

void
DuckDBManager::LoadSecrets(duckdb::ClientContext &context) {
auto duckdb_secrets = ReadDuckdbSecrets();
Expand All @@ -113,7 +132,7 @@ DuckDBManager::LoadSecrets(duckdb::ClientContext &context) {
for (auto &secret : duckdb_secrets) {
StringInfo secret_key = makeStringInfo();
bool is_r2_cloud_secret = (secret.type.rfind("R2", 0) == 0);
appendStringInfo(secret_key, "CREATE SECRET duckdbSecret_%d ", secret_id);
appendStringInfo(secret_key, "CREATE SECRET pgduckb_secret_%d ", secret_id);
appendStringInfo(secret_key, "(TYPE %s, KEY_ID '%s', SECRET '%s'", secret.type.c_str(), secret.id.c_str(),
secret.secret.c_str());
if (secret.region.length() && !is_r2_cloud_secret) {
Expand All @@ -140,6 +159,21 @@ DuckDBManager::LoadSecrets(duckdb::ClientContext &context) {
pfree(secret_key->data);
secret_id++;
}

secret_table_num_rows = secret_id;
}

void
DuckDBManager::DropSecrets(duckdb::ClientContext &context) {

for (auto secret_id = 0; secret_id < secret_table_num_rows; secret_id++) {
auto drop_secret_cmd = duckdb::StringUtil::Format("DROP SECRET pgduckb_secret_%d;", secret_id);
auto res = context.Query(drop_secret_cmd, false);
if (res->HasError()) {
elog(ERROR, "(PGDuckDB/DropSecrets) secret `%d` could not be dropped.", secret_id);
}
}
secret_table_num_rows = 0;
}

void
Expand Down
81 changes: 81 additions & 0 deletions test/regression/expected/secrets.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
SET duckdb.execution TO false;
SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);
NOTICE: result: name
VARCHAR
[ Rows: 0]


raw_query
-----------

(1 row)

SELECT last_value FROM duckdb.secrets_table_seq;
last_value
------------
1
(1 row)

-- INSERT SHOULD TRIGGER UPDATE OF SECRETS
INSERT INTO duckdb.secrets (type, id, secret, session_token, region)
VALUES ('S3', 'access_key_id_1', 'secret_access_key', 'session_token', 'us-east-1');
SELECT last_value FROM duckdb.secrets_table_seq;
last_value
------------
2
(1 row)

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);
NOTICE: result: name
VARCHAR
[ Rows: 1]
pgduckb_secret_0


raw_query
-----------

(1 row)

INSERT INTO duckdb.secrets (type, id, secret, session_token, region)
VALUES ('S3', 'access_key_id_2', 'secret_access_key', 'session_token', 'us-east-1');
SELECT last_value FROM duckdb.secrets_table_seq;
last_value
------------
3
(1 row)

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);
NOTICE: result: name
VARCHAR
[ Rows: 2]
pgduckb_secret_0
pgduckb_secret_1


raw_query
-----------

(1 row)

-- DELETE SHOULD TRIGGER UPDATE OF SECRETS
DELETE FROM duckdb.secrets WHERE id = 'access_key_id_1';
SELECT last_value FROM duckdb.secrets_table_seq;
last_value
------------
4
(1 row)

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);
NOTICE: result: name
VARCHAR
[ Rows: 1]
pgduckb_secret_0


raw_query
-----------

(1 row)

SET duckdb.execution TO true;
1 change: 1 addition & 0 deletions test/regression/schedule
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ test: standard_conforming_strings
test: query_filter
test: altered_tables
test: transaction_errors
test: secrets
31 changes: 31 additions & 0 deletions test/regression/sql/secrets.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

SET duckdb.execution TO false;

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);

SELECT last_value FROM duckdb.secrets_table_seq;

-- INSERT SHOULD TRIGGER UPDATE OF SECRETS

INSERT INTO duckdb.secrets (type, id, secret, session_token, region)
VALUES ('S3', 'access_key_id_1', 'secret_access_key', 'session_token', 'us-east-1');

SELECT last_value FROM duckdb.secrets_table_seq;

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);

INSERT INTO duckdb.secrets (type, id, secret, session_token, region)
VALUES ('S3', 'access_key_id_2', 'secret_access_key', 'session_token', 'us-east-1');

SELECT last_value FROM duckdb.secrets_table_seq;

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);

-- DELETE SHOULD TRIGGER UPDATE OF SECRETS
DELETE FROM duckdb.secrets WHERE id = 'access_key_id_1';

SELECT last_value FROM duckdb.secrets_table_seq;

SELECT * FROM duckdb.raw_query($$ SELECT name FROM duckdb_secrets() $$);

SET duckdb.execution TO true;