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.122 2002/05/17 20:53:33 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"
158 #include <sys/time.h>
160 #include "access/gistscan.h"
161 #include "access/hash.h"
162 #include "access/nbtree.h"
163 #include "access/rtree.h"
164 #include "access/xact.h"
165 #include "catalog/heap.h"
166 #include "catalog/index.h"
167 #include "catalog/namespace.h"
168 #include "commands/async.h"
169 #include "commands/sequence.h"
170 #include "commands/trigger.h"
171 #include "executor/spi.h"
172 #include "libpq/be-fsstubs.h"
173 #include "miscadmin.h"
174 #include "storage/proc.h"
175 #include "storage/sinval.h"
176 #include "storage/smgr.h"
177 #include "utils/guc.h"
178 #include "utils/inval.h"
179 #include "utils/memutils.h"
180 #include "utils/portal.h"
181 #include "utils/catcache.h"
182 #include "utils/relcache.h"
186 extern bool SharedBufferChanged;
188 static void AbortTransaction(void);
189 static void AtAbort_Cache(void);
190 static void AtAbort_Locks(void);
191 static void AtAbort_Memory(void);
192 static void AtCleanup_Memory(void);
193 static void AtCommit_Cache(void);
194 static void AtCommit_LocalCache(void);
195 static void AtCommit_Locks(void);
196 static void AtCommit_Memory(void);
197 static void AtStart_Cache(void);
198 static void AtStart_Locks(void);
199 static void AtStart_Memory(void);
200 static void CleanupTransaction(void);
201 static void CommitTransaction(void);
202 static void RecordTransactionAbort(void);
203 static void StartTransaction(void);
206 * global variables holding the current transaction state.
209 static TransactionStateData CurrentTransactionStateData = {
210 0, /* transaction id */
211 FirstCommandId, /* command id */
212 0, /* scan command id */
213 0x0, /* start time */
214 TRANS_DEFAULT, /* transaction state */
215 TBLOCK_DEFAULT /* transaction block state */
218 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
221 * User-tweakable parameters
223 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
226 int CommitDelay = 0; /* precommit delay in microseconds */
227 int CommitSiblings = 5; /* number of concurrent xacts needed to
230 static void (*_RollbackFunc) (void *) = NULL;
231 static void *_RollbackData = NULL;
234 * catalog creation transaction bootstrapping flag.
235 * This should be eliminated and added to the transaction
236 * state stuff. -cim 3/19/90
239 bool AMI_OVERRIDE = false;
241 /* ----------------------------------------------------------------
242 * transaction state accessors
243 * ----------------------------------------------------------------
248 /* --------------------------------
249 * TransactionFlushEnabled()
250 * SetTransactionFlushEnabled()
252 * These are used to test and set the "TransactionFlushState"
253 * varable. If this variable is true (the default), then
254 * the system will flush all dirty buffers to disk at the end
255 * of each transaction. If false then we are assuming the
256 * buffer pool resides in stable main memory, in which case we
257 * only do writes as necessary.
258 * --------------------------------
260 static int TransactionFlushState = 1;
263 TransactionFlushEnabled(void)
265 return TransactionFlushState;
269 SetTransactionFlushEnabled(bool state)
271 TransactionFlushState = (state == true);
277 /* --------------------------------
280 * This returns true if we are currently running a query
281 * within an executing transaction.
282 * --------------------------------
285 IsTransactionState(void)
287 TransactionState s = CurrentTransactionState;
295 case TRANS_INPROGRESS:
304 * Shouldn't get here, but lint is not happy with this...
309 /* --------------------------------
310 * IsAbortedTransactionBlockState
312 * This returns true if we are currently running a query
313 * within an aborted transaction block.
314 * --------------------------------
317 IsAbortedTransactionBlockState(void)
319 TransactionState s = CurrentTransactionState;
321 if (s->blockState == TBLOCK_ABORT)
328 /* --------------------------------
329 * GetCurrentTransactionId
330 * --------------------------------
333 GetCurrentTransactionId(void)
335 TransactionState s = CurrentTransactionState;
337 return s->transactionIdData;
341 /* --------------------------------
342 * GetCurrentCommandId
343 * --------------------------------
346 GetCurrentCommandId(void)
348 TransactionState s = CurrentTransactionState;
354 GetScanCommandId(void)
356 TransactionState s = CurrentTransactionState;
358 return s->scanCommandId;
362 /* --------------------------------
363 * GetCurrentTransactionStartTime
364 * --------------------------------
367 GetCurrentTransactionStartTime(void)
369 TransactionState s = CurrentTransactionState;
375 /* --------------------------------
376 * GetCurrentTransactionStartTimeUsec
377 * --------------------------------
380 GetCurrentTransactionStartTimeUsec(int *msec)
382 TransactionState s = CurrentTransactionState;
384 *msec = s->startTimeUsec;
390 /* --------------------------------
391 * TransactionIdIsCurrentTransactionId
392 * --------------------------------
395 TransactionIdIsCurrentTransactionId(TransactionId xid)
397 TransactionState s = CurrentTransactionState;
402 return TransactionIdEquals(xid, s->transactionIdData);
406 /* --------------------------------
407 * CommandIdIsCurrentCommandId
408 * --------------------------------
411 CommandIdIsCurrentCommandId(CommandId cid)
413 TransactionState s = CurrentTransactionState;
418 return (cid == s->commandId) ? true : false;
422 CommandIdGEScanCommandId(CommandId cid)
424 TransactionState s = CurrentTransactionState;
429 return (cid >= s->scanCommandId) ? true : false;
433 /* --------------------------------
434 * CommandCounterIncrement
435 * --------------------------------
438 CommandCounterIncrement(void)
440 CurrentTransactionStateData.commandId += 1;
441 if (CurrentTransactionStateData.commandId == FirstCommandId)
442 elog(ERROR, "You may only have 2^32-1 commands per transaction");
444 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
447 * make cache changes visible to me. AtCommit_LocalCache() instead of
448 * AtCommit_Cache() is called here.
450 AtCommit_LocalCache();
455 SetScanCommandId(CommandId savedId)
457 CurrentTransactionStateData.scanCommandId = savedId;
460 /* ----------------------------------------------------------------
461 * StartTransaction stuff
462 * ----------------------------------------------------------------
465 /* --------------------------------
467 * --------------------------------
472 AcceptInvalidationMessages();
475 /* --------------------------------
477 * --------------------------------
483 * at present, it is unknown to me what belongs here -cim 3/18/90
485 * There isn't anything to do at the start of a xact for locks. -mer
490 /* --------------------------------
492 * --------------------------------
498 * We shouldn't have any transaction contexts already.
500 Assert(TopTransactionContext == NULL);
501 Assert(TransactionCommandContext == NULL);
504 * Create a toplevel context for the transaction.
506 TopTransactionContext =
507 AllocSetContextCreate(TopMemoryContext,
508 "TopTransactionContext",
509 ALLOCSET_DEFAULT_MINSIZE,
510 ALLOCSET_DEFAULT_INITSIZE,
511 ALLOCSET_DEFAULT_MAXSIZE);
514 * Create a statement-level context and make it active.
516 TransactionCommandContext =
517 AllocSetContextCreate(TopTransactionContext,
518 "TransactionCommandContext",
519 ALLOCSET_DEFAULT_MINSIZE,
520 ALLOCSET_DEFAULT_INITSIZE,
521 ALLOCSET_DEFAULT_MAXSIZE);
522 MemoryContextSwitchTo(TransactionCommandContext);
526 /* ----------------------------------------------------------------
527 * CommitTransaction stuff
528 * ----------------------------------------------------------------
531 /* --------------------------------
532 * RecordTransactionCommit
534 * Note: the two calls to BufferManagerFlush() exist to ensure
535 * that data pages are written before log pages. These
536 * explicit calls should be replaced by a more efficient
537 * ordered page write scheme in the buffer manager
539 * --------------------------------
542 RecordTransactionCommit(void)
547 leak = BufferPoolCheckLeak();
549 xid = GetCurrentTransactionId();
552 * We only need to log the commit in xlog and clog if the transaction made
553 * any transaction-controlled XLOG entries. (Otherwise, its XID appears
554 * nowhere in permanent storage, so no one will ever care if it
555 * committed.) However, we must flush XLOG to disk if we made any XLOG
556 * entries, whether in or out of transaction control. For example, if we
557 * reported a nextval() result to the client, this ensures that any XLOG
558 * record generated by nextval will hit the disk before we report the
559 * transaction committed.
561 if (MyXactMadeXLogEntry)
567 START_CRIT_SECTION();
569 if (MyLastRecPtr.xrecoff != 0)
571 /* Need to emit a commit record */
573 xl_xact_commit xlrec;
575 xlrec.xtime = time(NULL);
576 rdata.buffer = InvalidBuffer;
577 rdata.data = (char *) (&xlrec);
578 rdata.len = SizeOfXactCommit;
582 * XXX SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
584 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
588 /* Just flush through last record written by me */
589 recptr = ProcLastRecEnd;
593 * Sleep before flush! So we can flush more than one commit
594 * records per single fsync. (The idea is some other backend may
595 * do the XLogFlush while we're sleeping. This needs work still,
596 * because on most Unixen, the minimum select() delay is 10msec or
597 * more, which is way too long.)
599 * We do not sleep if enableFsync is not turned on, nor if there are
600 * fewer than CommitSiblings other backends with active
603 if (CommitDelay > 0 && enableFsync &&
604 CountActiveBackends() >= CommitSiblings)
606 struct timeval delay;
609 delay.tv_usec = CommitDelay;
610 (void) select(0, NULL, NULL, NULL, &delay);
615 /* Mark the transaction committed in clog, if needed */
616 if (MyLastRecPtr.xrecoff != 0)
617 TransactionIdCommit(xid);
622 /* Break the chain of back-links in the XLOG records I output */
623 MyLastRecPtr.xrecoff = 0;
624 MyXactMadeXLogEntry = false;
626 /* Show myself as out of the transaction in PROC array */
627 MyProc->logRec.xrecoff = 0;
630 ResetBufferPool(true);
634 /* --------------------------------
636 * --------------------------------
642 * Make catalog changes visible to all backends.
644 AtEOXactInvalidationMessages(true);
647 /* --------------------------------
648 * AtCommit_LocalCache
649 * --------------------------------
652 AtCommit_LocalCache(void)
655 * Make catalog changes visible to me for the next command.
657 CommandEndInvalidationMessages(true);
660 /* --------------------------------
662 * --------------------------------
668 * XXX What if ProcReleaseLocks fails? (race condition?)
670 * Then you're up a creek! -mer 5/24/92
672 ProcReleaseLocks(true);
675 /* --------------------------------
677 * --------------------------------
680 AtCommit_Memory(void)
683 * Now that we're "out" of a transaction, have the system allocate
684 * things in the top memory context instead of per-transaction
687 MemoryContextSwitchTo(TopMemoryContext);
690 * Release all transaction-local memory.
692 Assert(TopTransactionContext != NULL);
693 MemoryContextDelete(TopTransactionContext);
694 TopTransactionContext = NULL;
695 TransactionCommandContext = NULL;
698 /* ----------------------------------------------------------------
699 * AbortTransaction stuff
700 * ----------------------------------------------------------------
703 /* --------------------------------
704 * RecordTransactionAbort
705 * --------------------------------
708 RecordTransactionAbort(void)
710 TransactionId xid = GetCurrentTransactionId();
713 * We only need to log the abort in xlog and clog if the transaction made
714 * any transaction-controlled XLOG entries. (Otherwise, its XID appears
715 * nowhere in permanent storage, so no one will ever care if it
716 * committed.) We do not flush XLOG to disk in any case, since the
717 * default assumption after a crash would be that we aborted, anyway.
719 * Extra check here is to catch case that we aborted partway through
720 * RecordTransactionCommit ...
722 if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
728 xlrec.xtime = time(NULL);
729 rdata.buffer = InvalidBuffer;
730 rdata.data = (char *) (&xlrec);
731 rdata.len = SizeOfXactAbort;
734 START_CRIT_SECTION();
737 * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
739 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
741 /* Mark the transaction aborted in clog */
742 TransactionIdAbort(xid);
747 /* Break the chain of back-links in the XLOG records I output */
748 MyLastRecPtr.xrecoff = 0;
749 MyXactMadeXLogEntry = false;
751 /* Show myself as out of the transaction in PROC array */
752 MyProc->logRec.xrecoff = 0;
755 * Tell bufmgr and smgr to release resources.
757 ResetBufferPool(false); /* false -> is abort */
760 /* --------------------------------
762 * --------------------------------
767 RelationCacheAbort();
768 AtEOXactInvalidationMessages(false);
771 /* --------------------------------
773 * --------------------------------
779 * XXX What if ProcReleaseLocks() fails? (race condition?)
781 * Then you're up a creek without a paddle! -mer
783 ProcReleaseLocks(false);
787 /* --------------------------------
789 * --------------------------------
795 * Make sure we are in a valid context (not a child of
796 * TransactionCommandContext...). Note that it is possible for this
797 * code to be called when we aren't in a transaction at all; go
798 * directly to TopMemoryContext in that case.
800 if (TransactionCommandContext != NULL)
802 MemoryContextSwitchTo(TransactionCommandContext);
805 * We do not want to destroy transaction contexts yet, but it
806 * should be OK to delete any command-local memory.
808 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
811 MemoryContextSwitchTo(TopMemoryContext);
815 /* ----------------------------------------------------------------
816 * CleanupTransaction stuff
817 * ----------------------------------------------------------------
820 /* --------------------------------
822 * --------------------------------
825 AtCleanup_Memory(void)
828 * Now that we're "out" of a transaction, have the system allocate
829 * things in the top memory context instead of per-transaction
832 MemoryContextSwitchTo(TopMemoryContext);
835 * Release all transaction-local memory.
837 if (TopTransactionContext != NULL)
838 MemoryContextDelete(TopTransactionContext);
839 TopTransactionContext = NULL;
840 TransactionCommandContext = NULL;
844 /* ----------------------------------------------------------------
846 * ----------------------------------------------------------------
849 /* --------------------------------
852 * --------------------------------
855 StartTransaction(void)
857 TransactionState s = CurrentTransactionState;
860 XactIsoLevel = DefaultXactIsoLevel;
863 * Check the current transaction state. If the transaction system is
864 * switched off, or if we're already in a transaction, do nothing.
865 * We're already in a transaction when the monitor sends a null
866 * command to the backend to flush the comm channel. This is a hacky
867 * fix to a communications problem, and we keep having to deal with it
868 * here. We should fix the comm channel code. mao 080891
870 if (s->state == TRANS_INPROGRESS)
874 * set the current transaction state information appropriately during
877 s->state = TRANS_START;
879 SetReindexProcessing(false);
882 * generate a new transaction id
884 s->transactionIdData = GetNewTransactionId();
886 XactLockTableInsert(s->transactionIdData);
889 * initialize current transaction state fields
891 s->commandId = FirstCommandId;
892 s->scanCommandId = FirstCommandId;
894 s->startTime = GetCurrentAbsoluteTime();
896 s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
899 * initialize the various transaction subsystems
906 * Tell the trigger manager to we're starting a transaction
908 DeferredTriggerBeginXact();
911 * done with start processing, set current transaction state to "in
914 s->state = TRANS_INPROGRESS;
920 * Tell me if we are currently in progress
924 CurrentXactInProgress(void)
926 return CurrentTransactionState->state == TRANS_INPROGRESS;
930 /* --------------------------------
933 * --------------------------------
936 CommitTransaction(void)
938 TransactionState s = CurrentTransactionState;
941 * check the current transaction state
943 if (s->state != TRANS_INPROGRESS)
944 elog(WARNING, "CommitTransaction and not in in-progress state");
947 * Tell the trigger manager that this transaction is about to be
948 * committed. He'll invoke all trigger deferred until XACT before we
949 * really start on committing the transaction.
951 DeferredTriggerEndXact();
953 /* Prevent cancel/die interrupt while cleaning up */
957 * set the current transaction state information appropriately during
958 * the abort processing
960 s->state = TRANS_COMMIT;
963 * do commit processing
966 /* handle commit for large objects [ PA, 7/17/98 ] */
969 /* NOTIFY commit must also come before lower-level cleanup */
975 /* Here is where we really truly commit. */
976 RecordTransactionCommit();
979 * Let others know about no transaction in progress by me. Note that
980 * this must be done _before_ releasing locks we hold and _after_
981 * RecordTransactionCommit.
983 * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by
984 * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if
985 * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0
986 * as running as well or it will see two tuple versions - one deleted
987 * by xid 1 and one inserted by xid 0. See notes in GetSnapshotData.
989 if (MyProc != (PROC *) NULL)
991 /* Lock SInvalLock because that's what GetSnapshotData uses. */
992 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
993 MyProc->xid = InvalidTransactionId;
994 MyProc->xmin = InvalidTransactionId;
995 LWLockRelease(SInvalLock);
999 * This is all post-commit cleanup. Note that if an error is raised
1000 * here, it's too late to abort the transaction. This should be just
1001 * noncritical resource releasing.
1004 RelationPurgeLocalRelation(true);
1005 smgrDoPendingDeletes(true);
1013 AtEOXact_Namespace(true);
1016 AtEOXact_CatCache(true);
1020 SharedBufferChanged = false; /* safest place to do it */
1022 /* Count transaction commit in statistics collector */
1023 pgstat_count_xact_commit();
1026 * done with commit processing, set current transaction state back to
1029 s->state = TRANS_DEFAULT;
1031 RESUME_INTERRUPTS();
1034 /* --------------------------------
1037 * --------------------------------
1040 AbortTransaction(void)
1042 TransactionState s = CurrentTransactionState;
1044 /* Prevent cancel/die interrupt while cleaning up */
1048 * Release any LW locks we might be holding as quickly as possible.
1049 * (Regular locks, however, must be held till we finish aborting.)
1050 * Releasing LW locks is critical since we might try to grab them
1051 * again while cleaning up!
1055 /* Clean up buffer I/O and buffer context locks, too */
1060 * Also clean up any open wait for lock, since the lock manager will
1061 * choke if we try to wait for another lock before doing this.
1066 * check the current transaction state
1068 if (s->state != TRANS_INPROGRESS)
1069 elog(WARNING, "AbortTransaction and not in in-progress state");
1072 * set the current transaction state information appropriately during
1073 * the abort processing
1075 s->state = TRANS_ABORT;
1078 * Reset user id which might have been changed transiently
1080 SetUserId(GetSessionUserId());
1083 * do abort processing
1085 DeferredTriggerAbortXact();
1086 lo_commit(false); /* 'false' means it's abort */
1091 /* Advertise the fact that we aborted in pg_clog. */
1092 RecordTransactionAbort();
1095 * Let others know about no transaction in progress by me. Note that
1096 * this must be done _before_ releasing locks we hold and _after_
1097 * RecordTransactionAbort.
1099 if (MyProc != (PROC *) NULL)
1101 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1102 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
1103 MyProc->xid = InvalidTransactionId;
1104 MyProc->xmin = InvalidTransactionId;
1105 LWLockRelease(SInvalLock);
1108 RelationPurgeLocalRelation(false);
1109 smgrDoPendingDeletes(false);
1111 AtEOXact_GUC(false);
1117 AtEOXact_Namespace(false);
1119 AtEOXact_CatCache(false);
1124 SharedBufferChanged = false; /* safest place to do it */
1126 /* Count transaction abort in statistics collector */
1127 pgstat_count_xact_rollback();
1130 * State remains TRANS_ABORT until CleanupTransaction().
1132 RESUME_INTERRUPTS();
1135 /* --------------------------------
1136 * CleanupTransaction
1138 * --------------------------------
1141 CleanupTransaction(void)
1143 TransactionState s = CurrentTransactionState;
1146 * State should still be TRANS_ABORT from AbortTransaction().
1148 if (s->state != TRANS_ABORT)
1149 elog(FATAL, "CleanupTransaction and not in abort state");
1152 * do abort cleanup processing
1157 * done with abort processing, set current transaction state back to
1160 s->state = TRANS_DEFAULT;
1163 /* --------------------------------
1164 * StartTransactionCommand
1165 * --------------------------------
1168 StartTransactionCommand(void)
1170 TransactionState s = CurrentTransactionState;
1172 switch (s->blockState)
1175 * if we aren't in a transaction block, we just do our usual
1176 * start transaction.
1178 case TBLOCK_DEFAULT:
1183 * We should never experience this -- if we do it means the
1184 * BEGIN state was not changed in the previous
1185 * CommitTransactionCommand(). If we get it, we print a
1186 * warning and change to the in-progress state.
1189 elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1190 s->blockState = TBLOCK_INPROGRESS;
1194 * This is the case when are somewhere in a transaction block
1195 * and about to start a new command. For now we do nothing
1196 * but someday we may do command-local resource
1199 case TBLOCK_INPROGRESS:
1203 * As with BEGIN, we should never experience this if we do it
1204 * means the END state was not changed in the previous
1205 * CommitTransactionCommand(). If we get it, we print a
1206 * warning, commit the transaction, start a new transaction
1207 * and change to the default state.
1210 elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_END");
1211 s->blockState = TBLOCK_DEFAULT;
1212 CommitTransaction();
1217 * Here we are in the middle of a transaction block but one of
1218 * the commands caused an abort so we do nothing but remain in
1219 * the abort state. Eventually we will get to the "END
1220 * TRANSACTION" which will set things straight.
1226 * This means we somehow aborted and the last call to
1227 * CommitTransactionCommand() didn't clear the state so we
1228 * remain in the ENDABORT state and maybe next time we get to
1229 * CommitTransactionCommand() the state will get reset to
1232 case TBLOCK_ENDABORT:
1233 elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1238 * We must switch to TransactionCommandContext before returning. This
1239 * is already done if we called StartTransaction, otherwise not.
1241 Assert(TransactionCommandContext != NULL);
1242 MemoryContextSwitchTo(TransactionCommandContext);
1245 /* --------------------------------
1246 * CommitTransactionCommand
1247 * --------------------------------
1250 CommitTransactionCommand(void)
1252 TransactionState s = CurrentTransactionState;
1254 switch (s->blockState)
1257 * if we aren't in a transaction block, we just do our usual
1258 * transaction commit
1260 case TBLOCK_DEFAULT:
1261 CommitTransaction();
1265 * This is the case right after we get a "BEGIN TRANSACTION"
1266 * command, but the user hasn't done anything else yet, so we
1267 * change to the "transaction block in progress" state and
1271 s->blockState = TBLOCK_INPROGRESS;
1275 * This is the case when we have finished executing a command
1276 * someplace within a transaction block. We increment the
1277 * command counter and return. Someday we may free resources
1278 * local to the command.
1280 * That someday is today, at least for memory allocated in
1281 * TransactionCommandContext. - vadim 03/25/97
1283 case TBLOCK_INPROGRESS:
1284 CommandCounterIncrement();
1285 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1289 * This is the case when we just got the "END TRANSACTION"
1290 * statement, so we commit the transaction and go back to the
1294 CommitTransaction();
1295 s->blockState = TBLOCK_DEFAULT;
1299 * Here we are in the middle of a transaction block but one of
1300 * the commands caused an abort so we do nothing but remain in
1301 * the abort state. Eventually we will get to the "END
1302 * TRANSACTION" which will set things straight.
1308 * Here we were in an aborted transaction block which just
1309 * processed the "END TRANSACTION" command from the user, so
1310 * clean up and return to the default state.
1312 case TBLOCK_ENDABORT:
1313 CleanupTransaction();
1314 s->blockState = TBLOCK_DEFAULT;
1319 /* --------------------------------
1320 * AbortCurrentTransaction
1321 * --------------------------------
1324 AbortCurrentTransaction(void)
1326 TransactionState s = CurrentTransactionState;
1328 switch (s->blockState)
1331 * if we aren't in a transaction block, we just do the basic
1332 * abort & cleanup transaction.
1334 case TBLOCK_DEFAULT:
1336 CleanupTransaction();
1340 * If we are in the TBLOCK_BEGIN it means something screwed up
1341 * right after reading "BEGIN TRANSACTION" so we enter the
1342 * abort state. Eventually an "END TRANSACTION" will fix
1346 s->blockState = TBLOCK_ABORT;
1348 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1352 * This is the case when are somewhere in a transaction block
1353 * which aborted so we abort the transaction and set the ABORT
1354 * state. Eventually an "END TRANSACTION" will fix things and
1355 * restore us to a normal state.
1357 case TBLOCK_INPROGRESS:
1358 s->blockState = TBLOCK_ABORT;
1360 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1364 * Here, the system was fouled up just after the user wanted
1365 * to end the transaction block so we abort the transaction
1366 * and put us back into the default state.
1369 s->blockState = TBLOCK_DEFAULT;
1371 CleanupTransaction();
1375 * Here, we are already in an aborted transaction state and
1376 * are waiting for an "END TRANSACTION" to come along and lo
1377 * and behold, we abort again! So we just remain in the abort
1384 * Here we were in an aborted transaction block which just
1385 * processed the "END TRANSACTION" command but somehow aborted
1386 * again.. since we must have done the abort processing, we
1387 * clean up and return to the default state.
1389 case TBLOCK_ENDABORT:
1390 CleanupTransaction();
1391 s->blockState = TBLOCK_DEFAULT;
1396 /* ----------------------------------------------------------------
1397 * transaction block support
1398 * ----------------------------------------------------------------
1400 /* --------------------------------
1401 * BeginTransactionBlock
1402 * --------------------------------
1405 BeginTransactionBlock(void)
1407 TransactionState s = CurrentTransactionState;
1410 * check the current transaction state
1412 if (s->blockState != TBLOCK_DEFAULT)
1413 elog(WARNING, "BEGIN: already a transaction in progress");
1416 * set the current transaction block state information appropriately
1417 * during begin processing
1419 s->blockState = TBLOCK_BEGIN;
1422 * do begin processing
1426 * done with begin processing, set block state to inprogress
1428 s->blockState = TBLOCK_INPROGRESS;
1431 /* --------------------------------
1432 * EndTransactionBlock
1433 * --------------------------------
1436 EndTransactionBlock(void)
1438 TransactionState s = CurrentTransactionState;
1441 * check the current transaction state
1443 if (s->blockState == TBLOCK_INPROGRESS)
1446 * here we are in a transaction block which should commit when we
1447 * get to the upcoming CommitTransactionCommand() so we set the
1448 * state to "END". CommitTransactionCommand() will recognize this
1449 * and commit the transaction and return us to the default state
1451 s->blockState = TBLOCK_END;
1455 if (s->blockState == TBLOCK_ABORT)
1458 * here, we are in a transaction block which aborted and since the
1459 * AbortTransaction() was already done, we do whatever is needed
1460 * and change to the special "END ABORT" state. The upcoming
1461 * CommitTransactionCommand() will recognise this and then put us
1462 * back in the default state.
1464 s->blockState = TBLOCK_ENDABORT;
1469 * here, the user issued COMMIT when not inside a transaction. Issue a
1470 * WARNING and go to abort state. The upcoming call to
1471 * CommitTransactionCommand() will then put us back into the default
1474 elog(WARNING, "COMMIT: no transaction in progress");
1476 s->blockState = TBLOCK_ENDABORT;
1479 /* --------------------------------
1480 * AbortTransactionBlock
1481 * --------------------------------
1485 AbortTransactionBlock(void)
1487 TransactionState s = CurrentTransactionState;
1490 * check the current transaction state
1492 if (s->blockState == TBLOCK_INPROGRESS)
1495 * here we were inside a transaction block something screwed up
1496 * inside the system so we enter the abort state, do the abort
1497 * processing and then return. We remain in the abort state until
1498 * we see an END TRANSACTION command.
1500 s->blockState = TBLOCK_ABORT;
1506 * here, the user issued ABORT when not inside a transaction. Issue a
1507 * WARNING and go to abort state. The upcoming call to
1508 * CommitTransactionCommand() will then put us back into the default
1511 elog(WARNING, "ROLLBACK: no transaction in progress");
1513 s->blockState = TBLOCK_ENDABORT;
1517 /* --------------------------------
1518 * UserAbortTransactionBlock
1519 * --------------------------------
1522 UserAbortTransactionBlock(void)
1524 TransactionState s = CurrentTransactionState;
1527 * if the transaction has already been automatically aborted with an
1528 * error, and the user subsequently types 'abort', allow it. (the
1529 * behavior is the same as if they had typed 'end'.)
1531 if (s->blockState == TBLOCK_ABORT)
1533 s->blockState = TBLOCK_ENDABORT;
1537 if (s->blockState == TBLOCK_INPROGRESS)
1540 * here we were inside a transaction block and we got an abort
1541 * command from the user, so we move to the abort state, do the
1542 * abort processing and then change to the ENDABORT state so we
1543 * will end up in the default state after the upcoming
1544 * CommitTransactionCommand().
1546 s->blockState = TBLOCK_ABORT;
1548 s->blockState = TBLOCK_ENDABORT;
1553 * here, the user issued ABORT when not inside a transaction. Issue a
1554 * WARNING and go to abort state. The upcoming call to
1555 * CommitTransactionCommand() will then put us back into the default
1558 elog(WARNING, "ROLLBACK: no transaction in progress");
1560 s->blockState = TBLOCK_ENDABORT;
1563 /* --------------------------------
1564 * AbortOutOfAnyTransaction
1566 * This routine is provided for error recovery purposes. It aborts any
1567 * active transaction or transaction block, leaving the system in a known
1569 * --------------------------------
1572 AbortOutOfAnyTransaction(void)
1574 TransactionState s = CurrentTransactionState;
1577 * Get out of any low-level transaction
1582 case TRANS_INPROGRESS:
1584 /* In a transaction, so clean up */
1586 CleanupTransaction();
1589 /* AbortTransaction already done, still need Cleanup */
1590 CleanupTransaction();
1593 /* Not in a transaction, do nothing */
1598 * Now reset the high-level state
1600 s->blockState = TBLOCK_DEFAULT;
1604 IsTransactionBlock(void)
1606 TransactionState s = CurrentTransactionState;
1608 if (s->blockState == TBLOCK_INPROGRESS
1609 || s->blockState == TBLOCK_ABORT
1610 || s->blockState == TBLOCK_ENDABORT)
1617 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1619 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1621 if (info == XLOG_XACT_COMMIT)
1623 TransactionIdCommit(record->xl_xid);
1624 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1626 else if (info == XLOG_XACT_ABORT)
1628 TransactionIdAbort(record->xl_xid);
1629 /* SHOULD REMOVE FILES OF ALL FAILED-TO-BE-CREATED RELATIONS */
1632 elog(PANIC, "xact_redo: unknown op code %u", info);
1636 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1638 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1640 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1641 elog(PANIC, "xact_undo: can't undo committed xaction");
1642 else if (info != XLOG_XACT_ABORT)
1643 elog(PANIC, "xact_redo: unknown op code %u", info);
1647 xact_desc(char *buf, uint8 xl_info, char *rec)
1649 uint8 info = xl_info & ~XLR_INFO_MASK;
1651 if (info == XLOG_XACT_COMMIT)
1653 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1654 struct tm *tm = localtime(&xlrec->xtime);
1656 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1657 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1658 tm->tm_hour, tm->tm_min, tm->tm_sec);
1660 else if (info == XLOG_XACT_ABORT)
1662 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1663 struct tm *tm = localtime(&xlrec->xtime);
1665 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1666 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1667 tm->tm_hour, tm->tm_min, tm->tm_sec);
1670 strcat(buf, "UNKNOWN");
1674 XactPushRollback(void (*func) (void *), void *data)
1677 if (_RollbackFunc != NULL)
1678 elog(PANIC, "XactPushRollback: already installed");
1681 _RollbackFunc = func;
1682 _RollbackData = data;
1686 XactPopRollback(void)
1688 _RollbackFunc = NULL;