Skip to content

Commit 56a31dd

Browse files
Merge pull request ApsaraDB#519 from ApsaraDB/POLARDB_11_DEV
merge: 20240722
2 parents 1c3c67b + 3ebc3d9 commit 56a31dd

File tree

12 files changed

+322
-113
lines changed

12 files changed

+322
-113
lines changed

.github/workflows/package.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ jobs:
2424
runs-on: ubuntu-latest
2525
strategy:
2626
matrix:
27-
base_image: [ centos7, anolis8, rocky8, rocky9 ]
27+
# base_image: [ centos7, anolis8, rocky8, rocky9 ]
28+
base_image: [ centos7, anolis8 ]
2829
steps:
2930
- name: Fetch source code
3031
uses: actions/checkout@v4

src/backend/commands/functioncmds.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
#include "parser/parse_func.h"
6262
#include "parser/parse_type.h"
6363
#include "pgstat.h"
64+
#include "tcop/pquery.h"
6465
#include "utils/acl.h"
6566
#include "utils/builtins.h"
6667
#include "utils/fmgroids.h"
@@ -2342,6 +2343,20 @@ ExecuteCallStmt(CallStmt *stmt, ParamListInfo params, bool atomic, DestReceiver
23422343
if (fcinfo.isnull)
23432344
elog(ERROR, "procedure returned null record");
23442345

2346+
/*
2347+
* Ensure there's an active snapshot whilst we execute whatever's
2348+
* involved here. Note that this is *not* sufficient to make the
2349+
* world safe for TOAST pointers to be included in the returned data:
2350+
* the referenced data could have gone away while we didn't hold a
2351+
* snapshot. Hence, it's incumbent on PLs that can do COMMIT/ROLLBACK
2352+
* to not return TOAST pointers, unless those pointers were fetched
2353+
* after the last COMMIT/ROLLBACK in the procedure.
2354+
*
2355+
* XXX that is a really nasty, hard-to-test requirement. Is there a
2356+
* way to remove it?
2357+
*/
2358+
EnsurePortalSnapshotExists();
2359+
23452360
td = DatumGetHeapTupleHeader(retval);
23462361
tupType = HeapTupleHeaderGetTypeId(td);
23472362
tupTypmod = HeapTupleHeaderGetTypMod(td);

src/backend/executor/spi.c

Lines changed: 49 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -260,12 +260,8 @@ SPI_commit(void)
260260
/* Start the actual commit */
261261
_SPI_current->internal_xact = true;
262262

263-
/*
264-
* Before committing, pop all active snapshots to avoid error about
265-
* "snapshot %p still active".
266-
*/
267-
while (ActiveSnapshotSet())
268-
PopActiveSnapshot();
263+
/* Release snapshots associated with portals */
264+
ForgetPortalSnapshots();
269265

270266
CommitTransactionCommand();
271267
MemoryContextSwitchTo(oldcontext);
@@ -300,6 +296,9 @@ SPI_rollback(void)
300296
/* Start the actual rollback */
301297
_SPI_current->internal_xact = true;
302298

299+
/* Release snapshots associated with portals */
300+
ForgetPortalSnapshots();
301+
303302
AbortCurrentTransaction();
304303
MemoryContextSwitchTo(oldcontext);
305304

@@ -2102,6 +2101,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
21022101
Oid my_lastoid = InvalidOid;
21032102
SPITupleTable *my_tuptable = NULL;
21042103
int res = 0;
2104+
bool allow_nonatomic = plan->no_snapshots; /* legacy API name */
21052105
bool pushed_active_snap = false;
21062106
ErrorContextCallback spierrcontext;
21072107
CachedPlan *cplan = NULL;
@@ -2134,11 +2134,12 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
21342134
* In the first two cases, we can just push the snap onto the stack once
21352135
* for the whole plan list.
21362136
*
2137-
* But if the plan has no_snapshots set to true, then don't manage
2138-
* snapshots at all. The caller should then take care of that.
2137+
* Note that snapshot != InvalidSnapshot implies an atomic execution
2138+
* context.
21392139
*/
2140-
if (snapshot != InvalidSnapshot && !plan->no_snapshots)
2140+
if (snapshot != InvalidSnapshot)
21412141
{
2142+
Assert(!allow_nonatomic);
21422143
if (read_only)
21432144
{
21442145
PushActiveSnapshot(snapshot);
@@ -2225,15 +2226,39 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22252226
stmt_list = cplan->stmt_list;
22262227

22272228
/*
2228-
* In the default non-read-only case, get a new snapshot, replacing
2229-
* any that we pushed in a previous cycle.
2229+
* If we weren't given a specific snapshot to use, and the statement
2230+
* list requires a snapshot, set that up.
22302231
*/
2231-
if (snapshot == InvalidSnapshot && !read_only && !plan->no_snapshots)
2232+
if (snapshot == InvalidSnapshot &&
2233+
(list_length(stmt_list) > 1 ||
2234+
(list_length(stmt_list) == 1 &&
2235+
PlannedStmtRequiresSnapshot(linitial_node(PlannedStmt,
2236+
stmt_list)))))
22322237
{
2233-
if (pushed_active_snap)
2234-
PopActiveSnapshot();
2235-
PushActiveSnapshot(GetTransactionSnapshot());
2236-
pushed_active_snap = true;
2238+
/*
2239+
* First, ensure there's a Portal-level snapshot. This back-fills
2240+
* the snapshot stack in case the previous operation was a COMMIT
2241+
* or ROLLBACK inside a procedure or DO block. (We can't put back
2242+
* the Portal snapshot any sooner, or we'd break cases like doing
2243+
* SET or LOCK just after COMMIT.) It's enough to check once per
2244+
* statement list, since COMMIT/ROLLBACK/CALL/DO can't appear
2245+
* within a multi-statement list.
2246+
*/
2247+
EnsurePortalSnapshotExists();
2248+
2249+
/*
2250+
* In the default non-read-only case, get a new per-statement-list
2251+
* snapshot, replacing any that we pushed in a previous cycle.
2252+
* Skip it when doing non-atomic execution, though (we rely
2253+
* entirely on the Portal snapshot in that case).
2254+
*/
2255+
if (!read_only && !allow_nonatomic)
2256+
{
2257+
if (pushed_active_snap)
2258+
PopActiveSnapshot();
2259+
PushActiveSnapshot(GetTransactionSnapshot());
2260+
pushed_active_snap = true;
2261+
}
22372262
}
22382263

22392264
foreach(lc2, stmt_list)
@@ -2246,6 +2271,7 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22462271
_SPI_current->lastoid = InvalidOid;
22472272
_SPI_current->tuptable = NULL;
22482273

2274+
/* Check for unsupported cases. */
22492275
if (stmt->utilityStmt)
22502276
{
22512277
if (IsA(stmt->utilityStmt, CopyStmt))
@@ -2277,9 +2303,10 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
22772303

22782304
/*
22792305
* If not read-only mode, advance the command counter before each
2280-
* command and update the snapshot.
2306+
* command and update the snapshot. (But skip it if the snapshot
2307+
* isn't under our control.)
22812308
*/
2282-
if (!read_only && !plan->no_snapshots)
2309+
if (!read_only && pushed_active_snap)
22832310
{
22842311
CommandCounterIncrement();
22852312
UpdateActiveSnapshotCommandId();
@@ -2313,13 +2340,11 @@ _SPI_execute_plan(SPIPlanPtr plan, ParamListInfo paramLI,
23132340
ProcessUtilityContext context;
23142341

23152342
/*
2316-
* If the SPI context is atomic, or we are asked to manage
2317-
* snapshots, then we are in an atomic execution context.
2318-
* Conversely, to propagate a nonatomic execution context, the
2319-
* caller must be in a nonatomic SPI context and manage
2320-
* snapshots itself.
2343+
* If the SPI context is atomic, or we were not told to allow
2344+
* nonatomic operations, tell ProcessUtility this is an atomic
2345+
* execution context.
23212346
*/
2322-
if (_SPI_current->atomic || !plan->no_snapshots)
2347+
if (_SPI_current->atomic || !allow_nonatomic)
23232348
context = PROCESS_UTILITY_QUERY;
23242349
else
23252350
context = PROCESS_UTILITY_QUERY_NONATOMIC;

src/backend/replication/logical/worker.c

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,13 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
194194
ResultRelInfo *resultRelInfo;
195195
RangeTblEntry *rte;
196196

197+
/*
198+
* Input functions may need an active snapshot, as may AFTER triggers
199+
* invoked during finish_estate. For safety, ensure an active snapshot
200+
* exists throughout all our usage of the executor.
201+
*/
202+
PushActiveSnapshot(GetTransactionSnapshot());
203+
197204
estate = CreateExecutorState();
198205

199206
rte = makeNode(RangeTblEntry);
@@ -221,6 +228,22 @@ create_estate_for_relation(LogicalRepRelMapEntry *rel)
221228
return estate;
222229
}
223230

231+
/*
232+
* Finish any operations related to the executor state created by
233+
* create_estate_for_relation().
234+
*/
235+
static void
236+
finish_estate(EState *estate)
237+
{
238+
/* Handle any queued AFTER triggers. */
239+
AfterTriggerEndQuery(estate);
240+
241+
/* Cleanup. */
242+
ExecResetTupleTable(estate->es_tupleTable, false);
243+
FreeExecutorState(estate);
244+
PopActiveSnapshot();
245+
}
246+
224247
/*
225248
* Executes default values for columns for which we can't map to remote
226249
* relation columns.
@@ -627,9 +650,6 @@ apply_handle_insert(StringInfo s)
627650
remoteslot = ExecInitExtraTupleSlot(estate,
628651
RelationGetDescr(rel->localrel));
629652

630-
/* Input functions may need an active snapshot, so get one */
631-
PushActiveSnapshot(GetTransactionSnapshot());
632-
633653
/* Process and store remote tuple in the slot */
634654
oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
635655
slot_store_cstrings(remoteslot, rel, newtup.values);
@@ -643,13 +663,8 @@ apply_handle_insert(StringInfo s)
643663

644664
/* Cleanup. */
645665
ExecCloseIndices(estate->es_result_relation_info);
646-
PopActiveSnapshot();
647666

648-
/* Handle queued AFTER triggers. */
649-
AfterTriggerEndQuery(estate);
650-
651-
ExecResetTupleTable(estate->es_tupleTable, false);
652-
FreeExecutorState(estate);
667+
finish_estate(estate);
653668

654669
logicalrep_rel_close(rel, NoLock);
655670

@@ -760,7 +775,6 @@ apply_handle_update(StringInfo s)
760775
}
761776
}
762777

763-
PushActiveSnapshot(GetTransactionSnapshot());
764778
ExecOpenIndices(estate->es_result_relation_info, false);
765779

766780
/* Build the search tuple. */
@@ -819,15 +833,10 @@ apply_handle_update(StringInfo s)
819833
}
820834

821835
/* Cleanup. */
836+
EvalPlanQualEnd(&epqstate);
822837
ExecCloseIndices(estate->es_result_relation_info);
823-
PopActiveSnapshot();
824838

825-
/* Handle queued AFTER triggers. */
826-
AfterTriggerEndQuery(estate);
827-
828-
EvalPlanQualEnd(&epqstate);
829-
ExecResetTupleTable(estate->es_tupleTable, false);
830-
FreeExecutorState(estate);
839+
finish_estate(estate);
831840

832841
logicalrep_rel_close(rel, NoLock);
833842

@@ -878,7 +887,6 @@ apply_handle_delete(StringInfo s)
878887
RelationGetDescr(rel->localrel));
879888
EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1);
880889

881-
PushActiveSnapshot(GetTransactionSnapshot());
882890
ExecOpenIndices(estate->es_result_relation_info, false);
883891

884892
/* Find the tuple using the replica identity index. */
@@ -919,15 +927,10 @@ apply_handle_delete(StringInfo s)
919927
}
920928

921929
/* Cleanup. */
930+
EvalPlanQualEnd(&epqstate);
922931
ExecCloseIndices(estate->es_result_relation_info);
923-
PopActiveSnapshot();
924932

925-
/* Handle queued AFTER triggers. */
926-
AfterTriggerEndQuery(estate);
927-
928-
EvalPlanQualEnd(&epqstate);
929-
ExecResetTupleTable(estate->es_tupleTable, false);
930-
FreeExecutorState(estate);
933+
finish_estate(estate);
931934

932935
logicalrep_rel_close(rel, NoLock);
933936

0 commit comments

Comments
 (0)