Skip to content

Commit 3824cf2

Browse files
committed
Route INSERT EXEC flush through execute_batch and fix rows-affected for sp_executesql. Flush the buffered INSERT EXEC temp table into the target via execute_batch. Since the flush re-enters through the inline handler (which pushes its own estate), redirect table-variable resolution, the per-statement implicit-transaction decision, and ownership chaining back to the caller's estate via insert_exec_flush_estate, and report rows-affected from the DestReceiver's captured-row count.
1 parent 7f1d085 commit 3824cf2

46 files changed

Lines changed: 595 additions & 426 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

contrib/babelfishpg_tds/src/backend/tds/tdsresponse.c

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2781,25 +2781,35 @@ StatementEnd_Internal(PLtsql_execstate *estate, PLtsql_stmt *stmt, bool error)
27812781
case PLTSQL_STMT_EXEC_BATCH:
27822782
case PLTSQL_STMT_EXEC_SP:
27832783
{
2784+
/* INSERT EXEC can target any of EXEC, EXEC_BATCH or EXEC_SP */
2785+
void *insert_exec = NULL;
2786+
27842787
is_proc = true;
27852788
command_type = TDS_CMD_EXECUTE;
2789+
2790+
switch (stmt->cmd_type)
2791+
{
2792+
case PLTSQL_STMT_EXEC:
2793+
insert_exec = ((PLtsql_stmt_exec *) stmt)->insert_exec;
2794+
break;
2795+
case PLTSQL_STMT_EXEC_BATCH:
2796+
insert_exec = ((PLtsql_stmt_exec_batch *) stmt)->insert_exec;
2797+
break;
2798+
case PLTSQL_STMT_EXEC_SP:
2799+
insert_exec = ((PLtsql_stmt_exec_sp *) stmt)->insert_exec;
2800+
break;
2801+
default:
2802+
break;
2803+
}
2804+
27862805
/*
27872806
* For INSERT EXEC, report the row count set in
27882807
* flush_insert_exec_temp_table(). Suppress it when an error is
27892808
* pending: the rows are rolled back, no count is sent to the
27902809
* client, and a counted DONE left pending here would otherwise
27912810
* carry a stale count into the following error DONE token.
27922811
*/
2793-
if (!markErrorFlag &&
2794-
stmt->cmd_type == PLTSQL_STMT_EXEC &&
2795-
((PLtsql_stmt_exec *) stmt)->insert_exec != NULL)
2796-
{
2797-
command_type = TDS_CMD_INSERT;
2798-
row_count_valid = true;
2799-
}
2800-
else if (!markErrorFlag &&
2801-
stmt->cmd_type == PLTSQL_STMT_EXEC_BATCH &&
2802-
((PLtsql_stmt_exec_batch *) stmt)->insert_exec != NULL)
2812+
if (!markErrorFlag && insert_exec != NULL)
28032813
{
28042814
command_type = TDS_CMD_INSERT;
28052815
row_count_valid = true;

contrib/babelfishpg_tsql/src/pl_exec-2.c

Lines changed: 12 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -867,9 +867,6 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt)
867867
/* whether procedure was created WITH RECOMPILE */
868868
bool created_with_recompile = false;
869869

870-
/* INSERT EXEC handling - temp table lifecycle */
871-
bool insert_exec_setup_done = false;
872-
873870
/*
874871
* We need to disable the explain gucs incase of sp_reset_connection
875872
* execution otherwise we will get explain output for it which is
@@ -901,8 +898,8 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt)
901898
* Setup INSERT EXEC (new path): create temp table to capture procedure
902899
* output. After procedure completes, temp table is flushed to target.
903900
*/
904-
if (pltsql_enable_new_insert_exec)
905-
insert_exec_setup_done = insert_exec_setup(estate, stmt->insert_exec, true);
901+
if (stmt->insert_exec != NULL)
902+
insert_exec_setup(estate, stmt->insert_exec, true);
906903

907904
if (IS_TDS_CONN())
908905
{
@@ -1338,12 +1335,12 @@ exec_stmt_exec(PLtsql_execstate *estate, PLtsql_stmt_exec *stmt)
13381335
exec_eval_cleanup(estate);
13391336
SPI_freetuptable(SPI_tuptable);
13401337

1341-
if (insert_exec_setup_done)
1338+
if (stmt->insert_exec != NULL)
13421339
insert_exec_flush_and_cleanup(estate, stmt->insert_exec);
13431340
}
13441341
PG_FINALLY();
13451342
{
1346-
if (insert_exec_setup_done)
1343+
if (stmt->insert_exec != NULL)
13471344
pltsql_insert_exec_reset_all();
13481345

13491346
/*
@@ -1543,9 +1540,6 @@ exec_stmt_exec_batch(PLtsql_execstate *estate, PLtsql_stmt_exec_batch *stmt)
15431540
char *old_db_name = get_cur_db_name();
15441541
char *cur_db_name = NULL;
15451542

1546-
/* INSERT EXEC handling - temp table lifecycle */
1547-
bool insert_exec_setup_done = false;
1548-
15491543
LOCAL_FCINFO(fcinfo, 1);
15501544

15511545
/*
@@ -1568,8 +1562,8 @@ exec_stmt_exec_batch(PLtsql_execstate *estate, PLtsql_stmt_exec_batch *stmt)
15681562
* output. No implicit transaction for dynamic SQL (different semantics
15691563
* than stored procs).
15701564
*/
1571-
if (pltsql_enable_new_insert_exec)
1572-
insert_exec_setup_done = insert_exec_setup(estate, stmt->insert_exec, false);
1565+
if (stmt->insert_exec != NULL)
1566+
insert_exec_setup(estate, stmt->insert_exec, false);
15731567

15741568
/* Get the C-String representation */
15751569
querystr = convert_value_to_string(estate, query, restype);
@@ -1592,12 +1586,12 @@ exec_stmt_exec_batch(PLtsql_execstate *estate, PLtsql_stmt_exec_batch *stmt)
15921586
if (fcinfo->isnull)
15931587
elog(ERROR, "pltsql_inline_handler failed");
15941588

1595-
if (insert_exec_setup_done)
1589+
if (stmt->insert_exec != NULL)
15961590
insert_exec_flush_and_cleanup(estate, stmt->insert_exec);
15971591
}
15981592
PG_FINALLY();
15991593
{
1600-
if (insert_exec_setup_done)
1594+
if (stmt->insert_exec != NULL)
16011595
pltsql_insert_exec_reset_all();
16021596

16031597
/* Restore past settings */
@@ -2224,9 +2218,6 @@ exec_stmt_exec_sp(PLtsql_execstate *estate, PLtsql_stmt_exec_sp *stmt)
22242218
int save_nestlevel;
22252219
int scope_level;
22262220
InlineCodeBlockArgs *args = NULL;
2227-
2228-
/* INSERT EXEC handling - temp table lifecycle */
2229-
bool insert_exec_setup_done = false;
22302221

22312222
batch = exec_eval_expr(estate, stmt->query, &isnull1, &restype1, &restypmod1);
22322223
if (isnull1)
@@ -2279,8 +2270,8 @@ exec_stmt_exec_sp(PLtsql_execstate *estate, PLtsql_stmt_exec_sp *stmt)
22792270
* The procedure output will be redirected to this temp table.
22802271
* After procedure completes, we flush temp table to target and cleanup.
22812272
*/
2282-
if (pltsql_enable_new_insert_exec)
2283-
insert_exec_setup_done = insert_exec_setup(estate, stmt->insert_exec, true);
2273+
if (stmt->insert_exec != NULL)
2274+
insert_exec_setup(estate, stmt->insert_exec, true);
22842275

22852276
if (strcmp(batchstr, "") != 0) /* check edge cases for
22862277
* sp_executesql */
@@ -2293,12 +2284,12 @@ exec_stmt_exec_sp(PLtsql_execstate *estate, PLtsql_stmt_exec_sp *stmt)
22932284
exec_assign_value(estate, estate->datums[stmt->return_code_dno], Int32GetDatum(ret), false, INT4OID, 0);
22942285
}
22952286

2296-
if (insert_exec_setup_done)
2287+
if (stmt->insert_exec != NULL)
22972288
insert_exec_flush_and_cleanup(estate, stmt->insert_exec);
22982289
}
22992290
PG_FINALLY();
23002291
{
2301-
if (insert_exec_setup_done)
2292+
if (stmt->insert_exec != NULL)
23022293
pltsql_insert_exec_reset_all();
23032294

23042295
pltsql_revert_guc(save_nestlevel);

contrib/babelfishpg_tsql/src/pl_exec.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4990,9 +4990,17 @@ exec_stmt_execsql(PLtsql_execstate *estate,
49904990
{
49914991
/* Open nesting level in engine */
49924992
BeginCompositeTriggers(CurrentMemoryContext);
4993-
/* TSQL commands must run inside an explicit transaction */
4993+
/*
4994+
* TSQL commands must run inside an explicit transaction.
4995+
*
4996+
* Skip this for the INSERT EXEC flush statement
4997+
* The flush runs while the INSERT EXEC context is still active,
4998+
* so the matching per-statement commit further below is suppressed.
4999+
* The flush is a single INSERT that runs correctly under autocommit.
5000+
*/
49945001
if (!pltsql_disable_batch_auto_commit && support_tsql_trans &&
4995-
stmt->txn_data == NULL && !IsTransactionBlockActive())
5002+
stmt->txn_data == NULL && !IsTransactionBlockActive() &&
5003+
insert_exec_flush_estate == NULL)
49965004
{
49975005
MemoryContext oldCxt = CurrentMemoryContext;
49985006

contrib/babelfishpg_tsql/src/pl_handler.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3067,6 +3067,16 @@ bbf_table_var_lookup(const char *relname, Oid relnamespace)
30673067
PLtsql_tbl *tbl;
30683068
PLtsql_execstate *estate = get_current_tsql_estate();
30693069

3070+
/*
3071+
* During an INSERT EXEC flush the query runs through execute_batch/the
3072+
* inline handler, which pushes its own (empty) estate. insert_exec_flush_estate
3073+
* points us back at the estate that actually declared the target table
3074+
* variable, so an "@tv" flush target resolves to its backing table.
3075+
* Outside the flush it is NULL and we use the current (topmost) estate.
3076+
*/
3077+
estate = insert_exec_flush_estate ? insert_exec_flush_estate
3078+
: get_current_tsql_estate();
3079+
30703080
if (prev_relname_lookup_hook)
30713081
relid = (*prev_relname_lookup_hook) (relname, relnamespace);
30723082
else

contrib/babelfishpg_tsql/src/pl_insert_exec.c

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,16 @@ static void insertexec_destroy(DestReceiver *self);
7373
* Global INSERT EXEC context pointer - defined in pltsql.h, declared here.
7474
*/
7575
InsertExecContext *insert_exec_ctx = NULL;
76+
PLtsql_execstate *insert_exec_flush_estate = NULL;
7677

7778
extern void exec_set_rowcount(uint64 rowno);
7879
extern void exec_set_found(PLtsql_execstate *estate, bool state);
80+
/*
81+
* The flush INSERT is routed through execute_batch (the top-level batch entry
82+
* point). It runs through the same econtext setup as a normal T-SQL batch.
83+
*/
84+
extern int execute_batch(PLtsql_execstate *estate, char *batch, InlineCodeBlockArgs *args, List *params);
85+
extern InlineCodeBlockArgs *create_args(int numargs);
7986

8087
/*
8188
* Build a comma-separated list of quoted column identifiers from the parser's
@@ -523,6 +530,8 @@ flush_insert_exec_temp_table(PLtsql_execstate *estate, const char *target_schema
523530
const char *temp_name;
524531
Relation temp_rel;
525532
char *qualified_target;
533+
InlineCodeBlockArgs *flush_args;
534+
PLtsql_execstate *flush_estate_saved;
526535

527536
if (insert_exec_ctx == NULL)
528537
return;
@@ -581,23 +590,38 @@ flush_insert_exec_temp_table(PLtsql_execstate *estate, const char *target_schema
581590

582591
pfree(qualified_target);
583592

584-
/* Route through SPI_execute to run the flush INSERT in the current
585-
* transaction/SPI context. execute_batch cannot be used here: it enters
586-
* pltsql_inline_handler as an independent batch that manages its own
587-
* transaction boundaries, which aborts when the flush runs mid-INSERT-EXEC
588-
* (notably for table-variable targets whose lifetime is tied to the
589-
* surrounding transaction). */
590-
rc = SPI_execute(flush_query.data, false, 0);
593+
/*
594+
* Run the flush through execute_batch, publishing the caller's estate via
595+
* insert_exec_flush_estate for the duration so the inline handler's own
596+
* empty estate does not shadow it.
597+
*/
598+
flush_args = create_args(0);
599+
600+
flush_estate_saved = insert_exec_flush_estate;
601+
insert_exec_flush_estate = estate;
602+
PG_TRY();
603+
{
604+
rc = execute_batch(estate, flush_query.data, flush_args, NULL);
605+
}
606+
PG_CATCH();
607+
{
608+
insert_exec_flush_estate = flush_estate_saved;
609+
PG_RE_THROW();
610+
}
611+
PG_END_TRY();
612+
insert_exec_flush_estate = flush_estate_saved;
591613

592614
pfree(flush_query.data);
593615

594-
if (rc != SPI_OK_INSERT && rc != SPI_OK_INSERT_RETURNING)
616+
if (rc != PLTSQL_RC_OK)
595617
elog(ERROR, "INSERT EXEC failed due to error while flushing temp table to target table");
596618

597-
/* Update rowcount and FOUND for T-SQL compatibility */
598-
estate->eval_processed = SPI_processed;
599-
exec_set_rowcount(SPI_processed);
600-
exec_set_found(estate, SPI_processed != 0);
619+
/*
620+
* Report rows-affected from the DestReceiver's captured-row count
621+
*/
622+
estate->eval_processed = insert_exec_ctx->rows_processed;
623+
exec_set_rowcount(insert_exec_ctx->rows_processed);
624+
exec_set_found(estate, insert_exec_ctx->rows_processed != 0);
601625
}
602626

603627
/*
@@ -758,6 +782,9 @@ insertexec_receive(TupleTableSlot *slot, DestReceiver *self)
758782
/* Insert the projected tuple */
759783
table_tuple_insert(temp_rel, insert_slot, myState->cid, 0, NULL);
760784

785+
/* INSERT EXEC rows-affected count */
786+
insert_exec_ctx->rows_processed++;
787+
761788
/* Close immediately - do not hold open across subtransaction boundaries */
762789
table_close(temp_rel, RowExclusiveLock);
763790

contrib/babelfishpg_tsql/src/pltsql.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2534,10 +2534,19 @@ typedef struct InsertExecContext
25342534
PLExecStateCallStack *call_stack_entry; /* Call stack entry when INSERT EXEC started */
25352535
Oid target_rel_oid; /* OID of target table - lock held to detect schema changes */
25362536
bool is_target_relation_modified; /* Set by bbf_object_access_hook when target table is altered */
2537+
uint64 rows_processed; /* Rows captured by the DestReceiver = INSERT EXEC rows-affected */
25372538
} InsertExecContext;
25382539

25392540
extern InsertExecContext *insert_exec_ctx;
25402541

2542+
/*
2543+
* Set only during an INSERT EXEC flush. The flush runs through the inline
2544+
* handler, which pushes its own empty estate; this points back at the estate
2545+
* that declared the flush target so table-variable lookup, the implicit-
2546+
* transaction decision, and ownership chaining all resolve against the caller.
2547+
*/
2548+
extern PLtsql_execstate *insert_exec_flush_estate;
2549+
25412550
extern Oid create_insert_exec_temp_table(const char *target_table, const char *column_list, const char *schema_name_in, const char *db_name_in);
25422551
extern void pltsql_set_insert_exec_context_info(const char *target_table);
25432552
extern void pltsql_insert_exec_reset_all(void);

contrib/babelfishpg_tsql/src/pltsql_utils.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,6 +3065,15 @@ get_current_func_oid(void)
30653065
if (!pltsql_support_tsql_transactions())
30663066
return InvalidOid;
30673067

3068+
/*
3069+
* During an INSERT EXEC flush the inline handler pushes its own (anonymous
3070+
* batch) estate, so fall back to insert_exec_flush_estate (the procedure
3071+
* that issued the INSERT EXEC) to keep ownership chaining intact.
3072+
*/
3073+
if (insert_exec_flush_estate != NULL)
3074+
return (insert_exec_flush_estate->func) ?
3075+
insert_exec_flush_estate->func->fn_oid : InvalidOid;
3076+
30683077
/*
30693078
* Fetch the top procedure excution state from execution state call stack
30703079
* and get the owner of that procedure. Top entry in stack will have

contrib/babelfishpg_tsql/src/tsqlIface.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,11 +2163,11 @@ class tsqlBuilder : public tsqlCommonMutator
21632163
*/
21642164
void handleInsertExec(TSqlParser::Dml_statementContext *ctx)
21652165
{
2166-
/* INSERT EXEC is not allowed in functions unless target is a table variable */
2166+
/* INSERT EXEC is not allowed in a function */
21672167
if (is_compiling_create_function())
21682168
{
21692169
auto ddl_object = ctx->insert_statement()->ddl_object();
2170-
if (ddl_object && !ddl_object->local_id())
2170+
if (ddl_object)
21712171
throw PGErrorWrapperException(ERROR, ERRCODE_INVALID_FUNCTION_DEFINITION,
21722172
"'INSERT EXEC' cannot be used within a function", getLineAndPos(ddl_object));
21732173
}

0 commit comments

Comments
 (0)