Skip to content
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
5 changes: 3 additions & 2 deletions src/backend/gpopt/CGPOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,17 @@ CGPOptimizer::GPOPTOptimizedPlan(
GPOS_CATCH_EX(ex)
{
// clone the error message before context free.
BOOL clone_failed = false;
CHAR *serialized_error_msg =
gpopt_context.CloneErrorMsg(MessageContext);
gpopt_context.CloneErrorMsg(MessageContext, &clone_failed);
// clean up context
gpopt_context.Free(gpopt_context.epinQuery, gpopt_context.epinPlStmt);

// Special handler for a few common user-facing errors. In particular,
// we want to use the correct error code for these, in case an application
// tries to do something smart with them.

if (GPOS_MATCH_EX(ex, gpdxl::ExmaGPDB, gpdxl::ExmiGPDBError))
if (clone_failed || GPOS_MATCH_EX(ex, gpdxl::ExmaGPDB, gpdxl::ExmiGPDBError))
{
PG_RE_THROW();
}
Expand Down
25 changes: 23 additions & 2 deletions src/backend/gpopt/utils/COptTasks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,34 @@ SOptContext::Free(SOptContext::EPin input, SOptContext::EPin output) const
//
//---------------------------------------------------------------------------
CHAR *
SOptContext::CloneErrorMsg(MemoryContext context) const
SOptContext::CloneErrorMsg(MemoryContext context, BOOL *clone_failed) const
{
*clone_failed = false;

if (nullptr == context || nullptr == m_error_msg)
{
return nullptr;
}
return gpdb::MemCtxtStrdup(context, m_error_msg);

CHAR *error_msg;
GPOS_TRY
{
#ifdef FAULT_INJECTOR
if (gpdb::InjectFaultInOptTasks("opt_clone_error_msg") == FaultInjectorTypeSkip)
{
GpdbEreport(ERRCODE_INTERNAL_ERROR, ERROR, "Injected error", nullptr);
}
#endif
error_msg = gpdb::MemCtxtStrdup(context, m_error_msg);
}
GPOS_CATCH_EX(ex)
{
error_msg = nullptr;
*clone_failed = true;
}
GPOS_CATCH_END;

return error_msg;
}


Expand Down
2 changes: 1 addition & 1 deletion src/include/gpopt/utils/COptTasks.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ struct SOptContext
void Free(EPin input, EPin epinOutput) const;

// Clone the error message in given context.
CHAR *CloneErrorMsg(struct MemoryContextData *context) const;
CHAR *CloneErrorMsg(struct MemoryContextData *context, BOOL *clone_failed) const;

// casting function
static SOptContext *Cast(void *ptr);
Expand Down
25 changes: 25 additions & 0 deletions src/test/regress/expected/gporca_faults.out
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,28 @@ SELECT gp_inject_fault('opt_relcache_translator_catalog_access', 'reset', 1);
Success:
(1 row)

-- Test to check that GPOPTOptimizedPlan() does not cause std::terminate() by throwing an uncaught exception.
CREATE TABLE test_orca_uncaught_exc(a int, b int) DISTRIBUTED RANDOMLY;
-- Since ORCA cannot optimize this query, an exception is generated. We then inject a second exception when the
-- first is caught and verify that it is not propagated further (no std::terminate() is called and backend is alive).
SELECT gp_inject_fault('opt_clone_error_msg', 'skip', 1);
gp_inject_fault
-----------------
Success:
(1 row)

SELECT sum(distinct a), count(distinct b) FROM test_orca_uncaught_exc;
sum | count
-----+-------
| 0
(1 row)

SELECT gp_inject_fault('opt_clone_error_msg', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)

-- start_ignore
DROP TABLE test_orca_uncaught_exc;
-- end_ignore
21 changes: 21 additions & 0 deletions src/test/regress/expected/gporca_faults_optimizer.out
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,24 @@ SELECT gp_inject_fault('opt_relcache_translator_catalog_access', 'reset', 1);
Success:
(1 row)

-- Test to check that GPOPTOptimizedPlan() does not cause std::terminate() by throwing an uncaught exception.
CREATE TABLE test_orca_uncaught_exc(a int, b int) DISTRIBUTED RANDOMLY;
-- Since ORCA cannot optimize this query, an exception is generated. We then inject a second exception when the
-- first is caught and verify that it is not propagated further (no std::terminate() is called and backend is alive).
SELECT gp_inject_fault('opt_clone_error_msg', 'skip', 1);
gp_inject_fault
-----------------
Success:
(1 row)

SELECT sum(distinct a), count(distinct b) FROM test_orca_uncaught_exc;
ERROR: Injected error (COptTasks.cpp:180)
SELECT gp_inject_fault('opt_clone_error_msg', 'reset', 1);
gp_inject_fault
-----------------
Success:
(1 row)

-- start_ignore
DROP TABLE test_orca_uncaught_exc;
-- end_ignore
12 changes: 12 additions & 0 deletions src/test/regress/sql/gporca_faults.sql
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,15 @@ SELECT * FROM func1_nosql_vol(5), foo;

-- The fault should *not* be hit above when optimizer = off, to reset it now.
SELECT gp_inject_fault('opt_relcache_translator_catalog_access', 'reset', 1);

-- Test to check that GPOPTOptimizedPlan() does not cause std::terminate() by throwing an uncaught exception.
CREATE TABLE test_orca_uncaught_exc(a int, b int) DISTRIBUTED RANDOMLY;
-- Since ORCA cannot optimize this query, an exception is generated. We then inject a second exception when the
-- first is caught and verify that it is not propagated further (no std::terminate() is called and backend is alive).
SELECT gp_inject_fault('opt_clone_error_msg', 'skip', 1);
SELECT sum(distinct a), count(distinct b) FROM test_orca_uncaught_exc;
SELECT gp_inject_fault('opt_clone_error_msg', 'reset', 1);

-- start_ignore
DROP TABLE test_orca_uncaught_exc;
-- end_ignore
Loading