Index: src/backend/access/transam/xact.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/access/transam/xact.c,v retrieving revision 1.262 diff -c -p -r1.262 xact.c *** src/backend/access/transam/xact.c 26 Mar 2008 18:48:59 -0000 1.262 --- src/backend/access/transam/xact.c 11 Apr 2008 17:07:51 -0000 *************** CommitTransaction(void) *** 1752,1757 **** --- 1752,1758 ---- AtEOXact_ComboCid(); AtEOXact_HashTables(true); AtEOXact_PgStat(true); + AtEOXact_Snapshot(true); pgstat_report_xact_timestamp(0); CurrentResourceOwner = NULL; *************** PrepareTransaction(void) *** 1984,1989 **** --- 1985,1991 ---- AtEOXact_ComboCid(); AtEOXact_HashTables(true); /* don't call AtEOXact_PgStat here */ + AtEOXact_Snapshot(true); CurrentResourceOwner = NULL; ResourceOwnerDelete(TopTransactionResourceOwner); *************** AbortTransaction(void) *** 2128,2133 **** --- 2130,2136 ---- AtEOXact_ComboCid(); AtEOXact_HashTables(false); AtEOXact_PgStat(false); + AtEOXact_Snapshot(false); pgstat_report_xact_timestamp(0); /* *************** CommitSubTransaction(void) *** 3806,3811 **** --- 3809,3815 ---- AtSubCommit_Notify(); AtEOSubXact_UpdateFlatFiles(true, s->subTransactionId, s->parent->subTransactionId); + AtSubCommit_Snapshot(); CallSubXactCallbacks(SUBXACT_EVENT_COMMIT_SUB, s->subTransactionId, s->parent->subTransactionId); *************** AbortSubTransaction(void) *** 3926,3931 **** --- 3930,3936 ---- AtSubAbort_Notify(); AtEOSubXact_UpdateFlatFiles(false, s->subTransactionId, s->parent->subTransactionId); + AtSubAbort_Snapshot(); /* Advertise the fact that we aborted in pg_clog. */ (void) RecordTransactionAbort(true); Index: src/backend/catalog/index.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/catalog/index.c,v retrieving revision 1.296 diff -c -p -r1.296 index.c *** src/backend/catalog/index.c 26 Mar 2008 21:10:37 -0000 1.296 --- src/backend/catalog/index.c 11 Apr 2008 17:08:49 -0000 *************** IndexBuildHeapScan(Relation heapRelation *** 1466,1471 **** --- 1466,1472 ---- TransactionId OldestXmin; BlockNumber root_blkno = InvalidBlockNumber; OffsetNumber root_offsets[MaxHeapTuplesPerPage]; + bool unreg_snapshot = false; /* * sanity checks *************** IndexBuildHeapScan(Relation heapRelation *** 1502,1508 **** } else if (indexInfo->ii_Concurrent) { ! snapshot = CopySnapshot(GetTransactionSnapshot()); OldestXmin = InvalidTransactionId; /* not used */ } else --- 1503,1510 ---- } else if (indexInfo->ii_Concurrent) { ! snapshot = RegisterSnapshot(GetTransactionSnapshot()); ! unreg_snapshot = true; OldestXmin = InvalidTransactionId; /* not used */ } else *************** IndexBuildHeapScan(Relation heapRelation *** 1787,1792 **** --- 1789,1796 ---- } heap_endscan(scan); + if (unreg_snapshot) + UnregisterSnapshot(snapshot); ExecDropSingleTupleTableSlot(slot); Index: src/backend/commands/cluster.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/cluster.c,v retrieving revision 1.173 diff -c -p -r1.173 cluster.c *** src/backend/commands/cluster.c 13 Apr 2008 19:18:14 -0000 1.173 --- src/backend/commands/cluster.c 14 Apr 2008 13:21:14 -0000 *************** cluster(ClusterStmt *stmt, bool isTopLev *** 211,216 **** --- 211,217 ---- rvs = get_tables_to_cluster(cluster_context); /* Commit to get out of starting transaction */ + PopActiveSnapshot(); CommitTransactionCommand(); /* Ok, now that we've got them all, cluster them one by one */ *************** cluster(ClusterStmt *stmt, bool isTopLev *** 221,228 **** /* Start a new transaction for each relation. */ StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); cluster_rel(rvtc, true); CommitTransactionCommand(); } --- 222,230 ---- /* Start a new transaction for each relation. */ StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! PushActiveSnapshot(GetTransactionSnapshot()); cluster_rel(rvtc, true); + PopActiveSnapshot(); CommitTransactionCommand(); } Index: src/backend/commands/copy.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/copy.c,v retrieving revision 1.298 diff -c -p -r1.298 copy.c *** src/backend/commands/copy.c 26 Mar 2008 18:48:59 -0000 1.298 --- src/backend/commands/copy.c 14 Apr 2008 17:36:17 -0000 *************** DoCopy(const CopyStmt *stmt, const char *** 1003,1008 **** --- 1003,1009 ---- Query *query; PlannedStmt *plan; DestReceiver *dest; + Snapshot snap; Assert(!is_from); cstate->rel = NULL; *************** DoCopy(const CopyStmt *stmt, const char *** 1044,1064 **** plan = planner(query, 0, NULL); /* ! * Update snapshot command ID to ensure this query sees results of any ! * previously executed queries. (It's a bit cheesy to modify ! * ActiveSnapshot without making a copy, but for the limited ways in ! * which COPY can be invoked, I think it's OK, because the active ! * snapshot shouldn't be shared with anything else anyway.) */ ! ActiveSnapshot->curcid = GetCurrentCommandId(false); /* Create dest receiver for COPY OUT */ dest = CreateDestReceiver(DestCopyOut, NULL); ((DR_copy *) dest)->cstate = cstate; /* Create a QueryDesc requesting no output */ ! cstate->queryDesc = CreateQueryDesc(plan, ! ActiveSnapshot, InvalidSnapshot, dest, NULL, false); /* --- 1045,1063 ---- plan = planner(query, 0, NULL); /* ! * Use a snapshot with an updated command ID to ensure this query sees ! * results of any previously executed queries. We cannot modify the ! * active snapshot in place, so get a copy instead. */ ! snap = CopySnapshot(GetActiveSnapshot()); ! snap->curcid = GetCurrentCommandId(false); /* Create dest receiver for COPY OUT */ dest = CreateDestReceiver(DestCopyOut, NULL); ((DR_copy *) dest)->cstate = cstate; /* Create a QueryDesc requesting no output */ ! cstate->queryDesc = CreateQueryDesc(plan, snap, InvalidSnapshot, dest, NULL, false); /* *************** CopyTo(CopyState cstate) *** 1390,1396 **** values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); ! scandesc = heap_beginscan(cstate->rel, ActiveSnapshot, 0, NULL); while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { --- 1389,1395 ---- values = (Datum *) palloc(num_phys_attrs * sizeof(Datum)); nulls = (bool *) palloc(num_phys_attrs * sizeof(bool)); ! scandesc = heap_beginscan(cstate->rel, GetActiveSnapshot(), 0, NULL); while ((tuple = heap_getnext(scandesc, ForwardScanDirection)) != NULL) { Index: src/backend/commands/explain.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/explain.c,v retrieving revision 1.171 diff -c -p -r1.171 explain.c *** src/backend/commands/explain.c 26 Mar 2008 18:48:59 -0000 1.171 --- src/backend/commands/explain.c 14 Apr 2008 17:36:42 -0000 *************** ExplainOnePlan(PlannedStmt *plannedstmt, *** 226,244 **** ExplainState *es; StringInfoData buf; int eflags; /* ! * Update snapshot command ID to ensure this query sees results of any ! * previously executed queries. (It's a bit cheesy to modify ! * ActiveSnapshot without making a copy, but for the limited ways in which ! * EXPLAIN can be invoked, I think it's OK, because the active snapshot ! * shouldn't be shared with anything else anyway.) */ ! ActiveSnapshot->curcid = GetCurrentCommandId(false); /* Create a QueryDesc requesting no output */ queryDesc = CreateQueryDesc(plannedstmt, ! ActiveSnapshot, InvalidSnapshot, None_Receiver, params, stmt->analyze); --- 226,244 ---- ExplainState *es; StringInfoData buf; int eflags; + Snapshot snap; /* ! * Use a snapshot with an updates command ID to ensure this query sees ! * results of any previously executed queries. We cannot modify the ! * active snapshot in place, so get a copy instead. */ ! snap = CopySnapshot(GetActiveSnapshot()); ! snap->curcid = GetCurrentCommandId(false); /* Create a QueryDesc requesting no output */ queryDesc = CreateQueryDesc(plannedstmt, ! snap, InvalidSnapshot, None_Receiver, params, stmt->analyze); Index: src/backend/commands/indexcmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/indexcmds.c,v retrieving revision 1.174 diff -c -p -r1.174 indexcmds.c *** src/backend/commands/indexcmds.c 26 Mar 2008 21:10:37 -0000 1.174 --- src/backend/commands/indexcmds.c 14 Apr 2008 23:46:23 -0000 *************** DefineIndex(RangeVar *heapRelation, *** 483,488 **** --- 483,489 ---- */ LockRelationIdForSession(&heaprelid, ShareUpdateExclusiveLock); + PopActiveSnapshot(); CommitTransactionCommand(); StartTransactionCommand(); *************** DefineIndex(RangeVar *heapRelation, *** 541,547 **** indexRelation = index_open(indexRelationId, RowExclusiveLock); /* Set ActiveSnapshot since functions in the indexes may need it */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* We have to re-build the IndexInfo struct, since it was lost in commit */ indexInfo = BuildIndexInfo(indexRelation); --- 542,548 ---- indexRelation = index_open(indexRelationId, RowExclusiveLock); /* Set ActiveSnapshot since functions in the indexes may need it */ ! PushActiveSnapshot(GetTransactionSnapshot()); /* We have to re-build the IndexInfo struct, since it was lost in commit */ indexInfo = BuildIndexInfo(indexRelation); *************** DefineIndex(RangeVar *heapRelation, *** 580,585 **** --- 581,589 ---- heap_close(pg_index, RowExclusiveLock); + /* we can do away with our snapshot */ + PopActiveSnapshot(); + /* * Commit this transaction to make the indisready update visible. */ *************** DefineIndex(RangeVar *heapRelation, *** 615,628 **** * We also set ActiveSnapshot to this snap, since functions in indexes may * need a snapshot. */ ! snapshot = CopySnapshot(GetTransactionSnapshot()); ! ActiveSnapshot = snapshot; /* * Scan the index and the heap, insert any missing index entries. */ validate_index(relationId, indexRelationId, snapshot); /* * The index is now valid in the sense that it contains all currently * interesting tuples. But since it might not contain tuples deleted just --- 619,635 ---- * We also set ActiveSnapshot to this snap, since functions in indexes may * need a snapshot. */ ! snapshot = RegisterSnapshot(GetTransactionSnapshot()); ! PushActiveSnapshot(snapshot); /* * Scan the index and the heap, insert any missing index entries. */ validate_index(relationId, indexRelationId, snapshot); + /* We no longer need the validating snapshot here anymore */ + UnregisterSnapshot(snapshot); + /* * The index is now valid in the sense that it contains all currently * interesting tuples. But since it might not contain tuples deleted just *************** DefineIndex(RangeVar *heapRelation, *** 644,650 **** * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not * check for that. */ ! old_snapshots = GetCurrentVirtualXIDs(ActiveSnapshot->xmax, false, PROC_IS_AUTOVACUUM | PROC_IN_VACUUM); while (VirtualTransactionIdIsValid(*old_snapshots)) --- 651,657 ---- * Also, GetCurrentVirtualXIDs never reports our own vxid, so we need not * check for that. */ ! old_snapshots = GetCurrentVirtualXIDs(snapshot->xmax, false, PROC_IS_AUTOVACUUM | PROC_IN_VACUUM); while (VirtualTransactionIdIsValid(*old_snapshots)) *************** DefineIndex(RangeVar *heapRelation, *** 675,680 **** --- 682,688 ---- heap_close(pg_index, RowExclusiveLock); + /* * The pg_index update will cause backends (including this one) to update * relcache entries for the index itself, but we should also send a *************** DefineIndex(RangeVar *heapRelation, *** 685,690 **** --- 693,701 ---- */ CacheInvalidateRelcacheByRelid(heaprelid.relId); + /* we can now do away with our active snapshot */ + PopActiveSnapshot(); + /* * Last thing to do is release the session-level lock on the parent table. */ *************** ReindexDatabase(const char *databaseName *** 1452,1457 **** --- 1463,1469 ---- heap_close(relationRelation, AccessShareLock); /* Now reindex each rel in a separate transaction */ + PopActiveSnapshot(); CommitTransactionCommand(); foreach(l, relids) { *************** ReindexDatabase(const char *databaseName *** 1459,1469 **** StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); if (reindex_relation(relid, true)) ereport(NOTICE, (errmsg("table \"%s\" was reindexed", get_rel_name(relid)))); CommitTransactionCommand(); } StartTransactionCommand(); --- 1471,1482 ---- StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! PushActiveSnapshot(GetTransactionSnapshot()); if (reindex_relation(relid, true)) ereport(NOTICE, (errmsg("table \"%s\" was reindexed", get_rel_name(relid)))); + PopActiveSnapshot(); CommitTransactionCommand(); } StartTransactionCommand(); Index: src/backend/commands/portalcmds.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/portalcmds.c,v retrieving revision 1.73 diff -c -p -r1.73 portalcmds.c *** src/backend/commands/portalcmds.c 2 Apr 2008 18:31:50 -0000 1.73 --- src/backend/commands/portalcmds.c 11 Apr 2008 17:07:51 -0000 *************** PerformCursorOpen(PlannedStmt *stmt, Par *** 121,127 **** /* * Start execution, inserting parameters if any. */ ! PortalStart(portal, params, ActiveSnapshot); Assert(portal->strategy == PORTAL_ONE_SELECT); --- 121,127 ---- /* * Start execution, inserting parameters if any. */ ! PortalStart(portal, params, GetActiveSnapshot()); Assert(portal->strategy == PORTAL_ONE_SELECT); *************** PersistHoldablePortal(Portal portal) *** 293,299 **** { QueryDesc *queryDesc = PortalGetQueryDesc(portal); Portal saveActivePortal; - Snapshot saveActiveSnapshot; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldcxt; --- 293,298 ---- *************** PersistHoldablePortal(Portal portal) *** 334,351 **** * Set up global portal context pointers. */ saveActivePortal = ActivePortal; - saveActiveSnapshot = ActiveSnapshot; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; - ActiveSnapshot = queryDesc->snapshot; CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); MemoryContextSwitchTo(PortalContext); /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. --- 333,350 ---- * Set up global portal context pointers. */ saveActivePortal = ActivePortal; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); MemoryContextSwitchTo(PortalContext); + PushActiveSnapshot(queryDesc->snapshot); + /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. *************** PersistHoldablePortal(Portal portal) *** 411,417 **** /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; --- 410,415 ---- *************** PersistHoldablePortal(Portal portal) *** 425,434 **** portal->status = PORTAL_READY; ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; /* * We can now release any subsidiary memory of the portal's heap context; * we'll never use it again. The executor already dropped its context, --- 423,433 ---- portal->status = PORTAL_READY; ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; + PopActiveSnapshot(); + /* * We can now release any subsidiary memory of the portal's heap context; * we'll never use it again. The executor already dropped its context, Index: src/backend/commands/prepare.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/prepare.c,v retrieving revision 1.85 diff -c -p -r1.85 prepare.c *** src/backend/commands/prepare.c 2 Apr 2008 18:31:50 -0000 1.85 --- src/backend/commands/prepare.c 11 Apr 2008 17:07:51 -0000 *************** ExecuteQuery(ExecuteStmt *stmt, const ch *** 264,270 **** /* * Run the portal to completion. */ ! PortalStart(portal, paramLI, ActiveSnapshot); (void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag); --- 264,270 ---- /* * Run the portal to completion. */ ! PortalStart(portal, paramLI, GetActiveSnapshot()); (void) PortalRun(portal, FETCH_ALL, false, dest, dest, completionTag); Index: src/backend/commands/trigger.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/trigger.c,v retrieving revision 1.231 diff -c -p -r1.231 trigger.c *** src/backend/commands/trigger.c 28 Mar 2008 00:21:55 -0000 1.231 --- src/backend/commands/trigger.c 11 Apr 2008 18:59:17 -0000 *************** void *** 2974,2979 **** --- 2974,2980 ---- AfterTriggerFireDeferred(void) { AfterTriggerEventList *events; + bool snap_pushed = false; /* Must be inside a transaction */ Assert(afterTriggers != NULL); *************** AfterTriggerFireDeferred(void) *** 2988,2994 **** */ events = &afterTriggers->events; if (events->head != NULL) ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* * Run all the remaining triggers. Loop until they are all gone, in case --- 2989,2998 ---- */ events = &afterTriggers->events; if (events->head != NULL) ! { ! PushActiveSnapshot(GetTransactionSnapshot()); ! snap_pushed = true; ! } /* * Run all the remaining triggers. Loop until they are all gone, in case *************** AfterTriggerFireDeferred(void) *** 3001,3006 **** --- 3005,3013 ---- afterTriggerInvokeEvents(events, firing_id, NULL, true); } + if (snap_pushed) + PopActiveSnapshot(); + Assert(events->head == NULL); } Index: src/backend/commands/vacuum.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/commands/vacuum.c,v retrieving revision 1.371 diff -c -p -r1.371 vacuum.c *** src/backend/commands/vacuum.c 26 Mar 2008 21:10:38 -0000 1.371 --- src/backend/commands/vacuum.c 11 Apr 2008 18:56:58 -0000 *************** vacuum(VacuumStmt *vacstmt, List *relids *** 407,412 **** --- 407,416 ---- */ if (use_own_xacts) { + /* ActiveSnapshot is not set by autovacuum */ + if (ActiveSnapshotSet()) + PopActiveSnapshot(); + /* matches the StartTransaction in PostgresMain() */ CommitTransactionCommand(); } *************** vacuum(VacuumStmt *vacstmt, List *relids *** 444,450 **** { StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } else old_context = MemoryContextSwitchTo(anl_context); --- 448,454 ---- { StartTransactionCommand(); /* functions in indexes may want a snapshot set */ ! PushActiveSnapshot(GetTransactionSnapshot()); } else old_context = MemoryContextSwitchTo(anl_context); *************** vacuum(VacuumStmt *vacstmt, List *relids *** 452,458 **** --- 456,465 ---- analyze_rel(relid, vacstmt, vac_strategy); if (use_own_xacts) + { + PopActiveSnapshot(); CommitTransactionCommand(); + } else { MemoryContextSwitchTo(old_context); *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 979,985 **** if (vacstmt->full) { /* functions in indexes may want a snapshot set */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } else { --- 986,992 ---- if (vacstmt->full) { /* functions in indexes may want a snapshot set */ ! PushActiveSnapshot(GetTransactionSnapshot()); } else { *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1036,1041 **** --- 1043,1050 ---- if (!onerel) { + if (vacstmt->full) + PopActiveSnapshot(); CommitTransactionCommand(); return; } *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1066,1071 **** --- 1075,1082 ---- (errmsg("skipping \"%s\" --- only table or database owner can vacuum it", RelationGetRelationName(onerel)))); relation_close(onerel, lmode); + if (vacstmt->full) + PopActiveSnapshot(); CommitTransactionCommand(); return; } *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1080,1085 **** --- 1091,1098 ---- (errmsg("skipping \"%s\" --- cannot vacuum indexes, views, or special system tables", RelationGetRelationName(onerel)))); relation_close(onerel, lmode); + if (vacstmt->full) + PopActiveSnapshot(); CommitTransactionCommand(); return; } *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1094,1099 **** --- 1107,1114 ---- if (isOtherTempNamespace(RelationGetNamespace(onerel))) { relation_close(onerel, lmode); + if (vacstmt->full) + PopActiveSnapshot(); CommitTransactionCommand(); return; } *************** vacuum_rel(Oid relid, VacuumStmt *vacstm *** 1141,1146 **** --- 1156,1163 ---- /* * Complete the transaction and free all temporary memory used. */ + if (vacstmt->full) + PopActiveSnapshot(); CommitTransactionCommand(); /* Index: src/backend/executor/execMain.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/execMain.c,v retrieving revision 1.305 diff -c -p -r1.305 execMain.c *** src/backend/executor/execMain.c 28 Mar 2008 00:21:55 -0000 1.305 --- src/backend/executor/execMain.c 11 Apr 2008 17:08:54 -0000 *************** *** 52,57 **** --- 52,58 ---- #include "utils/acl.h" #include "utils/lsyscache.h" #include "utils/memutils.h" + #include "utils/snapmgr.h" #include "utils/tqual.h" *************** ExecutorStart(QueryDesc *queryDesc, int *** 183,190 **** /* * Copy other important information into the EState */ ! estate->es_snapshot = queryDesc->snapshot; ! estate->es_crosscheck_snapshot = queryDesc->crosscheck_snapshot; estate->es_instrument = queryDesc->doInstrument; /* --- 184,191 ---- /* * Copy other important information into the EState */ ! estate->es_snapshot = RegisterSnapshot(queryDesc->snapshot); ! estate->es_crosscheck_snapshot = RegisterSnapshot(queryDesc->crosscheck_snapshot); estate->es_instrument = queryDesc->doInstrument; /* *************** ExecutorEnd(QueryDesc *queryDesc) *** 311,316 **** --- 312,321 ---- if (estate->es_select_into) CloseIntoRel(queryDesc); + /* do away with our snapshots */ + UnregisterSnapshot(estate->es_snapshot); + UnregisterSnapshot(estate->es_crosscheck_snapshot); + /* * Must switch out of context before destroying it */ Index: src/backend/executor/functions.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/functions.c,v retrieving revision 1.124 diff -c -p -r1.124 functions.c *** src/backend/executor/functions.c 26 Mar 2008 18:48:59 -0000 1.124 --- src/backend/executor/functions.c 11 Apr 2008 18:33:40 -0000 *************** postquel_start(execution_state *es, SQLF *** 295,309 **** * In a read-only function, use the surrounding query's snapshot; * otherwise take a new snapshot for each query. The snapshot should * include a fresh command ID so that all work to date in this transaction ! * is visible. We copy in both cases so that postquel_end can ! * unconditionally do FreeSnapshot. */ if (fcache->readonly_func) ! snapshot = CopySnapshot(ActiveSnapshot); else { CommandCounterIncrement(); ! snapshot = CopySnapshot(GetTransactionSnapshot()); } if (IsA(es->stmt, PlannedStmt)) --- 295,308 ---- * In a read-only function, use the surrounding query's snapshot; * otherwise take a new snapshot for each query. The snapshot should * include a fresh command ID so that all work to date in this transaction ! * is visible. */ if (fcache->readonly_func) ! snapshot = GetActiveSnapshot(); else { CommandCounterIncrement(); ! snapshot = GetTransactionSnapshot(); } if (IsA(es->stmt, PlannedStmt)) *************** static TupleTableSlot * *** 340,353 **** postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) { TupleTableSlot *result; - Snapshot saveActiveSnapshot; long count; /* Make our snapshot the active one for any called functions */ ! saveActiveSnapshot = ActiveSnapshot; ! PG_TRY(); ! { ! ActiveSnapshot = es->qd->snapshot; if (es->qd->utilitystmt) { --- 339,348 ---- postquel_getnext(execution_state *es, SQLFunctionCachePtr fcache) { TupleTableSlot *result; long count; /* Make our snapshot the active one for any called functions */ ! PushActiveSnapshot(es->qd->snapshot); if (es->qd->utilitystmt) { *************** postquel_getnext(execution_state *es, SQ *** 380,395 **** result = ExecutorRun(es->qd, ForwardScanDirection, count); } - } - PG_CATCH(); - { - /* Restore global vars and propagate error */ - ActiveSnapshot = saveActiveSnapshot; - PG_RE_THROW(); - } - PG_END_TRY(); ! ActiveSnapshot = saveActiveSnapshot; return result; } --- 375,382 ---- result = ExecutorRun(es->qd, ForwardScanDirection, count); } ! PopActiveSnapshot(); return result; } *************** postquel_getnext(execution_state *es, SQ *** 397,404 **** static void postquel_end(execution_state *es) { - Snapshot saveActiveSnapshot; - /* mark status done to ensure we don't do ExecutorEnd twice */ es->status = F_EXEC_DONE; --- 384,389 ---- *************** postquel_end(execution_state *es) *** 406,431 **** if (es->qd->utilitystmt == NULL) { /* Make our snapshot the active one for any called functions */ ! saveActiveSnapshot = ActiveSnapshot; ! PG_TRY(); ! { ! ActiveSnapshot = es->qd->snapshot; if (es->qd->operation != CMD_SELECT) AfterTriggerEndQuery(es->qd->estate); ExecutorEnd(es->qd); ! } ! PG_CATCH(); ! { ! /* Restore global vars and propagate error */ ! ActiveSnapshot = saveActiveSnapshot; ! PG_RE_THROW(); ! } ! PG_END_TRY(); ! ActiveSnapshot = saveActiveSnapshot; } - FreeSnapshot(es->qd->snapshot); FreeQueryDesc(es->qd); es->qd = NULL; } --- 391,405 ---- if (es->qd->utilitystmt == NULL) { /* Make our snapshot the active one for any called functions */ ! PushActiveSnapshot(es->qd->snapshot); if (es->qd->operation != CMD_SELECT) AfterTriggerEndQuery(es->qd->estate); ExecutorEnd(es->qd); ! ! PopActiveSnapshot(); } FreeQueryDesc(es->qd); es->qd = NULL; } Index: src/backend/executor/spi.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/executor/spi.c,v retrieving revision 1.193 diff -c -p -r1.193 spi.c *** src/backend/executor/spi.c 2 Apr 2008 18:31:50 -0000 1.193 --- src/backend/executor/spi.c 14 Apr 2008 17:37:50 -0000 *************** SPI_execp(SPIPlanPtr plan, Datum *Values *** 371,379 **** /* * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow ! * the caller to specify exactly which snapshots to use. Also, the caller ! * may specify that AFTER triggers should be queued as part of the outer ! * query rather than being fired immediately at the end of the command. * * This is currently not documented in spi.sgml because it is only intended * for use by RI triggers. --- 371,380 ---- /* * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow ! * the caller to specify exactly which snapshots to use, which will be ! * registered here. Also, the caller may specify that AFTER triggers should be ! * queued as part of the outer query rather than being fired immediately at the ! * end of the command. * * This is currently not documented in spi.sgml because it is only intended * for use by RI triggers. *************** SPI_cursor_open(const char *name, SPIPla *** 1101,1111 **** } /* ! * Set up the snapshot to use. (PortalStart will do CopySnapshot, so we * skip that here.) */ if (read_only) ! snapshot = ActiveSnapshot; else { CommandCounterIncrement(); --- 1102,1112 ---- } /* ! * Set up the snapshot to use. (PortalStart will do RegisterSnapshot, so we * skip that here.) */ if (read_only) ! snapshot = GetActiveSnapshot(); else { CommandCounterIncrement(); *************** _SPI_execute_plan(SPIPlanPtr plan, Param *** 1609,1623 **** volatile Oid my_lastoid = InvalidOid; SPITupleTable *volatile my_tuptable = NULL; volatile int res = 0; ! Snapshot saveActiveSnapshot; ! ! /* Be sure to restore ActiveSnapshot on error exit */ ! saveActiveSnapshot = ActiveSnapshot; ! PG_TRY(); ! { ! ErrorContextCallback spierrcontext; ! CachedPlan *cplan = NULL; ! ListCell *lc1; /* * Setup error traceback support for ereport() --- 1610,1619 ---- volatile Oid my_lastoid = InvalidOid; SPITupleTable *volatile my_tuptable = NULL; volatile int res = 0; ! bool have_active_snap = ActiveSnapshotSet(); ! ErrorContextCallback spierrcontext; ! CachedPlan *cplan = NULL; ! ListCell *lc1; /* * Setup error traceback support for ereport() *************** _SPI_execute_plan(SPIPlanPtr plan, Param *** 1653,1658 **** --- 1649,1655 ---- Node *stmt = (Node *) lfirst(lc2); bool canSetTag; DestReceiver *dest; + bool pushed_active_snap = false; _SPI_current->processed = 0; _SPI_current->lastoid = InvalidOid; *************** _SPI_execute_plan(SPIPlanPtr plan, Param *** 1708,1737 **** * ActiveSnapshot; if read-write, grab a full new snap. */ if (read_only) ! ActiveSnapshot = CopySnapshot(saveActiveSnapshot); else ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } else { /* * We interpret read_only with a specified snapshot to be * exactly that snapshot, but read-write means use the ! * snap with advancing of command ID. */ ! ActiveSnapshot = CopySnapshot(snapshot); if (!read_only) ! ActiveSnapshot->curcid = GetCurrentCommandId(false); } if (IsA(stmt, PlannedStmt) && ((PlannedStmt *) stmt)->utilityStmt == NULL) { QueryDesc *qdesc; qdesc = CreateQueryDesc((PlannedStmt *) stmt, ! ActiveSnapshot, ! crosscheck_snapshot, dest, paramLI, false); res = _SPI_pquery(qdesc, fire_triggers, --- 1705,1753 ---- * ActiveSnapshot; if read-write, grab a full new snap. */ if (read_only) ! { ! if (have_active_snap) ! { ! PushActiveSnapshot(GetActiveSnapshot()); ! pushed_active_snap = true; ! } ! } else ! { ! PushActiveSnapshot(GetTransactionSnapshot()); ! pushed_active_snap = true; ! } } else { + Snapshot snap; + /* * We interpret read_only with a specified snapshot to be * exactly that snapshot, but read-write means use the ! * snap with advancing of command ID. We cannot modify the ! * active snapshot in place, so get a copy instead. */ ! snap = CopySnapshot(snapshot); if (!read_only) ! snap->curcid = GetCurrentCommandId(false); ! PushActiveSnapshot(snap); ! pushed_active_snap = true; } if (IsA(stmt, PlannedStmt) && ((PlannedStmt *) stmt)->utilityStmt == NULL) { QueryDesc *qdesc; + Snapshot snap; + + if (ActiveSnapshotSet()) + snap = GetActiveSnapshot(); + else + snap = InvalidSnapshot; qdesc = CreateQueryDesc((PlannedStmt *) stmt, ! snap, crosscheck_snapshot, dest, paramLI, false); res = _SPI_pquery(qdesc, fire_triggers, *************** _SPI_execute_plan(SPIPlanPtr plan, Param *** 1751,1758 **** _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; res = SPI_OK_UTILITY; } ! FreeSnapshot(ActiveSnapshot); ! ActiveSnapshot = NULL; /* * The last canSetTag query sets the status values returned to --- 1767,1775 ---- _SPI_current->processed = _SPI_current->tuptable->alloced - _SPI_current->tuptable->free; res = SPI_OK_UTILITY; } ! ! if (pushed_active_snap) ! PopActiveSnapshot(); /* * The last canSetTag query sets the status values returned to *************** fail: *** 1804,1819 **** * Pop the error context stack */ error_context_stack = spierrcontext.previous; - } - PG_CATCH(); - { - /* Restore global vars and propagate error */ - ActiveSnapshot = saveActiveSnapshot; - PG_RE_THROW(); - } - PG_END_TRY(); - - ActiveSnapshot = saveActiveSnapshot; /* Save results for caller */ SPI_processed = my_processed; --- 1821,1826 ---- Index: src/backend/storage/ipc/procarray.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/ipc/procarray.c,v retrieving revision 1.43 diff -c -p -r1.43 procarray.c *** src/backend/storage/ipc/procarray.c 26 Mar 2008 18:48:59 -0000 1.43 --- src/backend/storage/ipc/procarray.c 14 Apr 2008 23:41:33 -0000 *************** GetSnapshotData(Snapshot snapshot, bool *** 681,691 **** Assert(snapshot != NULL); - /* Serializable snapshot must be computed before any other... */ - Assert(serializable ? - !TransactionIdIsValid(MyProc->xmin) : - TransactionIdIsValid(MyProc->xmin)); - /* * Allocating space for maxProcs xids is usually overkill; numProcs would * be sufficient. But it seems better to do the malloc while not holding --- 681,686 ---- *************** GetSnapshotData(Snapshot snapshot, bool *** 828,833 **** --- 823,832 ---- snapshot->xcnt = count; snapshot->subxcnt = subcount; + snapshot->active_count = 0; + snapshot->regd_count = 0; + snapshot->copied = false; + snapshot->curcid = GetCurrentCommandId(false); return snapshot; Index: src/backend/storage/large_object/inv_api.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/storage/large_object/inv_api.c,v retrieving revision 1.132 diff -c -p -r1.132 inv_api.c *** src/backend/storage/large_object/inv_api.c 12 Apr 2008 23:14:21 -0000 1.132 --- src/backend/storage/large_object/inv_api.c 14 Apr 2008 17:38:01 -0000 *************** inv_open(Oid lobjId, int flags, MemoryCo *** 246,257 **** } else if (flags & INV_READ) { ! /* be sure to copy snap into mcxt */ ! MemoryContext oldContext = MemoryContextSwitchTo(mcxt); ! ! retval->snapshot = CopySnapshot(ActiveSnapshot); retval->flags = IFS_RDLOCK; - MemoryContextSwitchTo(oldContext); } else elog(ERROR, "invalid flags: %d", flags); --- 246,253 ---- } else if (flags & INV_READ) { ! retval->snapshot = RegisterSnapshot(GetActiveSnapshot()); retval->flags = IFS_RDLOCK; } else elog(ERROR, "invalid flags: %d", flags); *************** inv_close(LargeObjectDesc *obj_desc) *** 274,280 **** { Assert(PointerIsValid(obj_desc)); if (obj_desc->snapshot != SnapshotNow) ! FreeSnapshot(obj_desc->snapshot); pfree(obj_desc); } --- 270,276 ---- { Assert(PointerIsValid(obj_desc)); if (obj_desc->snapshot != SnapshotNow) ! UnregisterSnapshot(obj_desc->snapshot); pfree(obj_desc); } Index: src/backend/tcop/fastpath.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/fastpath.c,v retrieving revision 1.99 diff -c -p -r1.99 fastpath.c *** src/backend/tcop/fastpath.c 26 Mar 2008 18:48:59 -0000 1.99 --- src/backend/tcop/fastpath.c 11 Apr 2008 18:30:48 -0000 *************** HandleFunctionRequest(StringInfo msgBuf) *** 309,315 **** * Now that we know we are in a valid transaction, set snapshot in case * needed by function itself or one of the datatype I/O routines. */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* * Begin parsing the buffer contents. --- 309,315 ---- * Now that we know we are in a valid transaction, set snapshot in case * needed by function itself or one of the datatype I/O routines. */ ! PushActiveSnapshot(GetTransactionSnapshot()); /* * Begin parsing the buffer contents. *************** HandleFunctionRequest(StringInfo msgBuf) *** 396,401 **** --- 396,404 ---- SendFunctionResult(retval, fcinfo.isnull, fip->rettype, rformat); + /* We no longer need the snapshot */ + PopActiveSnapshot(); + /* * Emit duration logging if appropriate. */ Index: src/backend/tcop/postgres.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/postgres.c,v retrieving revision 1.548 diff -c -p -r1.548 postgres.c *** src/backend/tcop/postgres.c 2 Apr 2008 18:31:50 -0000 1.548 --- src/backend/tcop/postgres.c 11 Apr 2008 17:08:54 -0000 *************** pg_plan_queries(List *querytrees, int cu *** 732,744 **** bool needSnapshot) { List * volatile stmt_list = NIL; ! Snapshot saveActiveSnapshot = ActiveSnapshot; ! ! /* PG_TRY to ensure previous ActiveSnapshot is restored on error */ ! PG_TRY(); ! { ! Snapshot mySnapshot = NULL; ! ListCell *query_list; foreach(query_list, querytrees) { --- 732,739 ---- bool needSnapshot) { List * volatile stmt_list = NIL; ! ListCell *query_list; ! bool snapshot_set = false; foreach(query_list, querytrees) { *************** pg_plan_queries(List *querytrees, int cu *** 752,762 **** } else { ! if (needSnapshot && mySnapshot == NULL) { ! mySnapshot = CopySnapshot(GetTransactionSnapshot()); ! ActiveSnapshot = mySnapshot; } stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams); } --- 747,758 ---- } else { ! if (needSnapshot && !snapshot_set) { ! PushActiveSnapshot(GetTransactionSnapshot()); ! snapshot_set = true; } + stmt = (Node *) pg_plan_query(query, cursorOptions, boundParams); } *************** pg_plan_queries(List *querytrees, int cu *** 764,779 **** stmt_list = lappend(stmt_list, stmt); } ! if (mySnapshot) ! FreeSnapshot(mySnapshot); ! } ! PG_CATCH(); ! { ! ActiveSnapshot = saveActiveSnapshot; ! PG_RE_THROW(); ! } ! PG_END_TRY(); ! ActiveSnapshot = saveActiveSnapshot; return stmt_list; } --- 760,767 ---- stmt_list = lappend(stmt_list, stmt); } ! if (snapshot_set) ! PopActiveSnapshot(); return stmt_list; } Index: src/backend/tcop/pquery.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/tcop/pquery.c,v retrieving revision 1.122 diff -c -p -r1.122 pquery.c *** src/backend/tcop/pquery.c 26 Mar 2008 18:48:59 -0000 1.122 --- src/backend/tcop/pquery.c 11 Apr 2008 18:30:19 -0000 *************** CreateQueryDesc(PlannedStmt *plannedstmt *** 70,77 **** qd->operation = plannedstmt->commandType; /* operation */ qd->plannedstmt = plannedstmt; /* plan */ qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */ ! qd->snapshot = snapshot; /* snapshot */ ! qd->crosscheck_snapshot = crosscheck_snapshot; /* RI check snapshot */ qd->dest = dest; /* output dest */ qd->params = params; /* parameter values passed into query */ qd->doInstrument = doInstrument; /* instrumentation wanted? */ --- 70,78 ---- qd->operation = plannedstmt->commandType; /* operation */ qd->plannedstmt = plannedstmt; /* plan */ qd->utilitystmt = plannedstmt->utilityStmt; /* in case DECLARE CURSOR */ ! qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */ ! /* RI check snapshot */ ! qd->crosscheck_snapshot = RegisterSnapshot(crosscheck_snapshot); qd->dest = dest; /* output dest */ qd->params = params; /* parameter values passed into query */ qd->doInstrument = doInstrument; /* instrumentation wanted? */ *************** CreateUtilityQueryDesc(Node *utilitystmt *** 98,104 **** qd->operation = CMD_UTILITY; /* operation */ qd->plannedstmt = NULL; qd->utilitystmt = utilitystmt; /* utility command */ ! qd->snapshot = snapshot; /* snapshot */ qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */ qd->dest = dest; /* output dest */ qd->params = params; /* parameter values passed into query */ --- 99,105 ---- qd->operation = CMD_UTILITY; /* operation */ qd->plannedstmt = NULL; qd->utilitystmt = utilitystmt; /* utility command */ ! qd->snapshot = RegisterSnapshot(snapshot); /* snapshot */ qd->crosscheck_snapshot = InvalidSnapshot; /* RI check snapshot */ qd->dest = dest; /* output dest */ qd->params = params; /* parameter values passed into query */ *************** CreateUtilityQueryDesc(Node *utilitystmt *** 118,123 **** --- 119,127 ---- void FreeQueryDesc(QueryDesc *qdesc) { + UnregisterSnapshot(qdesc->snapshot); + UnregisterSnapshot(qdesc->crosscheck_snapshot); + /* Can't be a live query */ Assert(qdesc->estate == NULL); /* Only the QueryDesc itself need be freed */ *************** ProcessQuery(PlannedStmt *plan, *** 152,167 **** elog(DEBUG3, "ProcessQuery"); /* ! * Must always set snapshot for plannable queries. Note we assume that ! * caller will take care of restoring ActiveSnapshot on exit/error. */ ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* * Create the QueryDesc object */ queryDesc = CreateQueryDesc(plan, ! ActiveSnapshot, InvalidSnapshot, dest, params, false); /* --- 156,170 ---- elog(DEBUG3, "ProcessQuery"); /* ! * Must always set a snapshot for plannable queries. */ ! PushActiveSnapshot(GetTransactionSnapshot()); /* * Create the QueryDesc object */ queryDesc = CreateQueryDesc(plan, ! GetActiveSnapshot(), InvalidSnapshot, dest, params, false); /* *************** ProcessQuery(PlannedStmt *plan, *** 216,230 **** /* Now take care of any queued AFTER triggers */ AfterTriggerEndQuery(queryDesc->estate); /* * Now, we close down all the scans and free allocated resources. */ ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); - - FreeSnapshot(ActiveSnapshot); - ActiveSnapshot = NULL; } /* --- 219,232 ---- /* Now take care of any queued AFTER triggers */ AfterTriggerEndQuery(queryDesc->estate); + PopActiveSnapshot(); + /* * Now, we close down all the scans and free allocated resources. */ ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); } /* *************** void *** 446,452 **** PortalStart(Portal portal, ParamListInfo params, Snapshot snapshot) { Portal saveActivePortal; - Snapshot saveActiveSnapshot; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldContext; --- 448,453 ---- *************** PortalStart(Portal portal, ParamListInfo *** 460,472 **** * Set up global portal context pointers. */ saveActivePortal = ActivePortal; - saveActiveSnapshot = ActiveSnapshot; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; - ActiveSnapshot = NULL; /* will be set later */ CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); --- 461,471 ---- *************** PortalStart(Portal portal, ParamListInfo *** 492,507 **** * copy it into the portal's context. */ if (snapshot) ! ActiveSnapshot = CopySnapshot(snapshot); else ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); /* * Create QueryDesc in portal's context; for the moment, set * the destination to DestNone. */ queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts), ! ActiveSnapshot, InvalidSnapshot, None_Receiver, params, --- 491,506 ---- * copy it into the portal's context. */ if (snapshot) ! PushActiveSnapshot(snapshot); else ! PushActiveSnapshot(GetTransactionSnapshot()); /* * Create QueryDesc in portal's context; for the moment, set * the destination to DestNone. */ queryDesc = CreateQueryDesc((PlannedStmt *) linitial(portal->stmts), ! GetActiveSnapshot(), InvalidSnapshot, None_Receiver, params, *************** PortalStart(Portal portal, ParamListInfo *** 545,550 **** --- 544,551 ---- portal->atEnd = false; /* allow fetches */ portal->portalPos = 0; portal->posOverflow = false; + + PopActiveSnapshot(); break; case PORTAL_ONE_RETURNING: *************** PortalStart(Portal portal, ParamListInfo *** 608,614 **** /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; --- 609,614 ---- *************** PortalStart(Portal portal, ParamListInfo *** 619,625 **** MemoryContextSwitchTo(oldContext); ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; --- 619,624 ---- *************** PortalRun(Portal portal, long count, boo *** 707,713 **** ResourceOwner saveTopTransactionResourceOwner; MemoryContext saveTopTransactionContext; Portal saveActivePortal; - Snapshot saveActiveSnapshot; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext saveMemoryContext; --- 706,711 ---- *************** PortalRun(Portal portal, long count, boo *** 751,764 **** saveTopTransactionResourceOwner = TopTransactionResourceOwner; saveTopTransactionContext = TopTransactionContext; saveActivePortal = ActivePortal; - saveActiveSnapshot = ActiveSnapshot; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; saveMemoryContext = CurrentMemoryContext; PG_TRY(); { ActivePortal = portal; - ActiveSnapshot = NULL; /* will be set later */ CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); --- 749,760 ---- *************** PortalRun(Portal portal, long count, boo *** 839,845 **** else MemoryContextSwitchTo(saveMemoryContext); ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; if (saveResourceOwner == saveTopTransactionResourceOwner) CurrentResourceOwner = TopTransactionResourceOwner; else --- 835,840 ---- *************** PortalRun(Portal portal, long count, boo *** 855,861 **** else MemoryContextSwitchTo(saveMemoryContext); ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; if (saveResourceOwner == saveTopTransactionResourceOwner) CurrentResourceOwner = TopTransactionResourceOwner; else --- 850,855 ---- *************** PortalRunSelect(Portal portal, *** 940,948 **** nprocessed = RunFromStore(portal, direction, count, dest); else { ! ActiveSnapshot = queryDesc->snapshot; ExecutorRun(queryDesc, direction, count); nprocessed = queryDesc->estate->es_processed; } if (!ScanDirectionIsNoMovement(direction)) --- 934,943 ---- nprocessed = RunFromStore(portal, direction, count, dest); else { ! PushActiveSnapshot(queryDesc->snapshot); ExecutorRun(queryDesc, direction, count); nprocessed = queryDesc->estate->es_processed; + PopActiveSnapshot(); } if (!ScanDirectionIsNoMovement(direction)) *************** PortalRunSelect(Portal portal, *** 982,990 **** nprocessed = RunFromStore(portal, direction, count, dest); else { ! ActiveSnapshot = queryDesc->snapshot; ExecutorRun(queryDesc, direction, count); nprocessed = queryDesc->estate->es_processed; } if (!ScanDirectionIsNoMovement(direction)) --- 977,986 ---- nprocessed = RunFromStore(portal, direction, count, dest); else { ! PushActiveSnapshot(queryDesc->snapshot); ExecutorRun(queryDesc, direction, count); nprocessed = queryDesc->estate->es_processed; + PopActiveSnapshot(); } if (!ScanDirectionIsNoMovement(direction)) *************** static void *** 1140,1145 **** --- 1136,1143 ---- PortalRunUtility(Portal portal, Node *utilityStmt, bool isTopLevel, DestReceiver *dest, char *completionTag) { + bool active_snapshot_set; + elog(DEBUG3, "ProcessUtility"); /* *************** PortalRunUtility(Portal portal, Node *ut *** 1152,1160 **** * hacks. Beware of listing anything that can modify the database --- if, * say, it has to update an index with expressions that invoke * user-defined functions, then it had better have a snapshot. - * - * Note we assume that caller will take care of restoring ActiveSnapshot - * on exit/error. */ if (!(IsA(utilityStmt, TransactionStmt) || IsA(utilityStmt, LockStmt) || --- 1150,1155 ---- *************** PortalRunUtility(Portal portal, Node *ut *** 1167,1175 **** IsA(utilityStmt, NotifyStmt) || IsA(utilityStmt, UnlistenStmt) || IsA(utilityStmt, CheckPointStmt))) ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); else ! ActiveSnapshot = NULL; ProcessUtility(utilityStmt, portal->sourceText, --- 1162,1173 ---- IsA(utilityStmt, NotifyStmt) || IsA(utilityStmt, UnlistenStmt) || IsA(utilityStmt, CheckPointStmt))) ! { ! PushActiveSnapshot(GetTransactionSnapshot()); ! active_snapshot_set = true; ! } else ! active_snapshot_set = false; ProcessUtility(utilityStmt, portal->sourceText, *************** PortalRunUtility(Portal portal, Node *ut *** 1181,1189 **** /* Some utility statements may change context on us */ MemoryContextSwitchTo(PortalGetHeapMemory(portal)); ! if (ActiveSnapshot) ! FreeSnapshot(ActiveSnapshot); ! ActiveSnapshot = NULL; } /* --- 1179,1193 ---- /* Some utility statements may change context on us */ MemoryContextSwitchTo(PortalGetHeapMemory(portal)); ! /* ! * Some utility commands may pop the ActiveSnapshot stack from under us, ! * so we only pop the stack if we actually see a snapshot set. Note that ! * the set of utility commands that do this must be the same set ! * disallowed to run inside a transaction; otherwise, we could be popping ! * a snapshot that belong to some other operation. ! */ ! if (active_snapshot_set && ActiveSnapshotSet()) ! PopActiveSnapshot(); } /* *************** PortalRunFetch(Portal portal, *** 1321,1327 **** { long result; Portal saveActivePortal; - Snapshot saveActiveSnapshot; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldContext; --- 1325,1330 ---- *************** PortalRunFetch(Portal portal, *** 1341,1353 **** * Set up global portal context pointers. */ saveActivePortal = ActivePortal; - saveActiveSnapshot = ActiveSnapshot; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; - ActiveSnapshot = NULL; /* will be set later */ CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); --- 1344,1354 ---- *************** PortalRunFetch(Portal portal, *** 1388,1394 **** /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; --- 1389,1394 ---- *************** PortalRunFetch(Portal portal, *** 1402,1408 **** portal->status = PORTAL_READY; ActivePortal = saveActivePortal; - ActiveSnapshot = saveActiveSnapshot; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; --- 1402,1407 ---- Index: src/backend/utils/adt/ri_triggers.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/adt/ri_triggers.c,v retrieving revision 1.107 diff -c -p -r1.107 ri_triggers.c *** src/backend/utils/adt/ri_triggers.c 26 Mar 2008 21:10:39 -0000 1.107 --- src/backend/utils/adt/ri_triggers.c 11 Apr 2008 17:08:54 -0000 *************** RI_Initial_Check(Trigger *trigger, Relat *** 2756,2767 **** /* * Run the plan. For safety we force a current snapshot to be used. (In * serializable mode, this arguably violates serializability, but we ! * really haven't got much choice.) We need at most one tuple returned, ! * so pass limit = 1. */ spi_result = SPI_execute_snapshot(qplan, NULL, NULL, ! CopySnapshot(GetLatestSnapshot()), InvalidSnapshot, true, false, 1); --- 2756,2768 ---- /* * Run the plan. For safety we force a current snapshot to be used. (In * serializable mode, this arguably violates serializability, but we ! * really haven't got much choice.) We don't need to register the ! * snapshot, because SPI_execute_snapshot will see to it. We need at most ! * one tuple returned, so pass limit = 1. */ spi_result = SPI_execute_snapshot(qplan, NULL, NULL, ! GetLatestSnapshot(), InvalidSnapshot, true, false, 1); *************** ri_PerformCheck(RI_QueryKey *qkey, SPIPl *** 3311,3323 **** * caller passes detectNewRows == false then it's okay to do the query * with the transaction snapshot; otherwise we use a current snapshot, and * tell the executor to error out if it finds any rows under the current ! * snapshot that wouldn't be visible per the transaction snapshot. */ if (IsXactIsoLevelSerializable && detectNewRows) { CommandCounterIncrement(); /* be sure all my own work is visible */ ! test_snapshot = CopySnapshot(GetLatestSnapshot()); ! crosscheck_snapshot = CopySnapshot(GetTransactionSnapshot()); } else { --- 3312,3326 ---- * caller passes detectNewRows == false then it's okay to do the query * with the transaction snapshot; otherwise we use a current snapshot, and * tell the executor to error out if it finds any rows under the current ! * snapshot that wouldn't be visible per the transaction snapshot. Note ! * that SPI_execute_snapshot will register the snapshots, so we don't need ! * to bother here. */ if (IsXactIsoLevelSerializable && detectNewRows) { CommandCounterIncrement(); /* be sure all my own work is visible */ ! test_snapshot = GetLatestSnapshot(); ! crosscheck_snapshot = GetTransactionSnapshot(); } else { Index: src/backend/utils/adt/txid.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/adt/txid.c,v retrieving revision 1.6 diff -c -p -r1.6 txid.c *** src/backend/utils/adt/txid.c 26 Mar 2008 18:48:59 -0000 1.6 --- src/backend/utils/adt/txid.c 11 Apr 2008 17:07:51 -0000 *************** txid_current_snapshot(PG_FUNCTION_ARGS) *** 362,368 **** TxidEpoch state; Snapshot cur; ! cur = ActiveSnapshot; if (cur == NULL) elog(ERROR, "txid_current_snapshot: ActiveSnapshot == NULL"); --- 362,368 ---- TxidEpoch state; Snapshot cur; ! cur = GetActiveSnapshot(); if (cur == NULL) elog(ERROR, "txid_current_snapshot: ActiveSnapshot == NULL"); Index: src/backend/utils/cache/plancache.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/cache/plancache.c,v retrieving revision 1.17 diff -c -p -r1.17 plancache.c *** src/backend/utils/cache/plancache.c 26 Mar 2008 18:48:59 -0000 1.17 --- src/backend/utils/cache/plancache.c 11 Apr 2008 17:07:51 -0000 *************** do_planning(List *querytrees, int cursor *** 550,580 **** /* * If a snapshot is already set (the normal case), we can just use that * for planning. But if it isn't, we have to tell pg_plan_queries to make ! * a snap if it needs one. In that case we should arrange to reset ! * ActiveSnapshot afterward, to ensure that RevalidateCachedPlan has no ! * caller-visible effects on the snapshot. Having to replan is an unusual ! * case, and it seems a really bad idea for RevalidateCachedPlan to affect ! * the snapshot only in unusual cases. (Besides, the snap might have been ! * created in a short-lived context.) */ ! if (ActiveSnapshot != NULL) ! stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, false); ! else ! { ! PG_TRY(); ! { ! stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, true); ! } ! PG_CATCH(); ! { ! /* Restore global vars and propagate error */ ! ActiveSnapshot = NULL; ! PG_RE_THROW(); ! } ! PG_END_TRY(); ! ! ActiveSnapshot = NULL; ! } return stmt_list; } --- 550,560 ---- /* * If a snapshot is already set (the normal case), we can just use that * for planning. But if it isn't, we have to tell pg_plan_queries to make ! * a snap if it needs one. pg_plan_queries is also responsible for making ! * sure ActiveSnapshot is reset in that case. */ ! stmt_list = pg_plan_queries(querytrees, cursorOptions, NULL, ! !ActiveSnapshotSet()); return stmt_list; } Index: src/backend/utils/time/snapmgr.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/backend/utils/time/snapmgr.c,v retrieving revision 1.1 diff -c -p -r1.1 snapmgr.c *** src/backend/utils/time/snapmgr.c 26 Mar 2008 18:48:59 -0000 1.1 --- src/backend/utils/time/snapmgr.c 14 Apr 2008 23:44:45 -0000 *************** *** 14,22 **** --- 14,25 ---- #include "access/xact.h" #include "access/transam.h" + #include "storage/proc.h" #include "storage/procarray.h" + #include "utils/memutils.h" #include "utils/snapmgr.h" #include "utils/tqual.h" + #include "utils/memutils.h" /* *************** Snapshot SerializableSnapshot = NULL; *** 31,43 **** Snapshot LatestSnapshot = NULL; /* - * This pointer is not maintained by this module, but it's convenient - * to declare it here anyway. Callers typically assign a copy of - * GetTransactionSnapshot's result to ActiveSnapshot. - */ - Snapshot ActiveSnapshot = NULL; - - /* * These are updated by GetSnapshotData. We initialize them this way * for the convenience of TransactionIdIsInProgress: even in bootstrap * mode, we don't want it to say that BootstrapTransactionId is in progress. --- 34,39 ---- *************** TransactionId TransactionXmin = FirstNor *** 46,51 **** --- 42,71 ---- TransactionId RecentXmin = FirstNormalTransactionId; TransactionId RecentGlobalXmin = FirstNormalTransactionId; + /* Elements of the active snapshot stack */ + typedef struct ActiveSnapshotElt + { + Snapshot as_snap; + char *as_name; + struct ActiveSnapshotElt *as_next; + } ActiveSnapshotElt; + + /* Elements of the list of registered snapshots */ + typedef struct RegdSnapshotElt + { + Snapshot s_snap; + struct RegdSnapshotElt *s_next; + } RegdSnapshotElt; + + /* Head of the list of registered snapshots */ + static RegdSnapshotElt *RegisteredSnapshotList = NULL; + + /* Top of the stack of active snapshots */ + static ActiveSnapshotElt *ActiveSnapshot = NULL; + + /* Memory context where tracked snapshots reside */ + static MemoryContext SnapshotContext = NULL; + /* * GetTransactionSnapshot *************** GetLatestSnapshot(void) *** 95,104 **** } /* * CopySnapshot * Copy the given snapshot. * ! * The copy is palloc'd in the current memory context. */ Snapshot CopySnapshot(Snapshot snapshot) --- 115,141 ---- } /* + * Initialize the private memory context for snapshots + */ + static void + SnapshotMemoryInit(void) + { + if (SnapshotContext != NULL) + return; + + SnapshotContext = AllocSetContextCreate(TopTransactionContext, + "Snapshot Context", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + } + + /* * CopySnapshot * Copy the given snapshot. * ! * The copy is palloc'd in SnapshotContext and has initial refcounts and ! * subtransaction level set to 0. These snapshots have the copied flag set. */ Snapshot CopySnapshot(Snapshot snapshot) *************** CopySnapshot(Snapshot snapshot) *** 107,121 **** Size subxipoff; Size size; /* We allocate any XID arrays needed in the same palloc block. */ size = subxipoff = sizeof(SnapshotData) + snapshot->xcnt * sizeof(TransactionId); if (snapshot->subxcnt > 0) size += snapshot->subxcnt * sizeof(TransactionId); ! newsnap = (Snapshot) palloc(size); memcpy(newsnap, snapshot, sizeof(SnapshotData)); /* setup XID array */ if (snapshot->xcnt > 0) { --- 144,168 ---- Size subxipoff; Size size; + Assert(snapshot != InvalidSnapshot); + + if (SnapshotContext == NULL) + SnapshotMemoryInit(); + /* We allocate any XID arrays needed in the same palloc block. */ size = subxipoff = sizeof(SnapshotData) + snapshot->xcnt * sizeof(TransactionId); if (snapshot->subxcnt > 0) size += snapshot->subxcnt * sizeof(TransactionId); ! newsnap = (Snapshot) MemoryContextAlloc(SnapshotContext, size); memcpy(newsnap, snapshot, sizeof(SnapshotData)); + newsnap->regd_count = 0; + newsnap->active_count = 0; + newsnap->level = 0; + newsnap->copied = true; + /* setup XID array */ if (snapshot->xcnt > 0) { *************** CopySnapshot(Snapshot snapshot) *** 141,156 **** /* * FreeSnapshot ! * Free a snapshot previously copied with CopySnapshot. * ! * This is currently identical to pfree, but is provided for cleanliness. ! * ! * Do *not* apply this to the results of GetTransactionSnapshot or ! * GetLatestSnapshot, since those are just static structs. */ void FreeSnapshot(Snapshot snapshot) { pfree(snapshot); } --- 188,204 ---- /* * FreeSnapshot ! * Free the memory associated with a snapshot. * ! * There shouldn't be any need to call this outside this module, as it is ! * called as soon as the snapshot's refcount goes down to 0. */ void FreeSnapshot(Snapshot snapshot) { + Assert(snapshot->regd_count == 0); + Assert(snapshot->active_count == 0); + pfree(snapshot); } *************** FreeXactSnapshot(void) *** 168,172 **** */ SerializableSnapshot = NULL; LatestSnapshot = NULL; ! ActiveSnapshot = NULL; /* just for cleanliness */ } --- 216,533 ---- */ SerializableSnapshot = NULL; LatestSnapshot = NULL; ! } ! ! /* ! * PushActiveSnapshot ! * Set the given snapshot as the current active snapshot ! * ! * If this is the first use of this snapshot, create a new long-lived copy with ! * active refcount=1. Otherwise, only increment the refcount. ! */ ! void ! PushActiveSnapshot(Snapshot snap) ! { ! ActiveSnapshotElt *newactive; ! ! Assert(snap != InvalidSnapshot); ! ! if (SnapshotContext == NULL) ! SnapshotMemoryInit(); ! ! /* Static snapshot? Create a persistent copy */ ! snap = snap->copied ? snap : CopySnapshot(snap); ! ! newactive = MemoryContextAlloc(SnapshotContext, sizeof(ActiveSnapshotElt)); ! newactive->as_snap = snap; ! newactive->as_next = ActiveSnapshot; ! ! if (newactive->as_snap->level == 0) ! newactive->as_snap->level = GetCurrentTransactionNestLevel(); ! newactive->as_snap->active_count++; ! ! ActiveSnapshot = newactive; ! } ! ! /* ! * PopActiveSnapshot ! * ! * Remove the topmost snapshot from the active snapshot stack, decrementing the ! * reference count, and free it if this was the last reference. ! */ ! void ! PopActiveSnapshot(void) ! { ! ActiveSnapshotElt *newstack; ! ! newstack = ActiveSnapshot->as_next; ! ! ActiveSnapshot->as_snap->active_count--; ! ! if (ActiveSnapshot->as_snap->active_count == 0 && ! ActiveSnapshot->as_snap->regd_count == 0) ! FreeSnapshot(ActiveSnapshot->as_snap); ! ! ActiveSnapshot = newstack; ! } ! ! Snapshot ! GetActiveSnapshot(void) ! { ! Assert(ActiveSnapshot != NULL); ! ! return ActiveSnapshot->as_snap; ! } ! ! bool ! ActiveSnapshotSet(void) ! { ! return ActiveSnapshot != NULL; ! } ! ! /* ! * RegisterSnapshot ! * Register a snapshot as being in use ! * ! * This registers a snapshot on our list. If the snapshot has been registered ! * previously, increment the reference count. ! * ! * If InvalidSnapshot or a non-MVCC snapshot is passed, it is not registered. ! * XXX -- should it be a hard error if a non-MVCC snapshot is passed? ! */ ! Snapshot ! RegisterSnapshot(Snapshot snapshot) ! { ! RegdSnapshotElt *elt; ! RegdSnapshotElt *newhead; ! ! if (snapshot == InvalidSnapshot) ! return InvalidSnapshot; ! ! SnapshotMemoryInit(); ! ! for (elt = RegisteredSnapshotList; elt != NULL; elt = elt->s_next) ! { ! if (elt->s_snap == snapshot) ! { ! elt->s_snap->regd_count++; ! return elt->s_snap; ! } ! } ! ! /* ! * Create the new list element. If the active count of the snapshot is ! * zero, then we must copy the snapshot to persistent memory (we already ! * checked that it is not on the registered list either, so it must be ! * new). Otherwise we can just increment the reference count. ! */ ! newhead = MemoryContextAlloc(SnapshotContext, sizeof(RegdSnapshotElt)); ! newhead->s_next = RegisteredSnapshotList; ! /* Static snapshot? Create a persistent copy */ ! newhead->s_snap = snapshot->copied ? snapshot : CopySnapshot(snapshot); ! ! if (newhead->s_snap->level == 0) ! newhead->s_snap->level = GetCurrentTransactionNestLevel(); ! newhead->s_snap->regd_count++; ! ! RegisteredSnapshotList = newhead; ! ! return RegisteredSnapshotList->s_snap; ! } ! ! /* ! * UnregisterSnapshot ! * Signals that a snapshot is no longer necessary ! * ! * If both reference counts falls to zero, the snapshot memory is released. ! * If only the registered list refcount falls to zero, just the list element is ! * freed. ! */ ! void ! UnregisterSnapshot(Snapshot snapshot) ! { ! RegdSnapshotElt *prev = NULL; ! RegdSnapshotElt *elt; ! bool found = false; ! ! if (snapshot == InvalidSnapshot) ! return; ! ! for (elt = RegisteredSnapshotList; elt != NULL; elt = elt->s_next) ! { ! if (elt->s_snap == snapshot) ! { ! elt->s_snap->regd_count--; ! found = true; ! ! if (elt->s_snap->regd_count == 0) ! { ! /* delink it from the registered snapshot list */ ! if (prev) ! prev->s_next = elt->s_next; ! else ! RegisteredSnapshotList = elt->s_next; ! ! /* free the snapshot itself if it's no longer relevant */ ! if (elt->s_snap->active_count == 0) ! FreeSnapshot(elt->s_snap); ! ! /* and free the list element */ ! pfree(elt); ! } ! ! break; ! } ! ! prev = elt; ! } ! ! if (!found) ! elog(WARNING, "unregistering failed for snapshot %p", snapshot); ! } ! ! /* ! * AtSubCommit_Snapshot ! */ ! void ! AtSubCommit_Snapshot(void) ! { ! ActiveSnapshotElt *active; ! RegdSnapshotElt *regd; ! int level; ! ! if (SnapshotContext == NULL) ! return; ! ! level = GetCurrentTransactionNestLevel(); ! ! /* ! * Relabel the active snapshots set in this subtransaction as though they ! * are owned by the parent subxact. ! */ ! for (active = ActiveSnapshot; active != NULL; active = active->as_next) ! { ! if (active->as_snap->level < level) ! break; ! active->as_snap->level = level - 1; ! } ! ! /* Reassign all registered snapshots to the parent subxact. */ ! for (regd = RegisteredSnapshotList; regd != NULL; regd = regd->s_next) ! { ! if (regd->s_snap->level == level) ! regd->s_snap->level--; ! } ! } ! ! /* ! * AtSubAbort_Snapshot ! * Clean up snapshots after a subtransaction abort ! */ ! void ! AtSubAbort_Snapshot(void) ! { ! int level; ! RegdSnapshotElt *prev; ! RegdSnapshotElt *regd; ! ! level = GetCurrentTransactionNestLevel(); ! ! /* Forget the active snapshots set by this subtransaction */ ! while (ActiveSnapshot && ActiveSnapshot->as_snap->level >= level) ! { ! ActiveSnapshotElt *next; ! ! next = ActiveSnapshot->as_next; ! ! /* ! * If it's still registered, we can't free it yet; mark it as not ! * active so that it's freed below. Otherwise do away with it. ! */ ! ActiveSnapshot->as_snap->active_count = 0; ! if (ActiveSnapshot->as_snap->regd_count == 0) ! FreeSnapshot(ActiveSnapshot->as_snap); ! ! /* and free the stack element */ ! pfree(ActiveSnapshot); ! ! ActiveSnapshot = next; ! } ! ! /* Unregister all snapshots registered during this subtransaction */ ! prev = NULL; ! for (regd = RegisteredSnapshotList; regd != NULL; ) ! { ! if (regd->s_snap->level == level) ! { ! RegdSnapshotElt *tofree; ! ! if (prev) ! prev->s_next = regd->s_next; ! else ! RegisteredSnapshotList = regd->s_next; ! ! tofree = regd; ! regd = regd->s_next; ! ! /* must be OK to free at this point */ ! Assert(tofree->s_snap->active_count == 0); ! tofree->s_snap->regd_count = 0; ! FreeSnapshot(tofree->s_snap); ! ! /* and free the list element */ ! pfree(tofree); ! } ! else ! { ! prev = regd; ! regd = regd->s_next; ! } ! } ! } ! ! /* ! * AtEOXact_Snapshot ! * Snapshot manager's cleanup function for end of transaction ! * ! * We just need to reset our memory context -- the memory itself will be freed ! * with TopTransactionContext. ! */ ! void ! AtEOXact_Snapshot(bool isCommit) ! { ! if (SnapshotContext == NULL) ! return; ! ! /* On commit, complain about leftover snapshots */ ! if (isCommit) ! { ! ActiveSnapshotElt *active; ! RegdSnapshotElt *regd; ! ! /* complain about unpopped active snapshots */ ! for (active = ActiveSnapshot; active != NULL; active = active->as_next) ! { ! ereport(WARNING, ! (errmsg("snapshot %p still active", active))); ! } ! ! ! /* complain about any unregistered snapshot */ ! for (regd = RegisteredSnapshotList; regd != NULL; regd = regd->s_next) ! { ! ereport(WARNING, ! (errmsg("snapshot %p not destroyed at commit (%d regd refs, %d active refs)", ! regd->s_snap, regd->s_snap->regd_count, ! regd->s_snap->active_count))); ! } ! } ! ! /* ! * And forget about them. We don't need to reset the context -- it'll ! * go away with TopTransactionContext. ! */ ! SnapshotContext = NULL; ! ActiveSnapshot = NULL; ! RegisteredSnapshotList = NULL; } Index: src/include/utils/snapmgr.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/utils/snapmgr.h,v retrieving revision 1.1 diff -c -p -r1.1 snapmgr.h *** src/include/utils/snapmgr.h 26 Mar 2008 18:48:59 -0000 1.1 --- src/include/utils/snapmgr.h 11 Apr 2008 19:49:26 -0000 *************** *** 18,24 **** extern PGDLLIMPORT Snapshot SerializableSnapshot; extern PGDLLIMPORT Snapshot LatestSnapshot; - extern PGDLLIMPORT Snapshot ActiveSnapshot; extern TransactionId TransactionXmin; extern TransactionId RecentXmin; --- 18,23 ---- *************** extern Snapshot CopySnapshot(Snapshot sn *** 30,33 **** --- 29,44 ---- extern void FreeSnapshot(Snapshot snapshot); extern void FreeXactSnapshot(void); + extern void PushActiveSnapshot(Snapshot snapshot); + extern void PopActiveSnapshot(void); + extern Snapshot GetActiveSnapshot(void); + extern bool ActiveSnapshotSet(void); + + extern Snapshot RegisterSnapshot(Snapshot snapshot); + extern void UnregisterSnapshot(Snapshot snapshot); + + extern void AtSubCommit_Snapshot(void); + extern void AtSubAbort_Snapshot(void); + extern void AtEOXact_Snapshot(bool isCommit); + #endif /* SNAPMGR_H */ Index: src/include/utils/snapshot.h =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/include/utils/snapshot.h,v retrieving revision 1.2 diff -c -p -r1.2 snapshot.h *** src/include/utils/snapshot.h 26 Mar 2008 21:10:39 -0000 1.2 --- src/include/utils/snapshot.h 14 Apr 2008 23:45:34 -0000 *************** typedef struct SnapshotData *** 57,62 **** --- 57,66 ---- * out any that are >= xmax */ CommandId curcid; /* in my xact, CID < curcid are visible */ + uint16 active_count; /* refcount on ActiveSnapshot stack */ + uint16 regd_count; /* refcount on RegisteredSnapshotList */ + int level; /* creating subtransaction level */ + bool copied; /* false if it's a static snapshot */ } SnapshotData; /* Index: src/pl/plpgsql/src/pl_exec.c =================================================================== RCS file: /home/alvherre/Code/cvs/pgsql/src/pl/plpgsql/src/pl_exec.c,v retrieving revision 1.209 diff -c -p -r1.209 pl_exec.c *** src/pl/plpgsql/src/pl_exec.c 6 Apr 2008 23:43:29 -0000 1.209 --- src/pl/plpgsql/src/pl_exec.c 11 Apr 2008 17:08:54 -0000 *************** exec_eval_simple_expr(PLpgSQL_execstate *** 4164,4170 **** CachedPlan *cplan; ParamListInfo paramLI; int i; ! Snapshot saveActiveSnapshot; /* * Forget it if expression wasn't simple before. --- 4164,4170 ---- CachedPlan *cplan; ParamListInfo paramLI; int i; ! MemoryContext oldcontext; /* * Forget it if expression wasn't simple before. *************** exec_eval_simple_expr(PLpgSQL_execstate *** 4253,4269 **** * updates made so far by our own function. */ SPI_push(); - saveActiveSnapshot = ActiveSnapshot; - - PG_TRY(); - { - MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); if (!estate->readonly_func) { CommandCounterIncrement(); ! ActiveSnapshot = CopySnapshot(GetTransactionSnapshot()); } /* --- 4253,4264 ---- * updates made so far by our own function. */ SPI_push(); oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); if (!estate->readonly_func) { CommandCounterIncrement(); ! PushActiveSnapshot(GetTransactionSnapshot()); } /* *************** exec_eval_simple_expr(PLpgSQL_execstate *** 4274,4289 **** isNull, NULL); MemoryContextSwitchTo(oldcontext); - } - PG_CATCH(); - { - /* Restore global vars and propagate error */ - ActiveSnapshot = saveActiveSnapshot; - PG_RE_THROW(); - } - PG_END_TRY(); ! ActiveSnapshot = saveActiveSnapshot; SPI_pop(); /* --- 4269,4278 ---- isNull, NULL); MemoryContextSwitchTo(oldcontext); ! if (!estate->readonly_func) ! PopActiveSnapshot(); ! SPI_pop(); /*