-
Notifications
You must be signed in to change notification settings - Fork 56
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
Harden against Postgres interactions with C++ destructors #254
base: main
Are you sure you want to change the base?
Conversation
a71d446
to
692de3b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At quick glance, there are calls to get_namespace_oid() and get_relname_relid() in DuckDBManager::CheckSecretsSeq() that also seem unsafe.
Is there a general plan for how to sanity check that each function is handling errors correctly. If I understand correctly, some code runs in "Postgres regime", where you use elog(ERROR), memory context etc, while other code runs in "duckdb regime" where you use c++ exceptions etc. And whenever you cross the boundary, you must use PostgresFunctionGuard or DuckDBFunctionGuard.
Can we have some kind of compiler support or conventions or something to help with getting that right?
src/pgduckdb_duckdb.cpp
Outdated
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think DuckDBManager::DropSecrets needs a similar change
Thanks for the review @hlinnaka ! There are indeed a lot more places that requires attention. (I didn't realize the PR wasn't in draft - updated now) My goal is to get to a place where we:
What do you think about this approach? |
692de3b
to
2c0729c
Compare
Yep, that works. Greenplum's orca optimizer does something similar, see e.g. https://github.com/cloudberrydb/cloudberrydb/blob/main/src/backend/gpopt/gpdbwrappers.cpp. I think PostGIS too. |
f1286bf
to
9cad7d7
Compare
Moved this to the 0.2.0 milestone, to allow us to focus on stability testing for 0.1.0. We might still merge part of this PR for 0.1.0 where necessary to resolve stability issues we find during testing, but for now merging this wholesale so close to the release seems more risky than postponing it. We'll try to merge this (and related fixes) early in the 0.2.0 release cycle so it can bake longer. |
d73eaca
to
19c6e55
Compare
@@ -561,8 +558,9 @@ duckdb_alter_table_trigger(PG_FUNCTION_ARGS) { | |||
SetUserIdAndSecContext(saved_userid, sec_context); | |||
AtEOXact_GUC(false, save_nestlevel); | |||
|
|||
if (ret != SPI_OK_SELECT) | |||
elog(ERROR, "SPI_exec failed: error code %s", SPI_result_code_string(ret)); | |||
if (ret != SPI_OK_SELECT) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be converted to SPI_exec_or_throw
afaict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm don't we want to reset the user id etc (L558-559) before throwing? I thought about adding a version with a callback to execute before throwing but that felt overkill.
Datum is_temporary_datum = SPI_getbinval(tuple, SPI_tuptable->tupdesc, 2, &isnull); | ||
if (isnull) { | ||
elog(ERROR, "Expected temporary boolean to be returned, but found NULL"); | ||
throw std::runtime_error("Expected temporary boolean to be returned, but found NULL"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we discussed this in person, but I'll write it down here as well:
I'm not entirely convinced yet that changing all the elogs to exceptions in this file and in pgduckdb_ruleutils.cpp
is all that helpful. It doesn't hurt either functionally, because you've wrapped this fuction in an InvokeCPPFunc
call, and there's no PG_TRY
around them. So there is no problem with throwing exceptions now.
But I think these specific functions probably won't benefit from being rewritten to C++ completely, and would be easier to maintain in Postgres C style. Mainly because they call a lot of Postgres functions and very few DuckDB functions. Wrapping all of those Postgres calls in PostgresFunctionGuard
calls doesn't necessarily sound like it makes the code easier to read and maintain. I'm happily proven wrong though.
If you're fairly sure this is the right direction for these functions I'm fine with merging this now. But I think it should be a separate commit from most of the other changes (specifically the new and improved macros). To make reverting it easier if it turns out we want to make these functions fully Postgres C instead of fully C++.
4049046
to
bb35b1a
Compare
c652a04
to
feebb13
Compare
c7b9261
to
5731604
Compare
5731604
to
ad0a63c
Compare
Fixes #93
elog(ERROR...
which would bypass C++ dtors, and instead throw regular exceptionselog(ERROR, ...
form)