-
Notifications
You must be signed in to change notification settings - Fork 56
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
22 changed files
with
1,069 additions
and
330 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
extern "C" { | ||
#include "postgres.h" | ||
#include "nodes/nodes.h" | ||
} | ||
void DuckdbHandleDDL(Node *ParseTree, const char *queryString); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,5 @@ | ||
#include "postgres.h" | ||
|
||
char *pgduckdb_relation_name(Oid relid); | ||
char *pgduckdb_function_name(Oid function_oid); | ||
char *pgduckdb_get_tabledef(Oid relation_id); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
extern "C" { | ||
#include "postgres.h" | ||
#include "access/tableam.h" | ||
} | ||
|
||
namespace pgduckdb { | ||
bool IsDuckdbTableAm(const TableAmRoutine *am); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
#include "duckdb.hpp" | ||
#include <regex> | ||
|
||
extern "C" { | ||
#include "postgres.h" | ||
#include "access/tableam.h" | ||
#include "catalog/pg_type.h" | ||
#include "commands/event_trigger.h" | ||
#include "fmgr.h" | ||
#include "catalog/pg_authid_d.h" | ||
#include "funcapi.h" | ||
#include "nodes/print.h" | ||
#include "nodes/makefuncs.h" | ||
#include "optimizer/optimizer.h" | ||
#include "tcop/utility.h" | ||
#include "utils/syscache.h" | ||
#include "commands/event_trigger.h" | ||
#include "executor/spi.h" | ||
#include "miscadmin.h" | ||
|
||
#include "pgduckdb/pgduckdb_ruleutils.h" | ||
} | ||
|
||
#include "pgduckdb/pgduckdb_duckdb.hpp" | ||
#include "pgduckdb/vendor/pg_list.hpp" | ||
#include <inttypes.h> | ||
|
||
void | ||
DuckdbHandleDDL(Node *parsetree, const char *queryString) { | ||
if (IsA(parsetree, CreateTableAsStmt)) { | ||
auto stmt = castNode(CreateTableAsStmt, parsetree); | ||
char *access_method = stmt->into->accessMethod ? stmt->into->accessMethod : default_table_access_method; | ||
if (strcmp(access_method, "duckdb") != 0) { | ||
/* not a duckdb table, so don't mess with the query */ | ||
return; | ||
} | ||
|
||
elog(ERROR, "DuckDB does not support CREATE TABLE AS yet"); | ||
} | ||
} | ||
|
||
extern "C" { | ||
|
||
PG_FUNCTION_INFO_V1(duckdb_create_table_trigger); | ||
|
||
Datum | ||
duckdb_create_table_trigger(PG_FUNCTION_ARGS) { | ||
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */ | ||
elog(ERROR, "not fired by event trigger manager"); | ||
|
||
SPI_connect(); | ||
|
||
/* Temporarily escalate privileges to superuser so we can insert into duckdb.tables */ | ||
Oid saved_userid; | ||
int sec_context; | ||
GetUserIdAndSecContext(&saved_userid, &sec_context); | ||
SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID, sec_context | SECURITY_LOCAL_USERID_CHANGE); | ||
/* | ||
* We track the table oid in duckdb.tables so we can later check in our | ||
* delete event trigger if the table was created using the duckdb access | ||
* method. See the code comment in that function for more details. | ||
*/ | ||
int ret = SPI_exec(R"( | ||
INSERT INTO duckdb.tables(relid) | ||
SELECT objid | ||
FROM pg_catalog.pg_event_trigger_ddl_commands() cmds | ||
JOIN pg_catalog.pg_class | ||
ON cmds.objid = pg_class.oid | ||
WHERE cmds.object_type = 'table' | ||
AND pg_class.relam = (SELECT oid FROM pg_am WHERE amname = 'duckdb') | ||
RETURNING relid)", | ||
0); | ||
|
||
/* Revert back to original privileges */ | ||
SetUserIdAndSecContext(saved_userid, sec_context); | ||
|
||
if (ret != SPI_OK_INSERT_RETURNING) | ||
elog(ERROR, "SPI_exec failed: error code %s", SPI_result_code_string(ret)); | ||
|
||
/* if we inserted a row it was a duckdb table */ | ||
auto isDuckdbTable = SPI_processed > 0; | ||
|
||
if (!isDuckdbTable) { | ||
SPI_finish(); | ||
PG_RETURN_NULL(); | ||
} | ||
if (SPI_processed != 1) { | ||
elog(ERROR, "Expected single table to be created, but found %" PRIu64, SPI_processed); | ||
} | ||
HeapTuple tuple = SPI_tuptable->vals[0]; | ||
bool isnull; | ||
Datum relid_datum = SPI_getbinval(tuple, SPI_tuptable->tupdesc, 1, &isnull); | ||
if (isnull) { | ||
elog(ERROR, "Expected relid to be returned, but found NULL"); | ||
} | ||
Oid relid = DatumGetObjectId(relid_datum); | ||
SPI_finish(); | ||
|
||
const char *query_cstr = pgduckdb_get_tabledef(relid); | ||
std::string query_string(query_cstr); | ||
|
||
auto db = pgduckdb::DuckDBManager::Get().GetDatabase(); | ||
auto connection = duckdb::make_uniq<duckdb::Connection>(db); | ||
auto &context = *connection->context; | ||
auto result = context.Query(query_string, false); | ||
if (result->HasError()) { | ||
elog(ERROR, "(PGDuckDB/duckdb_create_table_trigger) Could not create table: %s", result->GetError().c_str()); | ||
} | ||
|
||
PG_RETURN_NULL(); | ||
} | ||
PG_FUNCTION_INFO_V1(duckdb_drop_table_trigger); | ||
|
||
Datum | ||
duckdb_drop_table_trigger(PG_FUNCTION_ARGS) { | ||
if (!CALLED_AS_EVENT_TRIGGER(fcinfo)) /* internal error */ | ||
elog(ERROR, "not fired by event trigger manager"); | ||
|
||
SPI_connect(); | ||
|
||
/* Temporarily escalate privileges to superuser so we can delete from duckdb.tables */ | ||
Oid saved_userid; | ||
int sec_context; | ||
GetUserIdAndSecContext(&saved_userid, &sec_context); | ||
SetUserIdAndSecContext(BOOTSTRAP_SUPERUSERID, sec_context | SECURITY_LOCAL_USERID_CHANGE); | ||
|
||
// Because the table metadata is deleted from the postgres catalogs we | ||
// cannot find out if the table was using the duckdb access method. So | ||
// instead we keep our own metadata table that also tracks which tables are | ||
// duckdb tables. | ||
int ret = SPI_exec(R"( | ||
DELETE FROM duckdb.tables | ||
USING ( | ||
SELECT objid, object_name, object_identity | ||
FROM pg_catalog.pg_event_trigger_dropped_objects() | ||
WHERE object_type = 'table' | ||
) objs | ||
WHERE relid = objid | ||
RETURNING objs.object_identity | ||
)", | ||
0); | ||
|
||
/* Revert back to original privileges */ | ||
SetUserIdAndSecContext(saved_userid, sec_context); | ||
|
||
if (ret != SPI_OK_DELETE_RETURNING) | ||
elog(ERROR, "SPI_exec failed: error code %s", SPI_result_code_string(ret)); | ||
|
||
auto db = pgduckdb::DuckDBManager::Get().GetDatabase(); | ||
auto connection = duckdb::make_uniq<duckdb::Connection>(db); | ||
auto &context = *connection->context; | ||
|
||
auto result = context.Query("BEGIN TRANSACTION", false); | ||
if (result->HasError()) { | ||
elog(ERROR, "(PGDuckDB/duckdb_drop_table_trigger) Could not start transaction"); | ||
} | ||
|
||
for (auto proc = 0; proc < SPI_processed; proc++) { | ||
HeapTuple tuple = SPI_tuptable->vals[proc]; | ||
|
||
char *object_identity = SPI_getvalue(tuple, SPI_tuptable->tupdesc, 1); | ||
// TODO: Handle std::string creation in a safe way for allocation failures | ||
auto result = context.Query("DROP TABLE IF EXISTS " + std::string(object_identity), false); | ||
if (result->HasError()) { | ||
elog(ERROR, "(PGDuckDB/duckdb_drop_table_trigger) Could not drop table %s: %s", object_identity, | ||
result->GetError().c_str()); | ||
} | ||
} | ||
|
||
SPI_finish(); | ||
|
||
result = context.Query("COMMIT", false); | ||
if (result->HasError()) { | ||
elog(ERROR, "(PGDuckDB/duckdb_drop_table_trigger) Could not commit transaction"); | ||
} | ||
|
||
PG_RETURN_NULL(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.