Skip to content

Commit 2a7822b

Browse files
committed
fix(database_postgresql): refactor error handling in PostgreSQL database functions to always call PG_END_TRY, this fix a SIGSEGV error for corrupted stack
1 parent 780fe67 commit 2a7822b

1 file changed

Lines changed: 89 additions & 70 deletions

File tree

src/postgresql/database_postgresql.c

Lines changed: 89 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -475,20 +475,23 @@ int database_exec (cloudsync_context *data, const char *sql) {
475475
cloudsync_reset_error(data);
476476

477477
int rc;
478+
bool is_error = false;
478479
PG_TRY();
479480
{
480481
rc = SPI_execute(sql, false, 0);
481482
}
482483
PG_CATCH();
483484
{
484485
ErrorData *edata = CopyErrorData();
485-
int err = cloudsync_set_error(data, edata->message, DBRES_ERROR);
486+
rc = cloudsync_set_error(data, edata->message, DBRES_ERROR);
486487
FreeErrorData(edata);
487488
FlushErrorState();
488-
return err;
489+
is_error = true;
489490
}
490491
PG_END_TRY();
491492

493+
if (is_error) return rc;
494+
492495
// Increment command counter to make changes visible
493496
if (rc >= 0) {
494497
CommandCounterIncrement();
@@ -513,20 +516,23 @@ int database_exec_callback (cloudsync_context *data, const char *sql, int (*call
513516
cloudsync_reset_error(data);
514517

515518
int rc;
519+
bool is_error = false;
516520
PG_TRY();
517521
{
518522
rc = SPI_execute(sql, true, 0);
519523
}
520524
PG_CATCH();
521525
{
522526
ErrorData *edata = CopyErrorData();
523-
int err = cloudsync_set_error(data, edata->message, DBRES_ERROR);
527+
rc = cloudsync_set_error(data, edata->message, DBRES_ERROR);
524528
FreeErrorData(edata);
525529
FlushErrorState();
526-
return err;
530+
is_error = true;
531+
527532
}
528533
PG_END_TRY();
529534

535+
if (is_error) return rc;
530536
if (rc < 0) return cloudsync_set_error(data, "SPI_execute failed", DBRES_ERROR);
531537

532538
// Call callback for each row if provided
@@ -1049,73 +1055,81 @@ int databasevm_step (dbvm_t *vm) {
10491055
}
10501056
if (!stmt->plan_is_prepared || !stmt->plan) return DBRES_ERROR;
10511057

1058+
int rc = DBRES_DONE;
10521059
PG_TRY();
10531060
{
1054-
// if portal is open, we fetch one row
1055-
if (stmt->portal_open) {
1056-
// free prior fetched row batch
1057-
clear_fetch_batch(stmt);
1058-
1059-
SPI_cursor_fetch(stmt->portal, true /* forward */, 1);
1060-
1061-
if (SPI_processed == 0) {
1062-
// done
1061+
do {
1062+
// if portal is open, we fetch one row
1063+
if (stmt->portal_open) {
1064+
// free prior fetched row batch
10631065
clear_fetch_batch(stmt);
1064-
close_portal(stmt);
1065-
return DBRES_DONE;
1066+
1067+
SPI_cursor_fetch(stmt->portal, true /* forward */, 1);
1068+
1069+
if (SPI_processed == 0) {
1070+
// done
1071+
clear_fetch_batch(stmt);
1072+
close_portal(stmt);
1073+
rc = DBRES_DONE;
1074+
break;
1075+
}
1076+
1077+
MemoryContextReset(stmt->row_mcxt);
1078+
1079+
stmt->last_tuptable = SPI_tuptable;
1080+
stmt->current_tupdesc = stmt->last_tuptable->tupdesc;
1081+
stmt->current_tuple = stmt->last_tuptable->vals[0];
1082+
rc = DBRES_ROW;
1083+
break;
10661084
}
10671085

1068-
MemoryContextReset(stmt->row_mcxt);
1069-
1070-
stmt->last_tuptable = SPI_tuptable;
1071-
stmt->current_tupdesc = stmt->last_tuptable->tupdesc;
1072-
stmt->current_tuple = stmt->last_tuptable->vals[0];
1073-
return DBRES_ROW;
1074-
}
1075-
1076-
// First step: decide whether to use portal.
1077-
// Even for INSERT/UPDATE/DELETE ... RETURNING you WANT a portal.
1078-
// Strategy:
1079-
// - Only open a cursor if the plan supports it (avoid "cannot open INSERT query as cursor").
1080-
// - Otherwise execute once as a non-row-returning statement.
1081-
if (!stmt->executed_nonselect) {
1082-
if (SPI_is_cursor_plan(stmt->plan)) {
1083-
// try cursor open
1084-
stmt->portal = NULL;
1085-
if (stmt->nparams == 0) stmt->portal = SPI_cursor_open(NULL, stmt->plan, NULL, NULL, false);
1086-
else stmt->portal = SPI_cursor_open(NULL, stmt->plan, stmt->values, stmt->nulls, false);
1087-
1088-
if (stmt->portal != NULL) {
1089-
stmt->portal_open = true;
1090-
1091-
// fetch first row
1092-
clear_fetch_batch(stmt);
1093-
SPI_cursor_fetch(stmt->portal, true, 1);
1094-
1095-
if (SPI_processed == 0) {
1086+
// First step: decide whether to use portal.
1087+
// Even for INSERT/UPDATE/DELETE ... RETURNING you WANT a portal.
1088+
// Strategy:
1089+
// - Only open a cursor if the plan supports it (avoid "cannot open INSERT query as cursor").
1090+
// - Otherwise execute once as a non-row-returning statement.
1091+
if (!stmt->executed_nonselect) {
1092+
if (SPI_is_cursor_plan(stmt->plan)) {
1093+
// try cursor open
1094+
stmt->portal = NULL;
1095+
if (stmt->nparams == 0) stmt->portal = SPI_cursor_open(NULL, stmt->plan, NULL, NULL, false);
1096+
else stmt->portal = SPI_cursor_open(NULL, stmt->plan, stmt->values, stmt->nulls, false);
1097+
1098+
if (stmt->portal != NULL) {
1099+
stmt->portal_open = true;
1100+
1101+
// fetch first row
10961102
clear_fetch_batch(stmt);
1097-
close_portal(stmt);
1098-
return DBRES_DONE;
1103+
SPI_cursor_fetch(stmt->portal, true, 1);
1104+
1105+
if (SPI_processed == 0) {
1106+
clear_fetch_batch(stmt);
1107+
close_portal(stmt);
1108+
rc = DBRES_DONE;
1109+
break;
1110+
}
1111+
1112+
MemoryContextReset(stmt->row_mcxt);
1113+
1114+
stmt->last_tuptable = SPI_tuptable;
1115+
stmt->current_tupdesc = stmt->last_tuptable->tupdesc;
1116+
stmt->current_tuple = stmt->last_tuptable->vals[0];
1117+
rc = DBRES_ROW;
1118+
break;
10991119
}
1100-
1101-
MemoryContextReset(stmt->row_mcxt);
1102-
1103-
stmt->last_tuptable = SPI_tuptable;
1104-
stmt->current_tupdesc = stmt->last_tuptable->tupdesc;
1105-
stmt->current_tuple = stmt->last_tuptable->vals[0];
1106-
return DBRES_ROW;
11071120
}
1108-
}
11091121

1110-
// Execute once (non-row-returning or cursor open failed).
1111-
if (stmt->nparams == 0) SPI_execute_plan(stmt->plan, NULL, NULL, false, 0);
1112-
else SPI_execute_plan(stmt->plan, stmt->values, stmt->nulls, false, 0);
1122+
// Execute once (non-row-returning or cursor open failed).
1123+
if (stmt->nparams == 0) SPI_execute_plan(stmt->plan, NULL, NULL, false, 0);
1124+
else SPI_execute_plan(stmt->plan, stmt->values, stmt->nulls, false, 0);
11131125

1114-
stmt->executed_nonselect = true;
1115-
return DBRES_DONE;
1116-
}
1117-
1118-
return DBRES_DONE;
1126+
stmt->executed_nonselect = true;
1127+
rc = DBRES_DONE;
1128+
break;
1129+
}
1130+
1131+
rc = DBRES_DONE;
1132+
} while (0);
11191133
}
11201134
PG_CATCH();
11211135
{
@@ -1128,10 +1142,10 @@ int databasevm_step (dbvm_t *vm) {
11281142
clear_fetch_batch(stmt);
11291143
close_portal(stmt);
11301144

1131-
return err;
1145+
rc = err;
11321146
}
11331147
PG_END_TRY();
1134-
return DBRES_DONE;
1148+
return rc;
11351149
}
11361150

11371151
void databasevm_finalize (dbvm_t *vm) {
@@ -1650,25 +1664,28 @@ void *database_value_dup (dbvalue_t *value) {
16501664

16511665
int database_begin_savepoint (cloudsync_context *data, const char *savepoint_name) {
16521666
cloudsync_reset_error(data);
1667+
int rc = DBRES_OK;
1668+
16531669
PG_TRY();
16541670
{
16551671
BeginInternalSubTransaction(NULL);
16561672
}
16571673
PG_CATCH();
16581674
{
16591675
ErrorData *edata = CopyErrorData();
1660-
int err = cloudsync_set_error(data, edata->message, DBRES_ERROR);
1676+
rc = cloudsync_set_error(data, edata->message, DBRES_ERROR);
16611677
FreeErrorData(edata);
16621678
FlushErrorState();
1663-
return err;
16641679
}
16651680
PG_END_TRY();
16661681

1667-
return DBRES_OK;
1682+
return rc;
16681683
}
16691684

16701685
int database_commit_savepoint (cloudsync_context *data, const char *savepoint_name) {
16711686
cloudsync_reset_error(data);
1687+
int rc = DBRES_OK;
1688+
16721689
PG_TRY();
16731690
{
16741691
ReleaseCurrentSubTransaction();
@@ -1683,15 +1700,17 @@ int database_commit_savepoint (cloudsync_context *data, const char *savepoint_na
16831700
PG_CATCH();
16841701
{
16851702
FlushErrorState();
1686-
return DBRES_ERROR;
1703+
rc = DBRES_ERROR;
16871704
}
16881705
PG_END_TRY();
16891706

1690-
return DBRES_OK;
1707+
return rc;
16911708
}
16921709

16931710
int database_rollback_savepoint (cloudsync_context *data, const char *savepoint_name) {
16941711
cloudsync_reset_error(data);
1712+
int rc = DBRES_OK;
1713+
16951714
PG_TRY();
16961715
{
16971716
RollbackAndReleaseCurrentSubTransaction();
@@ -1705,11 +1724,11 @@ int database_rollback_savepoint (cloudsync_context *data, const char *savepoint_
17051724
PG_CATCH();
17061725
{
17071726
FlushErrorState();
1708-
return DBRES_ERROR;
1727+
rc = DBRES_ERROR;
17091728
}
17101729
PG_END_TRY();
17111730

1712-
return DBRES_OK;
1731+
return rc;
17131732
}
17141733

17151734
// MARK: - MEMORY -

0 commit comments

Comments
 (0)