From 692de3bd3e00073b030c6217b32cb82ef1c92fdf Mon Sep 17 00:00:00 2001 From: Yves Date: Mon, 7 Oct 2024 15:37:34 +0200 Subject: [PATCH] Handle C++ exceptions in PG hooks --- include/pgduckdb/pgduckdb_duckdb.hpp | 13 ++- include/pgduckdb/pgduckdb_utils.hpp | 10 ++ src/catalog/pgduckdb_transaction.cpp | 4 +- src/pgduckdb_ddl.cpp | 6 +- src/pgduckdb_duckdb.cpp | 108 ++++++++---------- src/pgduckdb_hooks.cpp | 20 +++- src/pgduckdb_node.cpp | 40 +++++-- src/pgduckdb_options.cpp | 30 ++--- src/pgduckdb_planner.cpp | 2 +- src/pgduckdb_types.cpp | 7 +- src/utility/copy.cpp | 2 +- .../expected/array_type_support.out | 4 +- .../expected/transaction_errors.out | 3 + 13 files changed, 151 insertions(+), 98 deletions(-) diff --git a/include/pgduckdb/pgduckdb_duckdb.hpp b/include/pgduckdb/pgduckdb_duckdb.hpp index 637cd3e3..34bb9f18 100644 --- a/include/pgduckdb/pgduckdb_duckdb.hpp +++ b/include/pgduckdb/pgduckdb_duckdb.hpp @@ -15,13 +15,24 @@ class DuckDBManager { static inline DuckDBManager & Get() { static DuckDBManager instance; + if (!instance.database) { + instance.Initialize(); + } return instance; } - duckdb::unique_ptr GetConnection(); + static inline duckdb::DuckDB & + GetDatabase() { + return *Get().database; + } + static duckdb::unique_ptr + CreateConnection(); private: DuckDBManager(); + + void Initialize(); + void InitializeDatabase(); bool CheckSecretsSeq(); void LoadSecrets(duckdb::ClientContext &); diff --git a/include/pgduckdb/pgduckdb_utils.hpp b/include/pgduckdb/pgduckdb_utils.hpp index 0bed8365..84bab9b6 100644 --- a/include/pgduckdb/pgduckdb_utils.hpp +++ b/include/pgduckdb/pgduckdb_utils.hpp @@ -6,6 +6,7 @@ extern "C" { #include "duckdb/common/exception.hpp" #include "duckdb/common/error_data.hpp" +#include "pgduckdb/pgduckdb_duckdb.hpp" #include #include @@ -101,4 +102,13 @@ DuckDBFunctionGuard(FuncType duckdb_function, const char* function_name, FuncArg std::abort(); // Cannot reach. } +inline duckdb::unique_ptr +DuckDBQueryOrThrow(duckdb::ClientContext &context, const std::string &query) { + auto res = context.Query(query, false); + if (res->HasError()) { + res->ThrowError(); + } + return res; +} + } // namespace pgduckdb diff --git a/src/catalog/pgduckdb_transaction.cpp b/src/catalog/pgduckdb_transaction.cpp index 44722a76..3d6dbec4 100644 --- a/src/catalog/pgduckdb_transaction.cpp +++ b/src/catalog/pgduckdb_transaction.cpp @@ -42,6 +42,7 @@ SchemaItems::GetTable(const string &entry_name) { if (it != tables.end()) { return it->second.get(); } + auto snapshot = schema->snapshot; auto &catalog = schema->catalog; @@ -59,7 +60,7 @@ SchemaItems::GetTable(const string &entry_name) { // Check if the Relation is a VIEW auto tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(rel_oid)); if (!HeapTupleIsValid(tuple)) { - elog(ERROR, "Cache lookup failed for relation %u", rel_oid); + throw std::runtime_error("Cache lookup failed for relation " + std::to_string(rel_oid)); } auto relForm = (Form_pg_class)GETSTRUCT(tuple); @@ -71,6 +72,7 @@ SchemaItems::GetTable(const string &entry_name) { // will get bound again and hit a PostgresIndexTable / PostgresHeapTable. return nullptr; } + ReleaseSysCache(tuple); ::Relation rel = PostgresTable::OpenRelation(rel_oid); diff --git a/src/pgduckdb_ddl.cpp b/src/pgduckdb_ddl.cpp index be0ccca0..1f48e684 100644 --- a/src/pgduckdb_ddl.cpp +++ b/src/pgduckdb_ddl.cpp @@ -30,7 +30,7 @@ extern "C" { */ void DuckdbTruncateTable(Oid relation_oid) { - auto connection = pgduckdb::DuckDBManager::Get().GetConnection(); + auto connection = pgduckdb::DuckDBManager::CreateConnection(); auto &context = *connection->context; auto query = std::string("TRUNCATE ") + pgduckdb_relation_name(relation_oid); auto result = context.Query(query, false); @@ -162,7 +162,7 @@ duckdb_create_table_trigger(PG_FUNCTION_ARGS) { std::string query_string(pgduckdb_get_tabledef(relid)); - auto connection = pgduckdb::DuckDBManager::Get().GetConnection(); + auto connection = pgduckdb::DuckDBManager::CreateConnection(); auto &context = *connection->context; auto result = context.Query(query_string, false); if (result->HasError()) { @@ -230,7 +230,7 @@ duckdb_drop_table_trigger(PG_FUNCTION_ARGS) { */ PreventInTransactionBlock(true, "DuckDB queries"); - auto connection = pgduckdb::DuckDBManager::Get().GetConnection(); + auto connection = pgduckdb::DuckDBManager::CreateConnection(); auto &context = *connection->context; auto result = context.Query("BEGIN TRANSACTION", false); diff --git a/src/pgduckdb_duckdb.cpp b/src/pgduckdb_duckdb.cpp index c08f3e19..6b17af92 100644 --- a/src/pgduckdb_duckdb.cpp +++ b/src/pgduckdb_duckdb.cpp @@ -30,30 +30,35 @@ extern "C" { #include #include #include +#include namespace pgduckdb { static bool -CheckDataDirectory(const char *data_directory) { +CheckDataDirectory(const std::string &data_directory) { struct stat info; - if (lstat(data_directory, &info) != 0) { + std::ostringstream oss; + if (lstat(data_directory.c_str(), &info) != 0) { if (errno == ENOENT) { - elog(DEBUG2, "(PGDuckDB/CheckDataDirectory) Directory `%s` doesn't exists", data_directory); + elog(DEBUG2, "(PGDuckDB/CheckDataDirectory) Directory `%s` doesn't exists", data_directory.c_str()); return false; - } else if (errno == EACCES) { - elog(ERROR, "(PGDuckDB/CheckDataDirectory) Can't access `%s` directory", data_directory); - } else { - elog(ERROR, "(PGDuckDB/CheckDataDirectory) Other error when reading `%s`", data_directory); } - } - if (!S_ISDIR(info.st_mode)) { - elog(WARNING, "(PGDuckDB/CheckDataDirectory) `%s` is not directory", data_directory); - } + oss << "(PGDuckDB/CheckDataDirectory) "; + if (errno == EACCES) { + oss << "Can't access `" << data_directory << "` directory"; + } else { + oss << "Other error when reading `" << data_directory << "`: (" << errno << ") " << strerror(errno); + } - if (access(data_directory, R_OK | W_OK)) { - elog(ERROR, "(PGDuckDB/CheckDataDirectory) Directory `%s` permission problem", data_directory); + throw std::runtime_error(oss.str()); + } else if (!S_ISDIR(info.st_mode)) { + oss << "(PGDuckDB/CheckDataDirectory) `" << data_directory << "` is not directory"; + throw std::runtime_error(oss.str()); + } else if (access(data_directory.c_str(), R_OK | W_OK)) { + oss << "(PGDuckDB/CheckDataDirectory) Directory `" << data_directory << "` permission problem"; + throw std::runtime_error(oss.str()); } return true; @@ -61,27 +66,23 @@ CheckDataDirectory(const char *data_directory) { static std::string GetExtensionDirectory() { - StringInfo duckdb_extension_data_directory = makeStringInfo(); - appendStringInfo(duckdb_extension_data_directory, "%s/duckdb_extensions", DataDir); - - if (!CheckDataDirectory(duckdb_extension_data_directory->data)) { - if (mkdir(duckdb_extension_data_directory->data, S_IRWXU | S_IRWXG | S_IRWXO) == -1) { - int error = errno; - pfree(duckdb_extension_data_directory->data); - elog(ERROR, - "(PGDuckDB/GetExtensionDirectory) Creating duckdb extensions directory failed with reason `%s`\n", - strerror(error)); - } - elog(DEBUG2, "(PGDuckDB/GetExtensionDirectory) Created %s as `duckdb.data_dir`", - duckdb_extension_data_directory->data); - }; + std::ostringstream oss; + oss << DataDir << "/duckdb_extensions"; + std::string data_directory = oss.str(); + + if (!CheckDataDirectory(data_directory)) { + std::filesystem::create_directories(data_directory); + elog(DEBUG2, "(PGDuckDB/GetExtensionDirectory) Created %s as `duckdb.data_dir`", data_directory.c_str()); + } - std::string duckdb_extension_directory(duckdb_extension_data_directory->data); - pfree(duckdb_extension_data_directory->data); - return duckdb_extension_directory; + return data_directory; } DuckDBManager::DuckDBManager() : secret_table_num_rows(0), secret_table_current_seq(0) { +} + +void +DuckDBManager::Initialize() { elog(DEBUG2, "(PGDuckDB/DuckDBManager) Creating DuckDB instance"); duckdb::DBConfig config; @@ -135,34 +136,30 @@ DuckDBManager::LoadSecrets(duckdb::ClientContext &context) { int secret_id = 0; for (auto &secret : duckdb_secrets) { - StringInfo secret_key = makeStringInfo(); + std::ostringstream query; bool is_r2_cloud_secret = (secret.type.rfind("R2", 0) == 0); - 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()); + query << "CREATE SECRET pgduckb_secret_" << std::to_string(secret_id) << " "; + query << "(TYPE " << secret.type << ", KEY_ID '" << secret.id << "', SECRET '" << secret.secret << "'"; if (secret.region.length() && !is_r2_cloud_secret) { - appendStringInfo(secret_key, ", REGION '%s'", secret.region.c_str()); + query << ", REGION '" << secret.region << "'"; } if (secret.session_token.length() && !is_r2_cloud_secret) { - appendStringInfo(secret_key, ", SESSION_TOKEN '%s'", secret.session_token.c_str()); + query << ", SESSION_TOKEN '" << secret.session_token << "'"; } if (secret.endpoint.length() && !is_r2_cloud_secret) { - appendStringInfo(secret_key, ", ENDPOINT '%s'", secret.endpoint.c_str()); + query << ", ENDPOINT '" << secret.endpoint << "'"; } if (is_r2_cloud_secret) { - appendStringInfo(secret_key, ", ACCOUNT_ID '%s'", secret.endpoint.c_str()); + query << ", ACCOUNT_ID '" << secret.endpoint << "'"; } if (!secret.use_ssl) { - appendStringInfo(secret_key, ", USE_SSL 'FALSE'"); - } - appendStringInfo(secret_key, ");"); - auto res = context.Query(secret_key->data, false); - if (res->HasError()) { - elog(ERROR, "(PGDuckDB/LoadSecrets) secret `%s` could not be loaded with DuckDB", secret.id.c_str()); + query << ", USE_SSL 'FALSE'"; } + query << ");"; + + DuckDBQueryOrThrow(context, query.str()); - pfree(secret_key->data); - secret_id++; + ++secret_id; } secret_table_num_rows = secret_id; @@ -186,25 +183,20 @@ DuckDBManager::LoadExtensions(duckdb::ClientContext &context) { auto duckdb_extensions = ReadDuckdbExtensions(); for (auto &extension : duckdb_extensions) { - StringInfo duckdb_extension = makeStringInfo(); if (extension.enabled) { - appendStringInfo(duckdb_extension, "LOAD %s;", extension.name.c_str()); - auto res = context.Query(duckdb_extension->data, false); - if (res->HasError()) { - elog(ERROR, "(PGDuckDB/LoadExtensions) `%s` could not be loaded with DuckDB", extension.name.c_str()); - } + DuckDBQueryOrThrow(context, "LOAD " + extension.name); } - pfree(duckdb_extension->data); } } duckdb::unique_ptr -DuckDBManager::GetConnection() { - auto connection = duckdb::make_uniq(*database); - if (CheckSecretsSeq()) { +DuckDBManager::CreateConnection() { + auto& instance = Get(); + auto connection = duckdb::make_uniq(GetDatabase()); + if (instance.CheckSecretsSeq()) { auto &context = *connection->context; - DropSecrets(context); - LoadSecrets(context); + instance.DropSecrets(context); + instance.LoadSecrets(context); } return connection; } diff --git a/src/pgduckdb_hooks.cpp b/src/pgduckdb_hooks.cpp index 7fbf6e4d..184d2ffd 100644 --- a/src/pgduckdb_hooks.cpp +++ b/src/pgduckdb_hooks.cpp @@ -23,6 +23,7 @@ extern "C" { #include "pgduckdb/utility/copy.hpp" #include "pgduckdb/vendor/pg_explain.hpp" #include "pgduckdb/vendor/pg_list.hpp" +#include "pgduckdb/pgduckdb_utils.hpp" static planner_hook_type prev_planner_hook = NULL; static ProcessUtility_hook_type prev_process_utility_hook = NULL; @@ -164,7 +165,7 @@ IsAllowedStatement(Query *query, bool throw_error = false) { } static PlannedStmt * -DuckdbPlannerHook(Query *parse, const char *query_string, int cursor_options, ParamListInfo bound_params) { +DuckdbPlannerHook_Unsafe(Query *parse, const char *query_string, int cursor_options, ParamListInfo bound_params) { if (pgduckdb::IsExtensionRegistered()) { if (duckdb_execution && IsAllowedStatement(parse)) { PlannedStmt *duckdbPlan = DuckdbPlanNode(parse, cursor_options); @@ -190,9 +191,15 @@ DuckdbPlannerHook(Query *parse, const char *query_string, int cursor_options, Pa } } +static PlannedStmt * +DuckdbPlannerHook(Query *parse, const char *query_string, int cursor_options, ParamListInfo bound_params) { + return pgduckdb::DuckDBFunctionGuard(DuckdbPlannerHook_Unsafe, __FUNCTION__, parse, query_string, cursor_options, bound_params); +} + static void -DuckdbUtilityHook(PlannedStmt *pstmt, const char *query_string, bool read_only_tree, ProcessUtilityContext context, - ParamListInfo params, struct QueryEnvironment *query_env, DestReceiver *dest, QueryCompletion *qc) { +DuckdbUtilityHook_Unsafe(PlannedStmt *pstmt, const char *query_string, bool read_only_tree, + ProcessUtilityContext context, ParamListInfo params, struct QueryEnvironment *query_env, + DestReceiver *dest, QueryCompletion *qc) { Node *parsetree = pstmt->utilityStmt; if (duckdb_execution && pgduckdb::IsExtensionRegistered() && IsA(parsetree, CopyStmt)) { uint64 processed; @@ -215,6 +222,13 @@ DuckdbUtilityHook(PlannedStmt *pstmt, const char *query_string, bool read_only_t } } +static void +DuckdbUtilityHook(PlannedStmt *pstmt, const char *query_string, bool read_only_tree, ProcessUtilityContext context, + ParamListInfo params, struct QueryEnvironment *query_env, DestReceiver *dest, QueryCompletion *qc) { + pgduckdb::DuckDBFunctionGuard(DuckdbUtilityHook_Unsafe, __FUNCTION__, pstmt, query_string, read_only_tree, context, params, + query_env, dest, qc); +} + extern "C" { #include "nodes/print.h" } diff --git a/src/pgduckdb_node.cpp b/src/pgduckdb_node.cpp index 6f33e3c3..c25ef3b2 100644 --- a/src/pgduckdb_node.cpp +++ b/src/pgduckdb_node.cpp @@ -1,5 +1,6 @@ #include "duckdb.hpp" +#include "duckdb/common/exception/conversion_exception.hpp" extern "C" { #include "postgres.h" @@ -73,14 +74,16 @@ Duckdb_CreateCustomScanState(CustomScan *cscan) { } void -Duckdb_BeginCustomScan(CustomScanState *cscanstate, EState *estate, int eflags) { +Duckdb_BeginCustomScan_Unsafe(CustomScanState *cscanstate, EState *estate, int eflags) { DuckdbScanState *duckdb_scan_state = (DuckdbScanState *)cscanstate; auto prepare_result = DuckdbPrepare(duckdb_scan_state->query); auto prepared_query = std::move(std::get<0>(prepare_result)); auto duckdb_connection = std::move(std::get<1>(prepare_result)); if (prepared_query->HasError()) { - elog(ERROR, "DuckDB re-planning failed %s", prepared_query->GetError().c_str()); + std::ostringstream oss; + oss << "DuckDB re-planning failed " << prepared_query->GetError(); + throw duckdb::Exception(duckdb::ExceptionType::EXECUTOR, oss.str().c_str()); } duckdb_scan_state->duckdb_connection = duckdb_connection.release(); @@ -92,6 +95,11 @@ Duckdb_BeginCustomScan(CustomScanState *cscanstate, EState *estate, int eflags) HOLD_CANCEL_INTERRUPTS(); } +void +Duckdb_BeginCustomScan(CustomScanState *cscanstate, EState *estate, int eflags) { + pgduckdb::DuckDBFunctionGuard(Duckdb_BeginCustomScan_Unsafe, __FUNCTION__, cscanstate, estate, eflags); +} + static void ExecuteQuery(DuckdbScanState *state) { auto &prepared = *state->prepared_statement; @@ -153,14 +161,14 @@ ExecuteQuery(DuckdbScanState *state) { } static TupleTableSlot * -Duckdb_ExecCustomScan(CustomScanState *node) { +Duckdb_ExecCustomScan_Unsafe(CustomScanState *node) { DuckdbScanState *duckdb_scan_state = (DuckdbScanState *)node; TupleTableSlot *slot = duckdb_scan_state->css.ss.ss_ScanTupleSlot; MemoryContext old_context; bool already_executed = duckdb_scan_state->is_executed; if (!already_executed) { - pgduckdb::DuckDBFunctionGuard(ExecuteQuery, "ExecuteQuery", duckdb_scan_state); + ExecuteQuery(duckdb_scan_state); } if (duckdb_scan_state->fetch_next) { @@ -189,7 +197,7 @@ Duckdb_ExecCustomScan(CustomScanState *node) { slot->tts_isnull[col] = false; if (!pgduckdb::ConvertDuckToPostgresValue(slot, value, col)) { CleanupDuckdbScanState(duckdb_scan_state); - elog(ERROR, "(PGDuckDB/Duckdb_ExecCustomScan) Value conversion failed"); + throw duckdb::ConversionException("Value conversion failed"); } } } @@ -206,26 +214,37 @@ Duckdb_ExecCustomScan(CustomScanState *node) { return slot; } +static TupleTableSlot * +Duckdb_ExecCustomScan(CustomScanState *node) { + return pgduckdb::DuckDBFunctionGuard(Duckdb_ExecCustomScan_Unsafe, __FUNCTION__, node); +} + void -Duckdb_EndCustomScan(CustomScanState *node) { +Duckdb_EndCustomScan_Unsafe(CustomScanState *node) { DuckdbScanState *duckdb_scan_state = (DuckdbScanState *)node; CleanupDuckdbScanState(duckdb_scan_state); RESUME_CANCEL_INTERRUPTS(); } +void +Duckdb_EndCustomScan(CustomScanState *node) { + pgduckdb::DuckDBFunctionGuard(Duckdb_EndCustomScan_Unsafe, __FUNCTION__, node); +} + void Duckdb_ReScanCustomScan(CustomScanState *node) { } void -Duckdb_ExplainCustomScan(CustomScanState *node, List *ancestors, ExplainState *es) { +Duckdb_ExplainCustomScan_Unsafe(CustomScanState *node, List *ancestors, ExplainState *es) { DuckdbScanState *duckdb_scan_state = (DuckdbScanState *)node; - pgduckdb::DuckDBFunctionGuard(ExecuteQuery, "ExecuteQuery", duckdb_scan_state); + ExecuteQuery(duckdb_scan_state); auto chunk = duckdb_scan_state->query_results->Fetch(); if (!chunk || chunk->size() == 0) { return; } + /* Is it safe to hardcode this as result of DuckDB explain? */ auto value = chunk->GetValue(1, 0); std::string explain_output = "\n\n"; @@ -234,6 +253,11 @@ Duckdb_ExplainCustomScan(CustomScanState *node, List *ancestors, ExplainState *e ExplainPropertyText("DuckDB Execution Plan", explain_output.c_str(), es); } +void +Duckdb_ExplainCustomScan(CustomScanState *node, List *ancestors, ExplainState *es) { + pgduckdb::DuckDBFunctionGuard(Duckdb_ExplainCustomScan_Unsafe, __FUNCTION__, node, ancestors, es); +} + extern "C" void DuckdbInitNode() { /* setup scan methods */ diff --git a/src/pgduckdb_options.cpp b/src/pgduckdb_options.cpp index 00216f8a..d7fe3cc3 100644 --- a/src/pgduckdb_options.cpp +++ b/src/pgduckdb_options.cpp @@ -17,6 +17,7 @@ extern "C" { #include "pgduckdb/pgduckdb_options.hpp" #include "pgduckdb/pgduckdb_duckdb.hpp" +#include "pgduckdb/pgduckdb_utils.hpp" namespace pgduckdb { @@ -115,18 +116,10 @@ ReadDuckdbExtensions() { static bool DuckdbInstallExtension(Datum name) { - auto connection = pgduckdb::DuckDBManager::Get().GetConnection(); - auto &context = *connection->context; - + auto connection = DuckDBManager::CreateConnection(); auto extension_name = DatumToString(name); - StringInfo install_extension_command = makeStringInfo(); - appendStringInfo(install_extension_command, "INSTALL %s;", extension_name.c_str()); - - auto res = context.Query(install_extension_command->data, false); - - pfree(install_extension_command->data); - + auto res = connection->context->Query("INSTALL " + extension_name, false); if (res->HasError()) { elog(WARNING, "(PGDuckDB/DuckdbInstallExtension) %s", res->GetError().c_str()); return false; @@ -160,21 +153,22 @@ PG_FUNCTION_INFO_V1(install_extension); Datum install_extension(PG_FUNCTION_ARGS) { Datum extension_name = PG_GETARG_DATUM(0); - bool result = pgduckdb::DuckdbInstallExtension(extension_name); + bool result = pgduckdb::DuckDBFunctionGuard(pgduckdb::DuckdbInstallExtension, __FUNCTION__, extension_name); PG_RETURN_BOOL(result); } +const char* pgduckdb_raw_query_unsafe(const char *query) { + auto connection = pgduckdb::DuckDBManager::CreateConnection(); + auto result = pgduckdb::DuckDBQueryOrThrow(*connection->context, query); + return strdup(result->ToString().c_str()); +} + PG_FUNCTION_INFO_V1(pgduckdb_raw_query); Datum pgduckdb_raw_query(PG_FUNCTION_ARGS) { const char *query = text_to_cstring(PG_GETARG_TEXT_PP(0)); - auto connection = pgduckdb::DuckDBManager::Get().GetConnection(); - auto &context = *connection->context; - auto result = context.Query(query, false); - if (result->HasError()) { - elog(ERROR, "(PGDuckDB/DuckdbInstallExtension) %s", result->GetError().c_str()); - } - elog(NOTICE, "result: %s", result->ToString().c_str()); + auto res = pgduckdb::DuckDBFunctionGuard(pgduckdb_raw_query_unsafe, __FUNCTION__, query); + elog(NOTICE, "result: %s", res); PG_RETURN_BOOL(true); } diff --git a/src/pgduckdb_planner.cpp b/src/pgduckdb_planner.cpp index 8485139d..b335a04c 100644 --- a/src/pgduckdb_planner.cpp +++ b/src/pgduckdb_planner.cpp @@ -46,7 +46,7 @@ DuckdbPrepare(const Query *query) { elog(DEBUG2, "(PGDuckDB/DuckdbPrepare) Preparing: %s", query_string); - auto duckdb_connection = pgduckdb::DuckDBManager::Get().GetConnection(); + auto duckdb_connection = pgduckdb::DuckDBManager::CreateConnection(); auto context = duckdb_connection->context; auto prepared_query = context->Prepare(query_string); return {std::move(prepared_query), std::move(duckdb_connection)}; diff --git a/src/pgduckdb_types.cpp b/src/pgduckdb_types.cpp index 9d3a005e..a70f8321 100644 --- a/src/pgduckdb_types.cpp +++ b/src/pgduckdb_types.cpp @@ -747,8 +747,11 @@ ConvertPostgresParameterToDuckValue(Datum value, Oid postgres_type) { case FLOAT8OID: { return duckdb::Value::DOUBLE(DatumGetFloat8(value)); } - default: - elog(ERROR, "Could not convert Postgres parameter of type: %d to DuckDB type", postgres_type); + default: { + std::ostringstream oss; + oss << "Could not convert Postgres parameter of type: " << postgres_type << " to DuckDB type"; + throw duckdb::NotImplementedException(oss.str()); + } } } diff --git a/src/utility/copy.cpp b/src/utility/copy.cpp index c1482d6c..c9b878ba 100644 --- a/src/utility/copy.cpp +++ b/src/utility/copy.cpp @@ -43,7 +43,7 @@ DuckdbCopy(PlannedStmt *pstmt, const char *query_string, struct QueryEnvironment return false; } - auto duckdb_connection = pgduckdb::DuckDBManager::Get().GetConnection(); + auto duckdb_connection = pgduckdb::DuckDBManager::CreateConnection(); auto res = duckdb_connection->context->Query(query_string, false); if (res->HasError()) { diff --git a/test/regression/expected/array_type_support.out b/test/regression/expected/array_type_support.out index 8ac36fd9..f9f43bd2 100644 --- a/test/regression/expected/array_type_support.out +++ b/test/regression/expected/array_type_support.out @@ -34,7 +34,7 @@ INSERT INTO int_array_2d VALUES ('{{11, 12, 13}, {14, 15, 16}}'), ('{{17, 18}, {19, 20}}'); SELECT * FROM int_array_2d; -ERROR: (PGDuckDB/ExecuteQuery) Invalid Input Error: Dimensionality of the schema and the data does not match, data contains more dimensions than the amount of dimensions specified by the schema +ERROR: (PGDuckDB/Duckdb_ExecCustomScan) Invalid Input Error: Dimensionality of the schema and the data does not match, data contains more dimensions than the amount of dimensions specified by the schema drop table int_array_2d; -- INT4 (single dimensional data, two dimensionsal type) CREATE TABLE int_array_2d(a INT[][]); @@ -44,7 +44,7 @@ INSERT INTO int_array_2d VALUES ('{11, 12, 13}'), ('{17, 18}'); SELECT * FROM int_array_2d; -ERROR: (PGDuckDB/ExecuteQuery) Invalid Input Error: Dimensionality of the schema and the data does not match, data contains fewer dimensions than the amount of dimensions specified by the schema +ERROR: (PGDuckDB/Duckdb_ExecCustomScan) Invalid Input Error: Dimensionality of the schema and the data does not match, data contains fewer dimensions than the amount of dimensions specified by the schema drop table int_array_2d; -- INT4 (two dimensional data and type) CREATE TABLE int_array_2d(a INT[][]); diff --git a/test/regression/expected/transaction_errors.out b/test/regression/expected/transaction_errors.out index 94cad122..a4c308b1 100644 --- a/test/regression/expected/transaction_errors.out +++ b/test/regression/expected/transaction_errors.out @@ -1,6 +1,9 @@ CREATE TABLE foo AS SELECT 'bar' AS t; BEGIN; SET duckdb.execution = true; SELECT t::integer AS t1 FROM foo; ROLLBACK; ERROR: invalid input syntax for type integer: "bar" +ERROR: (PGDuckDB/Duckdb_ExecCustomScan) Conversion Error: Could not convert string 'bar' to INT32 +LINE 1: SELECT (t)::integer AS t1 FROM public.foo + ^ SET duckdb.execution = true; SELECT 1 FROM foo; ?column?