-
Notifications
You must be signed in to change notification settings - Fork 48
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
Handle errors in execution #176
Changes from all commits
490361c
29aa8c9
12f2ba1
e5768d5
466d77e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,15 @@ | ||
#pragma once | ||
|
||
extern "C" { | ||
#include "postgres.h" | ||
} | ||
|
||
#include "duckdb/common/exception.hpp" | ||
|
||
#include <vector> | ||
#include <string> | ||
#include <sstream> | ||
|
||
#include <cstdio> | ||
|
||
namespace pgduckdb { | ||
|
||
inline std::vector<std::string> | ||
|
@@ -19,4 +23,59 @@ TokenizeString(char *str, const char delimiter) { | |
return v; | ||
}; | ||
|
||
/* | ||
* DuckdbGlobalLock should be held before calling. | ||
*/ | ||
template <typename T, typename FuncType, typename... FuncArgs> | ||
T | ||
PostgresFunctionGuard(FuncType postgres_function, FuncArgs... args) { | ||
T return_value; | ||
bool error = false; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs to be marked There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually it seems this is probably not necessary. I was thinking about the problem described here, but that does not apply since |
||
MemoryContext ctx = CurrentMemoryContext; | ||
ErrorData *edata = nullptr; | ||
// clang-format off | ||
PG_TRY(); | ||
{ | ||
return_value = postgres_function(args...); | ||
} | ||
PG_CATCH(); | ||
{ | ||
MemoryContextSwitchTo(ctx); | ||
edata = CopyErrorData(); | ||
FlushErrorState(); | ||
error = true; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like it would probably be better to throw an exception here already. That way we can preserve the original Postgres error message for much better debugging. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure is it correct to throw from this. I would prefer that this function outputs error message but still caller needs to handle this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do you mean with "correct" in this case? Do you mean is it allowed to throw an exception in PG_CATCH? In which case, I think it's fine, but indeed I'm also not 100% sure. But if you throw an exception after the PG_CATCH if Or do you mean is it what the caller should do? I think in at least 90% of the cases the caller will simply want to propagate the error message in the form of an exception. In the last 10% maybe they want more control, in which case returning the error message is maybe indeed what we would want. But I think we should make it as easy as possible to write correct code, so I think we should also have a helper that simply throws an exception. And honestly, maybe throwing an exception in all cases might be fine too, since a caller can always There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @JelteF guard function will now throw InternalException with error message constructed from postgresql log |
||
} | ||
PG_END_TRY(); | ||
// clang-format on | ||
if (error) { | ||
throw duckdb::Exception(duckdb::ExceptionType::EXECUTOR, edata->message); | ||
} | ||
return return_value; | ||
} | ||
|
||
template <typename FuncType, typename... FuncArgs> | ||
void | ||
PostgresFunctionGuard(FuncType postgres_function, FuncArgs... args) { | ||
bool error = false; | ||
MemoryContext ctx = CurrentMemoryContext; | ||
ErrorData *edata = nullptr; | ||
// clang-format off | ||
PG_TRY(); | ||
{ | ||
postgres_function(args...); | ||
} | ||
PG_CATCH(); | ||
{ | ||
MemoryContextSwitchTo(ctx); | ||
edata = CopyErrorData(); | ||
FlushErrorState(); | ||
error = true; | ||
} | ||
PG_END_TRY(); | ||
// clang-format on | ||
if (error) { | ||
throw duckdb::Exception(duckdb::ExceptionType::EXECUTOR, edata->message); | ||
} | ||
} | ||
|
||
} // namespace pgduckdb |
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 should have a similar function for the other way around, i.e. where we call DuckDB C++ APIs from Postgres C code.