@@ -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
11371151void databasevm_finalize (dbvm_t * vm ) {
@@ -1650,25 +1664,28 @@ void *database_value_dup (dbvalue_t *value) {
16501664
16511665int 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
16701685int 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
16931710int 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