1 /*-------------------------------------------------------------------------
4 * top level transaction system support routines
6 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.99 2001/03/13 01:17:05 tgl Exp $
14 * Transaction aborts can now occur two ways:
16 * 1) system dies from some internal cause (Assert, etc..)
19 * These two cases used to be treated identically, but now
20 * we need to distinguish them. Why? consider the following
25 * 1) user types BEGIN 1) user types BEGIN
26 * 2) user does something 2) user does something
27 * 3) user does not like what 3) system aborts for some reason
28 * she sees and types ABORT
30 * In case 1, we want to abort the transaction and return to the
31 * default state. In case 2, there may be more commands coming
32 * our way which are part of the same transaction block and we have
33 * to ignore these commands until we see an END transaction.
34 * (or an ABORT! --djm)
36 * Internal aborts are now handled by AbortTransactionBlock(), just as
37 * they always have been, and user aborts are now handled by
38 * UserAbortTransactionBlock(). Both of them rely on AbortTransaction()
39 * to do all the real work. The only difference is what state we
40 * enter after AbortTransaction() does its work:
42 * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
43 * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
45 * Low-level transaction abort handling is divided into two phases:
46 * * AbortTransaction() executes as soon as we realize the transaction
47 * has failed. It should release all shared resources (locks etc)
48 * so that we do not delay other backends unnecessarily.
49 * * CleanupTransaction() executes when we finally see a user COMMIT
50 * or ROLLBACK command; it cleans things up and gets us out of
51 * the transaction internally. In particular, we mustn't destroy
52 * TransactionCommandContext until this point.
55 * This file is an attempt at a redesign of the upper layer
56 * of the V1 transaction system which was too poorly thought
57 * out to describe. This new system hopes to be both simpler
58 * in design, simpler to extend and needs to contain added
59 * functionality to solve problems beyond the scope of the V1
60 * system. (In particuler, communication of transaction
61 * information between parallel backends has to be supported)
63 * The essential aspects of the transaction system are:
65 * o transaction id generation
66 * o transaction log updating
68 * o cache invalidation
71 * Hence, the functional division of the transaction code is
72 * based on what of the above things need to be done during
73 * a start/commit/abort transaction. For instance, the
74 * routine AtCommit_Memory() takes care of all the memory
75 * cleanup stuff done at commit time.
77 * The code is layered as follows:
84 * are provided to do the lower level work like recording
85 * the transaction status in the log and doing memory cleanup.
86 * above these routines are another set of functions:
88 * StartTransactionCommand
89 * CommitTransactionCommand
90 * AbortCurrentTransaction
92 * These are the routines used in the postgres main processing
93 * loop. They are sensitive to the current transaction block state
94 * and make calls to the lower level routines appropriately.
96 * Support for transaction blocks is provided via the functions:
98 * StartTransactionBlock
99 * CommitTransactionBlock
100 * AbortTransactionBlock
102 * These are invoked only in responce to a user "BEGIN", "END",
103 * or "ABORT" command. The tricky part about these functions
104 * is that they are called within the postgres main loop, in between
105 * the StartTransactionCommand() and CommitTransactionCommand().
107 * For example, consider the following sequence of user commands:
110 * 2) retrieve (foo.all)
111 * 3) append foo (bar = baz)
114 * in the main processing loop, this results in the following
115 * transaction sequence:
117 * / StartTransactionCommand();
118 * 1) / ProcessUtility(); << begin
119 * \ StartTransactionBlock();
120 * \ CommitTransactionCommand();
122 * / StartTransactionCommand();
123 * 2) < ProcessQuery(); << retrieve (foo.all)
124 * \ CommitTransactionCommand();
126 * / StartTransactionCommand();
127 * 3) < ProcessQuery(); << append foo (bar = baz)
128 * \ CommitTransactionCommand();
130 * / StartTransactionCommand();
131 * 4) / ProcessUtility(); << end
132 * \ CommitTransactionBlock();
133 * \ CommitTransactionCommand();
135 * The point of this example is to demonstrate the need for
136 * StartTransactionCommand() and CommitTransactionCommand() to
137 * be state smart -- they should do nothing in between the calls
138 * to StartTransactionBlock() and EndTransactionBlock() and
139 * outside these calls they need to do normal start/commit
142 * Furthermore, suppose the "retrieve (foo.all)" caused an abort
143 * condition. We would then want to abort the transaction and
144 * ignore all subsequent commands up to the "end".
147 *-------------------------------------------------------------------------
151 * Large object clean up added in CommitTransaction() to prevent buffer leaks.
153 * [PA] is Pascal André <andre@via.ecp.fr>
155 #include "postgres.h"
157 #include <sys/time.h>
159 #include "access/nbtree.h"
160 #include "access/xact.h"
161 #include "catalog/heap.h"
162 #include "catalog/index.h"
163 #include "commands/async.h"
164 #include "commands/sequence.h"
165 #include "commands/trigger.h"
166 #include "executor/spi.h"
167 #include "libpq/be-fsstubs.h"
168 #include "miscadmin.h"
169 #include "storage/proc.h"
170 #include "storage/sinval.h"
171 #include "storage/smgr.h"
172 #include "utils/inval.h"
173 #include "utils/memutils.h"
174 #include "utils/portal.h"
175 #include "utils/catcache.h"
176 #include "utils/relcache.h"
177 #include "utils/temprel.h"
179 extern bool SharedBufferChanged;
181 static void AbortTransaction(void);
182 static void AtAbort_Cache(void);
183 static void AtAbort_Locks(void);
184 static void AtAbort_Memory(void);
185 static void AtCleanup_Memory(void);
186 static void AtCommit_Cache(void);
187 static void AtCommit_LocalCache(void);
188 static void AtCommit_Locks(void);
189 static void AtCommit_Memory(void);
190 static void AtStart_Cache(void);
191 static void AtStart_Locks(void);
192 static void AtStart_Memory(void);
193 static void CleanupTransaction(void);
194 static void CommitTransaction(void);
195 static void RecordTransactionAbort(void);
196 static void StartTransaction(void);
199 * global variables holding the current transaction state.
201 * Note: when we are running several slave processes, the
202 * current transaction state data is copied into shared memory
203 * and the CurrentTransactionState pointer changed to
204 * point to the shared copy. All this occurrs in slaves.c
207 TransactionStateData CurrentTransactionStateData = {
208 0, /* transaction id */
209 FirstCommandId, /* command id */
210 0, /* scan command id */
211 0x0, /* start time */
212 TRANS_DEFAULT, /* transaction state */
213 TBLOCK_DEFAULT /* transaction block state */
216 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
219 * User-tweakable parameters
221 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
224 int CommitDelay = 0; /* precommit delay in microseconds */
225 int CommitSiblings = 5; /* number of concurrent xacts needed to sleep */
227 static void (*_RollbackFunc)(void*) = NULL;
228 static void *_RollbackData = NULL;
231 * info returned when the system is disabled
233 * Apparently a lot of this code is inherited from other prototype systems.
234 * For DisabledStartTime, use a symbolic value to make the relationships clearer.
235 * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
236 * every day. It appears that if we return a value guaranteed larger than
237 * any real time associated with a transaction then comparisons in other
238 * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
240 * Note: I have no idea what the significance of the
241 * 1073741823 in DisabledStartTime.. I just carried
242 * this over when converting things from the old
243 * V1 transaction system. -cim 3/18/90
246 TransactionId DisabledTransactionId = (TransactionId) -1;
248 CommandId DisabledCommandId = (CommandId) -1;
250 AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
256 bool CommandIdCounterOverflowFlag;
259 * catalog creation transaction bootstrapping flag.
260 * This should be eliminated and added to the transaction
261 * state stuff. -cim 3/19/90
264 bool AMI_OVERRIDE = false;
266 /* ----------------------------------------------------------------
267 * transaction state accessors
268 * ----------------------------------------------------------------
271 /* --------------------------------
272 * TranactionFlushEnabled()
273 * SetTransactionFlushEnabled()
275 * These are used to test and set the "TransactionFlushState"
276 * varable. If this variable is true (the default), then
277 * the system will flush all dirty buffers to disk at the end
278 * of each transaction. If false then we are assuming the
279 * buffer pool resides in stable main memory, in which case we
280 * only do writes as necessary.
281 * --------------------------------
283 static int TransactionFlushState = 1;
286 TransactionFlushEnabled(void)
288 return TransactionFlushState;
293 SetTransactionFlushEnabled(bool state)
295 TransactionFlushState = (state == true);
299 /* --------------------------------
302 * This returns true if we are currently running a query
303 * within an executing transaction.
304 * --------------------------------
307 IsTransactionState(void)
309 TransactionState s = CurrentTransactionState;
317 case TRANS_INPROGRESS:
328 * Shouldn't get here, but lint is not happy with this...
335 /* --------------------------------
336 * IsAbortedTransactionBlockState
338 * This returns true if we are currently running a query
339 * within an aborted transaction block.
340 * --------------------------------
343 IsAbortedTransactionBlockState(void)
345 TransactionState s = CurrentTransactionState;
347 if (s->blockState == TBLOCK_ABORT)
353 /* --------------------------------
354 * OverrideTransactionSystem
356 * This is used to temporarily disable the transaction
357 * processing system in order to do initialization of
358 * the transaction system data structures and relations
360 * --------------------------------
362 int SavedTransactionState;
365 OverrideTransactionSystem(bool flag)
367 TransactionState s = CurrentTransactionState;
371 if (s->state == TRANS_DISABLED)
374 SavedTransactionState = s->state;
375 s->state = TRANS_DISABLED;
379 if (s->state != TRANS_DISABLED)
382 s->state = SavedTransactionState;
386 /* --------------------------------
387 * GetCurrentTransactionId
389 * This returns the id of the current transaction, or
390 * the id of the "disabled" transaction.
391 * --------------------------------
394 GetCurrentTransactionId(void)
396 TransactionState s = CurrentTransactionState;
399 * if the transaction system is disabled, we return
400 * the special "disabled" transaction id.
403 if (s->state == TRANS_DISABLED)
404 return (TransactionId) DisabledTransactionId;
407 * otherwise return the current transaction id.
410 return (TransactionId) s->transactionIdData;
414 /* --------------------------------
415 * GetCurrentCommandId
416 * --------------------------------
419 GetCurrentCommandId(void)
421 TransactionState s = CurrentTransactionState;
424 * if the transaction system is disabled, we return
425 * the special "disabled" command id.
428 if (s->state == TRANS_DISABLED)
429 return (CommandId) DisabledCommandId;
435 GetScanCommandId(void)
437 TransactionState s = CurrentTransactionState;
440 * if the transaction system is disabled, we return
441 * the special "disabled" command id.
444 if (s->state == TRANS_DISABLED)
445 return (CommandId) DisabledCommandId;
447 return s->scanCommandId;
451 /* --------------------------------
452 * GetCurrentTransactionStartTime
453 * --------------------------------
456 GetCurrentTransactionStartTime(void)
458 TransactionState s = CurrentTransactionState;
461 * if the transaction system is disabled, we return
462 * the special "disabled" starting time.
465 if (s->state == TRANS_DISABLED)
466 return (AbsoluteTime) DisabledStartTime;
472 /* --------------------------------
473 * TransactionIdIsCurrentTransactionId
474 * --------------------------------
477 TransactionIdIsCurrentTransactionId(TransactionId xid)
479 TransactionState s = CurrentTransactionState;
485 TransactionIdEquals(xid, s->transactionIdData);
489 /* --------------------------------
490 * CommandIdIsCurrentCommandId
491 * --------------------------------
494 CommandIdIsCurrentCommandId(CommandId cid)
496 TransactionState s = CurrentTransactionState;
501 return (cid == s->commandId) ? true : false;
505 CommandIdGEScanCommandId(CommandId cid)
507 TransactionState s = CurrentTransactionState;
512 return (cid >= s->scanCommandId) ? true : false;
516 /* --------------------------------
517 * ClearCommandIdCounterOverflowFlag
518 * --------------------------------
522 ClearCommandIdCounterOverflowFlag(void)
524 CommandIdCounterOverflowFlag = false;
529 /* --------------------------------
530 * CommandCounterIncrement
531 * --------------------------------
534 CommandCounterIncrement(void)
536 CurrentTransactionStateData.commandId += 1;
537 if (CurrentTransactionStateData.commandId == FirstCommandId)
539 CommandIdCounterOverflowFlag = true;
540 elog(ERROR, "You may only have 2^32-1 commands per transaction");
543 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
546 * make cache changes visible to me. AtCommit_LocalCache() instead of
547 * AtCommit_Cache() is called here.
549 AtCommit_LocalCache();
555 SetScanCommandId(CommandId savedId)
558 CurrentTransactionStateData.scanCommandId = savedId;
562 /* ----------------------------------------------------------------
563 * initialization stuff
564 * ----------------------------------------------------------------
567 InitializeTransactionSystem(void)
569 InitializeTransactionLog();
572 /* ----------------------------------------------------------------
573 * StartTransaction stuff
574 * ----------------------------------------------------------------
577 /* --------------------------------
579 * --------------------------------
587 /* --------------------------------
589 * --------------------------------
596 * at present, it is unknown to me what belongs here -cim 3/18/90
598 * There isn't anything to do at the start of a xact for locks. -mer
603 /* --------------------------------
605 * --------------------------------
611 * We shouldn't have any transaction contexts already.
614 Assert(TopTransactionContext == NULL);
615 Assert(TransactionCommandContext == NULL);
618 * Create a toplevel context for the transaction.
621 TopTransactionContext =
622 AllocSetContextCreate(TopMemoryContext,
623 "TopTransactionContext",
624 ALLOCSET_DEFAULT_MINSIZE,
625 ALLOCSET_DEFAULT_INITSIZE,
626 ALLOCSET_DEFAULT_MAXSIZE);
629 * Create a statement-level context and make it active.
632 TransactionCommandContext =
633 AllocSetContextCreate(TopTransactionContext,
634 "TransactionCommandContext",
635 ALLOCSET_DEFAULT_MINSIZE,
636 ALLOCSET_DEFAULT_INITSIZE,
637 ALLOCSET_DEFAULT_MAXSIZE);
638 MemoryContextSwitchTo(TransactionCommandContext);
642 /* ----------------------------------------------------------------
643 * CommitTransaction stuff
644 * ----------------------------------------------------------------
647 /* --------------------------------
648 * RecordTransactionCommit
650 * Note: the two calls to BufferManagerFlush() exist to ensure
651 * that data pages are written before log pages. These
652 * explicit calls should be replaced by a more efficient
653 * ordered page write scheme in the buffer manager
655 * --------------------------------
658 RecordTransactionCommit()
663 xid = GetCurrentTransactionId();
665 leak = BufferPoolCheckLeak();
667 if (MyLastRecPtr.xrecoff != 0)
670 xl_xact_commit xlrec;
675 xlrec.xtime = time(NULL);
676 rdata.buffer = InvalidBuffer;
677 rdata.data = (char *)(&xlrec);
678 rdata.len = SizeOfXactCommit;
681 START_CRIT_SECTION();
683 * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
685 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
688 * Sleep before commit! So we can flush more than one
689 * commit records per single fsync. (The idea is some other
690 * backend may do the XLogFlush while we're sleeping. This
691 * needs work still, because on most Unixen, the minimum
692 * select() delay is 10msec or more, which is way too long.)
694 * We do not sleep if enableFsync is not turned on, nor if there
695 * are fewer than CommitSiblings other backends with active
698 if (CommitDelay > 0 && enableFsync &&
699 CountActiveBackends() >= CommitSiblings)
701 struct timeval delay;
704 delay.tv_usec = CommitDelay;
705 (void) select(0, NULL, NULL, NULL, &delay);
710 /* Break the chain of back-links in the XLOG records I output */
711 MyLastRecPtr.xrecoff = 0;
713 TransactionIdCommit(xid);
718 /* Show myself as out of the transaction in PROC array */
719 MyProc->logRec.xrecoff = 0;
722 ResetBufferPool(true);
726 /* --------------------------------
728 * --------------------------------
734 * Make catalog changes visible to all backend.
737 RegisterInvalid(true);
740 /* --------------------------------
741 * AtCommit_LocalCache
742 * --------------------------------
745 AtCommit_LocalCache(void)
748 * Make catalog changes visible to me for the next command.
751 ImmediateLocalInvalidation(true);
754 /* --------------------------------
756 * --------------------------------
762 * XXX What if ProcReleaseLocks fails? (race condition?)
764 * Then you're up a creek! -mer 5/24/92
767 ProcReleaseLocks(true);
770 /* --------------------------------
772 * --------------------------------
775 AtCommit_Memory(void)
778 * Now that we're "out" of a transaction, have the
779 * system allocate things in the top memory context instead
780 * of per-transaction contexts.
783 MemoryContextSwitchTo(TopMemoryContext);
786 * Release all transaction-local memory.
789 Assert(TopTransactionContext != NULL);
790 MemoryContextDelete(TopTransactionContext);
791 TopTransactionContext = NULL;
792 TransactionCommandContext = NULL;
795 /* ----------------------------------------------------------------
796 * AbortTransaction stuff
797 * ----------------------------------------------------------------
800 /* --------------------------------
801 * RecordTransactionAbort
802 * --------------------------------
805 RecordTransactionAbort(void)
807 TransactionId xid = GetCurrentTransactionId();
810 * Double check here is to catch case that we aborted partway through
811 * RecordTransactionCommit ...
813 if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
819 xlrec.xtime = time(NULL);
820 rdata.buffer = InvalidBuffer;
821 rdata.data = (char *)(&xlrec);
822 rdata.len = SizeOfXactAbort;
825 START_CRIT_SECTION();
827 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
829 TransactionIdAbort(xid);
834 /* Break the chain of back-links in the XLOG records I output */
835 MyLastRecPtr.xrecoff = 0;
836 /* Show myself as out of the transaction in PROC array */
837 MyProc->logRec.xrecoff = 0;
840 * Tell bufmgr and smgr to release resources.
842 ResetBufferPool(false); /* false -> is abort */
845 /* --------------------------------
847 * --------------------------------
852 RelationCacheAbort();
853 RegisterInvalid(false);
856 /* --------------------------------
858 * --------------------------------
864 * XXX What if ProcReleaseLocks() fails? (race condition?)
866 * Then you're up a creek without a paddle! -mer
869 ProcReleaseLocks(false);
873 /* --------------------------------
875 * --------------------------------
881 * Make sure we are in a valid context (not a child of
882 * TransactionCommandContext...). Note that it is possible
883 * for this code to be called when we aren't in a transaction
884 * at all; go directly to TopMemoryContext in that case.
887 if (TransactionCommandContext != NULL)
889 MemoryContextSwitchTo(TransactionCommandContext);
892 * We do not want to destroy transaction contexts yet,
893 * but it should be OK to delete any command-local memory.
896 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
900 MemoryContextSwitchTo(TopMemoryContext);
905 /* ----------------------------------------------------------------
906 * CleanupTransaction stuff
907 * ----------------------------------------------------------------
910 /* --------------------------------
912 * --------------------------------
915 AtCleanup_Memory(void)
918 * Now that we're "out" of a transaction, have the
919 * system allocate things in the top memory context instead
920 * of per-transaction contexts.
923 MemoryContextSwitchTo(TopMemoryContext);
926 * Release all transaction-local memory.
929 if (TopTransactionContext != NULL)
930 MemoryContextDelete(TopTransactionContext);
931 TopTransactionContext = NULL;
932 TransactionCommandContext = NULL;
936 /* ----------------------------------------------------------------
938 * ----------------------------------------------------------------
941 /* --------------------------------
944 * --------------------------------
947 StartTransaction(void)
949 TransactionState s = CurrentTransactionState;
952 XactIsoLevel = DefaultXactIsoLevel;
955 * Check the current transaction state. If the transaction system
956 * is switched off, or if we're already in a transaction, do nothing.
957 * We're already in a transaction when the monitor sends a null
958 * command to the backend to flush the comm channel. This is a
959 * hacky fix to a communications problem, and we keep having to
960 * deal with it here. We should fix the comm channel code. mao 080891
963 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
967 * set the current transaction state information
968 * appropriately during start processing
971 s->state = TRANS_START;
973 SetReindexProcessing(false);
976 * generate a new transaction id
979 GetNewTransactionId(&(s->transactionIdData));
981 XactLockTableInsert(s->transactionIdData);
984 * initialize current transaction state fields
987 s->commandId = FirstCommandId;
988 s->scanCommandId = FirstCommandId;
989 s->startTime = GetCurrentAbsoluteTime();
992 * initialize the various transaction subsystems
1000 * Tell the trigger manager to we're starting a transaction
1003 DeferredTriggerBeginXact();
1006 * done with start processing, set current transaction
1007 * state to "in progress"
1010 s->state = TRANS_INPROGRESS;
1016 * Tell me if we are currently in progress
1020 CurrentXactInProgress(void)
1022 return CurrentTransactionState->state == TRANS_INPROGRESS;
1026 /* --------------------------------
1029 * --------------------------------
1032 CommitTransaction(void)
1034 TransactionState s = CurrentTransactionState;
1037 * check the current transaction state
1040 if (s->state == TRANS_DISABLED)
1043 if (s->state != TRANS_INPROGRESS)
1044 elog(NOTICE, "CommitTransaction and not in in-progress state ");
1046 /* Prevent cancel/die interrupt while cleaning up */
1050 * Tell the trigger manager that this transaction is about to be
1051 * committed. He'll invoke all trigger deferred until XACT before
1052 * we really start on committing the transaction.
1055 DeferredTriggerEndXact();
1058 * set the current transaction state information
1059 * appropriately during the abort processing
1062 s->state = TRANS_COMMIT;
1065 * do commit processing
1069 /* handle commit for large objects [ PA, 7/17/98 ] */
1072 /* NOTIFY commit must also come before lower-level cleanup */
1077 RecordTransactionCommit();
1080 * Let others know about no transaction in progress by me. Note that
1081 * this must be done _before_ releasing locks we hold and
1082 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1083 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1084 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1085 * xid 0 as running as well or it will see two tuple versions - one
1086 * deleted by xid 1 and one inserted by xid 0.
1088 if (MyProc != (PROC *) NULL)
1090 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1091 SpinAcquire(SInvalLock);
1092 MyProc->xid = InvalidTransactionId;
1093 MyProc->xmin = InvalidTransactionId;
1094 SpinRelease(SInvalLock);
1097 RelationPurgeLocalRelation(true);
1098 AtEOXact_temp_relations(true);
1099 smgrDoPendingDeletes(true);
1105 AtEOXact_CatCache(true);
1109 SharedBufferChanged = false; /* safest place to do it */
1112 * done with commit processing, set current transaction
1113 * state back to default
1116 s->state = TRANS_DEFAULT;
1118 RESUME_INTERRUPTS();
1121 /* --------------------------------
1124 * --------------------------------
1127 AbortTransaction(void)
1129 TransactionState s = CurrentTransactionState;
1131 /* Prevent cancel/die interrupt while cleaning up */
1135 * Let others to know about no transaction in progress - vadim
1138 if (MyProc != (PROC *) NULL)
1140 MyProc->xid = InvalidTransactionId;
1141 MyProc->xmin = InvalidTransactionId;
1145 * Release any spinlocks or buffer context locks we might be holding
1146 * as quickly as possible. (Real locks, however, must be held till
1147 * we finish aborting.) Releasing spinlocks is critical since we
1148 * might try to grab them again while cleaning up!
1150 ProcReleaseSpins(NULL);
1153 * Also clean up any open wait for lock, since the lock manager
1154 * will choke if we try to wait for another lock before doing this.
1159 * check the current transaction state
1162 if (s->state == TRANS_DISABLED)
1164 RESUME_INTERRUPTS();
1168 if (s->state != TRANS_INPROGRESS)
1169 elog(NOTICE, "AbortTransaction and not in in-progress state");
1172 * set the current transaction state information
1173 * appropriately during the abort processing
1176 s->state = TRANS_ABORT;
1179 * Reset user id which might have been changed transiently
1181 SetUserId(GetSessionUserId());
1184 * do abort processing
1187 DeferredTriggerAbortXact();
1188 lo_commit(false); /* 'false' means it's abort */
1192 RecordTransactionAbort();
1194 RelationPurgeLocalRelation(false);
1195 AtEOXact_temp_relations(false);
1196 smgrDoPendingDeletes(false);
1201 AtEOXact_CatCache(false);
1206 SharedBufferChanged = false; /* safest place to do it */
1209 * State remains TRANS_ABORT until CleanupTransaction().
1212 RESUME_INTERRUPTS();
1215 /* --------------------------------
1216 * CleanupTransaction
1218 * --------------------------------
1221 CleanupTransaction(void)
1223 TransactionState s = CurrentTransactionState;
1225 if (s->state == TRANS_DISABLED)
1229 * State should still be TRANS_ABORT from AbortTransaction().
1232 if (s->state != TRANS_ABORT)
1233 elog(FATAL, "CleanupTransaction and not in abort state");
1236 * do abort cleanup processing
1242 * done with abort processing, set current transaction
1243 * state back to default
1246 s->state = TRANS_DEFAULT;
1249 /* --------------------------------
1250 * StartTransactionCommand
1251 * --------------------------------
1254 StartTransactionCommand(void)
1256 TransactionState s = CurrentTransactionState;
1258 switch (s->blockState)
1261 * if we aren't in a transaction block, we
1262 * just do our usual start transaction.
1265 case TBLOCK_DEFAULT:
1270 * We should never experience this -- if we do it
1271 * means the BEGIN state was not changed in the previous
1272 * CommitTransactionCommand(). If we get it, we print
1273 * a warning and change to the in-progress state.
1277 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1278 s->blockState = TBLOCK_INPROGRESS;
1282 * This is the case when are somewhere in a transaction
1283 * block and about to start a new command. For now we
1284 * do nothing but someday we may do command-local resource
1288 case TBLOCK_INPROGRESS:
1292 * As with BEGIN, we should never experience this
1293 * if we do it means the END state was not changed in the
1294 * previous CommitTransactionCommand(). If we get it, we
1295 * print a warning, commit the transaction, start a new
1296 * transaction and change to the default state.
1300 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1301 s->blockState = TBLOCK_DEFAULT;
1302 CommitTransaction();
1307 * Here we are in the middle of a transaction block but
1308 * one of the commands caused an abort so we do nothing
1309 * but remain in the abort state. Eventually we will get
1310 * to the "END TRANSACTION" which will set things straight.
1317 * This means we somehow aborted and the last call to
1318 * CommitTransactionCommand() didn't clear the state so
1319 * we remain in the ENDABORT state and maybe next time
1320 * we get to CommitTransactionCommand() the state will
1321 * get reset to default.
1324 case TBLOCK_ENDABORT:
1325 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1330 * We must switch to TransactionCommandContext before returning.
1331 * This is already done if we called StartTransaction, otherwise not.
1333 Assert(TransactionCommandContext != NULL);
1334 MemoryContextSwitchTo(TransactionCommandContext);
1337 /* --------------------------------
1338 * CommitTransactionCommand
1339 * --------------------------------
1342 CommitTransactionCommand(void)
1344 TransactionState s = CurrentTransactionState;
1346 switch (s->blockState)
1349 * if we aren't in a transaction block, we
1350 * just do our usual transaction commit
1353 case TBLOCK_DEFAULT:
1354 CommitTransaction();
1358 * This is the case right after we get a "BEGIN TRANSACTION"
1359 * command, but the user hasn't done anything else yet, so
1360 * we change to the "transaction block in progress" state
1365 s->blockState = TBLOCK_INPROGRESS;
1369 * This is the case when we have finished executing a command
1370 * someplace within a transaction block. We increment the
1371 * command counter and return. Someday we may free resources
1372 * local to the command.
1374 * That someday is today, at least for memory allocated in
1375 * TransactionCommandContext.
1379 case TBLOCK_INPROGRESS:
1380 CommandCounterIncrement();
1381 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1385 * This is the case when we just got the "END TRANSACTION"
1386 * statement, so we commit the transaction and go back to
1387 * the default state.
1391 CommitTransaction();
1392 s->blockState = TBLOCK_DEFAULT;
1396 * Here we are in the middle of a transaction block but
1397 * one of the commands caused an abort so we do nothing
1398 * but remain in the abort state. Eventually we will get
1399 * to the "END TRANSACTION" which will set things straight.
1406 * Here we were in an aborted transaction block which
1407 * just processed the "END TRANSACTION" command from the
1408 * user, so clean up and return to the default state.
1411 case TBLOCK_ENDABORT:
1412 CleanupTransaction();
1413 s->blockState = TBLOCK_DEFAULT;
1418 /* --------------------------------
1419 * AbortCurrentTransaction
1420 * --------------------------------
1423 AbortCurrentTransaction(void)
1425 TransactionState s = CurrentTransactionState;
1427 switch (s->blockState)
1430 * if we aren't in a transaction block, we
1431 * just do the basic abort & cleanup transaction.
1434 case TBLOCK_DEFAULT:
1436 CleanupTransaction();
1440 * If we are in the TBLOCK_BEGIN it means something
1441 * screwed up right after reading "BEGIN TRANSACTION"
1442 * so we enter the abort state. Eventually an "END
1443 * TRANSACTION" will fix things.
1447 s->blockState = TBLOCK_ABORT;
1449 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1453 * This is the case when are somewhere in a transaction
1454 * block which aborted so we abort the transaction and
1455 * set the ABORT state. Eventually an "END TRANSACTION"
1456 * will fix things and restore us to a normal state.
1459 case TBLOCK_INPROGRESS:
1460 s->blockState = TBLOCK_ABORT;
1462 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1466 * Here, the system was fouled up just after the
1467 * user wanted to end the transaction block so we
1468 * abort the transaction and put us back into the
1473 s->blockState = TBLOCK_DEFAULT;
1475 CleanupTransaction();
1479 * Here, we are already in an aborted transaction
1480 * state and are waiting for an "END TRANSACTION" to
1481 * come along and lo and behold, we abort again!
1482 * So we just remain in the abort state.
1489 * Here we were in an aborted transaction block which
1490 * just processed the "END TRANSACTION" command but somehow
1491 * aborted again.. since we must have done the abort
1492 * processing, we clean up and return to the default state.
1495 case TBLOCK_ENDABORT:
1496 CleanupTransaction();
1497 s->blockState = TBLOCK_DEFAULT;
1502 /* ----------------------------------------------------------------
1503 * transaction block support
1504 * ----------------------------------------------------------------
1506 /* --------------------------------
1507 * BeginTransactionBlock
1508 * --------------------------------
1511 BeginTransactionBlock(void)
1513 TransactionState s = CurrentTransactionState;
1516 * check the current transaction state
1519 if (s->state == TRANS_DISABLED)
1522 if (s->blockState != TBLOCK_DEFAULT)
1523 elog(NOTICE, "BEGIN: already a transaction in progress");
1526 * set the current transaction block state information
1527 * appropriately during begin processing
1530 s->blockState = TBLOCK_BEGIN;
1533 * do begin processing
1538 * done with begin processing, set block state to inprogress
1541 s->blockState = TBLOCK_INPROGRESS;
1544 /* --------------------------------
1545 * EndTransactionBlock
1546 * --------------------------------
1549 EndTransactionBlock(void)
1551 TransactionState s = CurrentTransactionState;
1554 * check the current transaction state
1557 if (s->state == TRANS_DISABLED)
1560 if (s->blockState == TBLOCK_INPROGRESS)
1563 * here we are in a transaction block which should commit
1564 * when we get to the upcoming CommitTransactionCommand()
1565 * so we set the state to "END". CommitTransactionCommand()
1566 * will recognize this and commit the transaction and return
1567 * us to the default state
1570 s->blockState = TBLOCK_END;
1574 if (s->blockState == TBLOCK_ABORT)
1577 * here, we are in a transaction block which aborted
1578 * and since the AbortTransaction() was already done,
1579 * we do whatever is needed and change to the special
1580 * "END ABORT" state. The upcoming CommitTransactionCommand()
1581 * will recognise this and then put us back in the default
1585 s->blockState = TBLOCK_ENDABORT;
1590 * here, the user issued COMMIT when not inside a transaction.
1591 * Issue a notice and go to abort state. The upcoming call to
1592 * CommitTransactionCommand() will then put us back into the
1596 elog(NOTICE, "COMMIT: no transaction in progress");
1598 s->blockState = TBLOCK_ENDABORT;
1601 /* --------------------------------
1602 * AbortTransactionBlock
1603 * --------------------------------
1607 AbortTransactionBlock(void)
1609 TransactionState s = CurrentTransactionState;
1612 * check the current transaction state
1615 if (s->state == TRANS_DISABLED)
1618 if (s->blockState == TBLOCK_INPROGRESS)
1621 * here we were inside a transaction block something
1622 * screwed up inside the system so we enter the abort state,
1623 * do the abort processing and then return.
1624 * We remain in the abort state until we see an
1625 * END TRANSACTION command.
1628 s->blockState = TBLOCK_ABORT;
1634 * here, the user issued ABORT when not inside a transaction.
1635 * Issue a notice and go to abort state. The upcoming call to
1636 * CommitTransactionCommand() will then put us back into the
1640 elog(NOTICE, "ROLLBACK: no transaction in progress");
1642 s->blockState = TBLOCK_ENDABORT;
1647 /* --------------------------------
1648 * UserAbortTransactionBlock
1649 * --------------------------------
1652 UserAbortTransactionBlock(void)
1654 TransactionState s = CurrentTransactionState;
1657 * check the current transaction state
1660 if (s->state == TRANS_DISABLED)
1664 * if the transaction has already been automatically aborted with an
1665 * error, and the user subsequently types 'abort', allow it. (the
1666 * behavior is the same as if they had typed 'end'.)
1668 if (s->blockState == TBLOCK_ABORT)
1670 s->blockState = TBLOCK_ENDABORT;
1674 if (s->blockState == TBLOCK_INPROGRESS)
1677 * here we were inside a transaction block and we
1678 * got an abort command from the user, so we move to
1679 * the abort state, do the abort processing and
1680 * then change to the ENDABORT state so we will end up
1681 * in the default state after the upcoming
1682 * CommitTransactionCommand().
1685 s->blockState = TBLOCK_ABORT;
1687 s->blockState = TBLOCK_ENDABORT;
1692 * here, the user issued ABORT when not inside a transaction.
1693 * Issue a notice and go to abort state. The upcoming call to
1694 * CommitTransactionCommand() will then put us back into the
1698 elog(NOTICE, "ROLLBACK: no transaction in progress");
1700 s->blockState = TBLOCK_ENDABORT;
1703 /* --------------------------------
1704 * AbortOutOfAnyTransaction
1706 * This routine is provided for error recovery purposes. It aborts any
1707 * active transaction or transaction block, leaving the system in a known
1709 * --------------------------------
1712 AbortOutOfAnyTransaction(void)
1714 TransactionState s = CurrentTransactionState;
1717 * Get out of any low-level transaction
1722 case TRANS_INPROGRESS:
1724 /* In a transaction, so clean up */
1726 CleanupTransaction();
1729 /* AbortTransaction already done, still need Cleanup */
1730 CleanupTransaction();
1733 case TRANS_DISABLED:
1734 /* Not in a transaction, do nothing */
1739 * Now reset the high-level state
1741 s->blockState = TBLOCK_DEFAULT;
1745 IsTransactionBlock(void)
1747 TransactionState s = CurrentTransactionState;
1749 if (s->blockState == TBLOCK_INPROGRESS
1750 || s->blockState == TBLOCK_ABORT
1751 || s->blockState == TBLOCK_ENDABORT)
1758 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1760 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1762 if (info == XLOG_XACT_COMMIT)
1764 TransactionIdCommit(record->xl_xid);
1765 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1767 else if (info == XLOG_XACT_ABORT)
1769 TransactionIdAbort(record->xl_xid);
1772 elog(STOP, "xact_redo: unknown op code %u", info);
1776 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1778 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1780 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1781 elog(STOP, "xact_undo: can't undo committed xaction");
1782 else if (info != XLOG_XACT_ABORT)
1783 elog(STOP, "xact_redo: unknown op code %u", info);
1787 xact_desc(char *buf, uint8 xl_info, char* rec)
1789 uint8 info = xl_info & ~XLR_INFO_MASK;
1791 if (info == XLOG_XACT_COMMIT)
1793 xl_xact_commit *xlrec = (xl_xact_commit*) rec;
1794 struct tm *tm = localtime(&xlrec->xtime);
1796 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1797 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1798 tm->tm_hour, tm->tm_min, tm->tm_sec);
1800 else if (info == XLOG_XACT_ABORT)
1802 xl_xact_abort *xlrec = (xl_xact_abort*) rec;
1803 struct tm *tm = localtime(&xlrec->xtime);
1805 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1806 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1807 tm->tm_hour, tm->tm_min, tm->tm_sec);
1810 strcat(buf, "UNKNOWN");
1814 XactPushRollback(void (*func) (void *), void* data)
1817 if (_RollbackFunc != NULL)
1818 elog(STOP, "XactPushRollback: already installed");
1821 _RollbackFunc = func;
1822 _RollbackData = data;
1826 XactPopRollback(void)
1828 _RollbackFunc = NULL;