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.106 2001/07/12 04:11:13 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"
181 extern bool SharedBufferChanged;
183 static void AbortTransaction(void);
184 static void AtAbort_Cache(void);
185 static void AtAbort_Locks(void);
186 static void AtAbort_Memory(void);
187 static void AtCleanup_Memory(void);
188 static void AtCommit_Cache(void);
189 static void AtCommit_LocalCache(void);
190 static void AtCommit_Locks(void);
191 static void AtCommit_Memory(void);
192 static void AtStart_Cache(void);
193 static void AtStart_Locks(void);
194 static void AtStart_Memory(void);
195 static void CleanupTransaction(void);
196 static void CommitTransaction(void);
197 static void RecordTransactionAbort(void);
198 static void StartTransaction(void);
201 * global variables holding the current transaction state.
204 static TransactionStateData CurrentTransactionStateData = {
205 0, /* transaction id */
206 FirstCommandId, /* command id */
207 0, /* scan command id */
208 0x0, /* start time */
209 TRANS_DEFAULT, /* transaction state */
210 TBLOCK_DEFAULT /* transaction block state */
213 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
216 * User-tweakable parameters
218 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
221 int CommitDelay = 0; /* precommit delay in microseconds */
222 int CommitSiblings = 5; /* number of concurrent xacts needed to
225 static void (*_RollbackFunc) (void *) = NULL;
226 static void *_RollbackData = NULL;
229 * info returned when the system is disabled
231 * Apparently a lot of this code is inherited from other prototype systems.
233 * For DisabledStartTime, use a symbolic value to make the relationships clearer.
234 * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
235 * every day. It appears that if we return a value guaranteed larger than
236 * any real time associated with a transaction then comparisons in other
237 * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
240 static CommandId DisabledCommandId = (CommandId) -1;
242 static AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME;
245 * catalog creation transaction bootstrapping flag.
246 * This should be eliminated and added to the transaction
247 * state stuff. -cim 3/19/90
250 bool AMI_OVERRIDE = false;
252 /* ----------------------------------------------------------------
253 * transaction state accessors
254 * ----------------------------------------------------------------
257 /* --------------------------------
258 * TranactionFlushEnabled()
259 * SetTransactionFlushEnabled()
261 * These are used to test and set the "TransactionFlushState"
262 * varable. If this variable is true (the default), then
263 * the system will flush all dirty buffers to disk at the end
264 * of each transaction. If false then we are assuming the
265 * buffer pool resides in stable main memory, in which case we
266 * only do writes as necessary.
267 * --------------------------------
269 static int TransactionFlushState = 1;
272 TransactionFlushEnabled(void)
274 return TransactionFlushState;
279 SetTransactionFlushEnabled(bool state)
281 TransactionFlushState = (state == true);
285 /* --------------------------------
288 * This returns true if we are currently running a query
289 * within an executing transaction.
290 * --------------------------------
293 IsTransactionState(void)
295 TransactionState s = CurrentTransactionState;
303 case TRANS_INPROGRESS:
314 * Shouldn't get here, but lint is not happy with this...
321 /* --------------------------------
322 * IsAbortedTransactionBlockState
324 * This returns true if we are currently running a query
325 * within an aborted transaction block.
326 * --------------------------------
329 IsAbortedTransactionBlockState(void)
331 TransactionState s = CurrentTransactionState;
333 if (s->blockState == TBLOCK_ABORT)
339 /* --------------------------------
340 * OverrideTransactionSystem
342 * This is used to temporarily disable the transaction
343 * processing system in order to do initialization of
344 * the transaction system data structures and relations
346 * --------------------------------
348 static int SavedTransactionState;
351 OverrideTransactionSystem(bool flag)
353 TransactionState s = CurrentTransactionState;
357 if (s->state == TRANS_DISABLED)
360 SavedTransactionState = s->state;
361 s->state = TRANS_DISABLED;
365 if (s->state != TRANS_DISABLED)
368 s->state = SavedTransactionState;
372 /* --------------------------------
373 * GetCurrentTransactionId
375 * This returns the id of the current transaction, or
376 * the id of the "disabled" transaction.
377 * --------------------------------
380 GetCurrentTransactionId(void)
382 TransactionState s = CurrentTransactionState;
385 * if the transaction system is disabled, we return the special
386 * "disabled" transaction id.
388 if (s->state == TRANS_DISABLED)
389 return DisabledTransactionId;
392 * otherwise return the current transaction id.
394 return s->transactionIdData;
398 /* --------------------------------
399 * GetCurrentCommandId
400 * --------------------------------
403 GetCurrentCommandId(void)
405 TransactionState s = CurrentTransactionState;
408 * if the transaction system is disabled, we return the special
409 * "disabled" command id.
411 if (s->state == TRANS_DISABLED)
412 return DisabledCommandId;
418 GetScanCommandId(void)
420 TransactionState s = CurrentTransactionState;
423 * if the transaction system is disabled, we return the special
424 * "disabled" command id.
426 if (s->state == TRANS_DISABLED)
427 return DisabledCommandId;
429 return s->scanCommandId;
433 /* --------------------------------
434 * GetCurrentTransactionStartTime
435 * --------------------------------
438 GetCurrentTransactionStartTime(void)
440 TransactionState s = CurrentTransactionState;
443 * if the transaction system is disabled, we return the special
444 * "disabled" starting time.
446 if (s->state == TRANS_DISABLED)
447 return DisabledStartTime;
453 /* --------------------------------
454 * TransactionIdIsCurrentTransactionId
455 * --------------------------------
458 TransactionIdIsCurrentTransactionId(TransactionId xid)
460 TransactionState s = CurrentTransactionState;
465 return TransactionIdEquals(xid, s->transactionIdData);
469 /* --------------------------------
470 * CommandIdIsCurrentCommandId
471 * --------------------------------
474 CommandIdIsCurrentCommandId(CommandId cid)
476 TransactionState s = CurrentTransactionState;
481 return (cid == s->commandId) ? true : false;
485 CommandIdGEScanCommandId(CommandId cid)
487 TransactionState s = CurrentTransactionState;
492 return (cid >= s->scanCommandId) ? true : false;
496 /* --------------------------------
497 * CommandCounterIncrement
498 * --------------------------------
501 CommandCounterIncrement(void)
503 CurrentTransactionStateData.commandId += 1;
504 if (CurrentTransactionStateData.commandId == FirstCommandId)
505 elog(ERROR, "You may only have 2^32-1 commands per transaction");
507 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
510 * make cache changes visible to me. AtCommit_LocalCache() instead of
511 * AtCommit_Cache() is called here.
513 AtCommit_LocalCache();
518 SetScanCommandId(CommandId savedId)
520 CurrentTransactionStateData.scanCommandId = savedId;
523 /* ----------------------------------------------------------------
524 * initialization stuff
525 * ----------------------------------------------------------------
528 InitializeTransactionSystem(void)
530 InitializeTransactionLog();
533 /* ----------------------------------------------------------------
534 * StartTransaction stuff
535 * ----------------------------------------------------------------
538 /* --------------------------------
540 * --------------------------------
545 AcceptInvalidationMessages();
548 /* --------------------------------
550 * --------------------------------
557 * at present, it is unknown to me what belongs here -cim 3/18/90
559 * There isn't anything to do at the start of a xact for locks. -mer
564 /* --------------------------------
566 * --------------------------------
573 * We shouldn't have any transaction contexts already.
575 Assert(TopTransactionContext == NULL);
576 Assert(TransactionCommandContext == NULL);
579 * Create a toplevel context for the transaction.
581 TopTransactionContext =
582 AllocSetContextCreate(TopMemoryContext,
583 "TopTransactionContext",
584 ALLOCSET_DEFAULT_MINSIZE,
585 ALLOCSET_DEFAULT_INITSIZE,
586 ALLOCSET_DEFAULT_MAXSIZE);
589 * Create a statement-level context and make it active.
591 TransactionCommandContext =
592 AllocSetContextCreate(TopTransactionContext,
593 "TransactionCommandContext",
594 ALLOCSET_DEFAULT_MINSIZE,
595 ALLOCSET_DEFAULT_INITSIZE,
596 ALLOCSET_DEFAULT_MAXSIZE);
597 MemoryContextSwitchTo(TransactionCommandContext);
601 /* ----------------------------------------------------------------
602 * CommitTransaction stuff
603 * ----------------------------------------------------------------
606 /* --------------------------------
607 * RecordTransactionCommit
609 * Note: the two calls to BufferManagerFlush() exist to ensure
610 * that data pages are written before log pages. These
611 * explicit calls should be replaced by a more efficient
612 * ordered page write scheme in the buffer manager
614 * --------------------------------
617 RecordTransactionCommit()
622 xid = GetCurrentTransactionId();
624 leak = BufferPoolCheckLeak();
626 if (MyLastRecPtr.xrecoff != 0)
629 xl_xact_commit xlrec;
634 xlrec.xtime = time(NULL);
635 rdata.buffer = InvalidBuffer;
636 rdata.data = (char *) (&xlrec);
637 rdata.len = SizeOfXactCommit;
640 START_CRIT_SECTION();
643 * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
645 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
648 * Sleep before commit! So we can flush more than one commit
649 * records per single fsync. (The idea is some other backend may
650 * do the XLogFlush while we're sleeping. This needs work still,
651 * because on most Unixen, the minimum select() delay is 10msec or
652 * more, which is way too long.)
654 * We do not sleep if enableFsync is not turned on, nor if there are
655 * fewer than CommitSiblings other backends with active
658 if (CommitDelay > 0 && enableFsync &&
659 CountActiveBackends() >= CommitSiblings)
661 struct timeval delay;
664 delay.tv_usec = CommitDelay;
665 (void) select(0, NULL, NULL, NULL, &delay);
670 /* Break the chain of back-links in the XLOG records I output */
671 MyLastRecPtr.xrecoff = 0;
673 TransactionIdCommit(xid);
678 /* Show myself as out of the transaction in PROC array */
679 MyProc->logRec.xrecoff = 0;
682 ResetBufferPool(true);
686 /* --------------------------------
688 * --------------------------------
694 * Make catalog changes visible to all backends.
696 AtEOXactInvalidationMessages(true);
699 /* --------------------------------
700 * AtCommit_LocalCache
701 * --------------------------------
704 AtCommit_LocalCache(void)
707 * Make catalog changes visible to me for the next command.
709 CommandEndInvalidationMessages(true);
712 /* --------------------------------
714 * --------------------------------
720 * XXX What if ProcReleaseLocks fails? (race condition?)
722 * Then you're up a creek! -mer 5/24/92
724 ProcReleaseLocks(true);
727 /* --------------------------------
729 * --------------------------------
732 AtCommit_Memory(void)
735 * Now that we're "out" of a transaction, have the system allocate
736 * things in the top memory context instead of per-transaction
739 MemoryContextSwitchTo(TopMemoryContext);
742 * Release all transaction-local memory.
744 Assert(TopTransactionContext != NULL);
745 MemoryContextDelete(TopTransactionContext);
746 TopTransactionContext = NULL;
747 TransactionCommandContext = NULL;
750 /* ----------------------------------------------------------------
751 * AbortTransaction stuff
752 * ----------------------------------------------------------------
755 /* --------------------------------
756 * RecordTransactionAbort
757 * --------------------------------
760 RecordTransactionAbort(void)
762 TransactionId xid = GetCurrentTransactionId();
765 * Double check here is to catch case that we aborted partway through
766 * RecordTransactionCommit ...
768 if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
774 xlrec.xtime = time(NULL);
775 rdata.buffer = InvalidBuffer;
776 rdata.data = (char *) (&xlrec);
777 rdata.len = SizeOfXactAbort;
780 START_CRIT_SECTION();
782 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
784 TransactionIdAbort(xid);
789 /* Break the chain of back-links in the XLOG records I output */
790 MyLastRecPtr.xrecoff = 0;
791 /* Show myself as out of the transaction in PROC array */
792 MyProc->logRec.xrecoff = 0;
795 * Tell bufmgr and smgr to release resources.
797 ResetBufferPool(false); /* false -> is abort */
800 /* --------------------------------
802 * --------------------------------
807 RelationCacheAbort();
808 AtEOXactInvalidationMessages(false);
811 /* --------------------------------
813 * --------------------------------
820 * XXX What if ProcReleaseLocks() fails? (race condition?)
822 * Then you're up a creek without a paddle! -mer
824 ProcReleaseLocks(false);
828 /* --------------------------------
830 * --------------------------------
837 * Make sure we are in a valid context (not a child of
838 * TransactionCommandContext...). Note that it is possible for this
839 * code to be called when we aren't in a transaction at all; go
840 * directly to TopMemoryContext in that case.
842 if (TransactionCommandContext != NULL)
844 MemoryContextSwitchTo(TransactionCommandContext);
847 * We do not want to destroy transaction contexts yet, but it
848 * should be OK to delete any command-local memory.
850 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
853 MemoryContextSwitchTo(TopMemoryContext);
857 /* ----------------------------------------------------------------
858 * CleanupTransaction stuff
859 * ----------------------------------------------------------------
862 /* --------------------------------
864 * --------------------------------
867 AtCleanup_Memory(void)
871 * Now that we're "out" of a transaction, have the system allocate
872 * things in the top memory context instead of per-transaction
875 MemoryContextSwitchTo(TopMemoryContext);
878 * Release all transaction-local memory.
880 if (TopTransactionContext != NULL)
881 MemoryContextDelete(TopTransactionContext);
882 TopTransactionContext = NULL;
883 TransactionCommandContext = NULL;
887 /* ----------------------------------------------------------------
889 * ----------------------------------------------------------------
892 /* --------------------------------
895 * --------------------------------
898 StartTransaction(void)
900 TransactionState s = CurrentTransactionState;
903 XactIsoLevel = DefaultXactIsoLevel;
906 * Check the current transaction state. If the transaction system is
907 * switched off, or if we're already in a transaction, do nothing.
908 * We're already in a transaction when the monitor sends a null
909 * command to the backend to flush the comm channel. This is a hacky
910 * fix to a communications problem, and we keep having to deal with it
911 * here. We should fix the comm channel code. mao 080891
913 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
917 * set the current transaction state information appropriately during
920 s->state = TRANS_START;
922 SetReindexProcessing(false);
925 * generate a new transaction id
927 GetNewTransactionId(&(s->transactionIdData));
929 XactLockTableInsert(s->transactionIdData);
932 * initialize current transaction state fields
934 s->commandId = FirstCommandId;
935 s->scanCommandId = FirstCommandId;
936 s->startTime = GetCurrentAbsoluteTime();
939 * initialize the various transaction subsystems
946 * Tell the trigger manager to we're starting a transaction
948 DeferredTriggerBeginXact();
951 * done with start processing, set current transaction state to "in
954 s->state = TRANS_INPROGRESS;
960 * Tell me if we are currently in progress
964 CurrentXactInProgress(void)
966 return CurrentTransactionState->state == TRANS_INPROGRESS;
971 /* --------------------------------
974 * --------------------------------
977 CommitTransaction(void)
979 TransactionState s = CurrentTransactionState;
982 * check the current transaction state
984 if (s->state == TRANS_DISABLED)
987 if (s->state != TRANS_INPROGRESS)
988 elog(NOTICE, "CommitTransaction and not in in-progress state ");
991 * Tell the trigger manager that this transaction is about to be
992 * committed. He'll invoke all trigger deferred until XACT before we
993 * really start on committing the transaction.
995 DeferredTriggerEndXact();
997 /* Prevent cancel/die interrupt while cleaning up */
1001 * set the current transaction state information appropriately during
1002 * the abort processing
1004 s->state = TRANS_COMMIT;
1007 * do commit processing
1010 /* handle commit for large objects [ PA, 7/17/98 ] */
1013 /* NOTIFY commit must also come before lower-level cleanup */
1018 RecordTransactionCommit();
1021 * Let others know about no transaction in progress by me. Note that
1022 * this must be done _before_ releasing locks we hold and
1023 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1024 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1025 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1026 * xid 0 as running as well or it will see two tuple versions - one
1027 * deleted by xid 1 and one inserted by xid 0.
1029 if (MyProc != (PROC *) NULL)
1031 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1032 SpinAcquire(SInvalLock);
1033 MyProc->xid = InvalidTransactionId;
1034 MyProc->xmin = InvalidTransactionId;
1035 SpinRelease(SInvalLock);
1038 RelationPurgeLocalRelation(true);
1039 AtEOXact_temp_relations(true);
1040 smgrDoPendingDeletes(true);
1046 AtEOXact_CatCache(true);
1050 SharedBufferChanged = false;/* safest place to do it */
1052 /* Count transaction commit in statistics collector */
1053 pgstat_count_xact_commit();
1056 * done with commit processing, set current transaction state back to
1059 s->state = TRANS_DEFAULT;
1061 RESUME_INTERRUPTS();
1064 /* --------------------------------
1067 * --------------------------------
1070 AbortTransaction(void)
1072 TransactionState s = CurrentTransactionState;
1074 /* Prevent cancel/die interrupt while cleaning up */
1078 * Let others to know about no transaction in progress - vadim
1081 * XXX it'd be nice to acquire SInvalLock for this, but too much risk of
1082 * lockup: what if we were holding SInvalLock when we elog'd? Net effect
1083 * is that we are relying on fetch/store of an xid to be atomic, else
1084 * other backends might see a partially-zeroed xid here. Would it be
1085 * safe to release spins before we reset xid/xmin? But see also
1086 * GetNewTransactionId, which does the same thing.
1088 if (MyProc != (PROC *) NULL)
1090 MyProc->xid = InvalidTransactionId;
1091 MyProc->xmin = InvalidTransactionId;
1095 * Release any spinlocks or buffer context locks we might be holding
1096 * as quickly as possible. (Real locks, however, must be held till we
1097 * finish aborting.) Releasing spinlocks is critical since we might
1098 * try to grab them again while cleaning up!
1100 ProcReleaseSpins(NULL);
1104 * Also clean up any open wait for lock, since the lock manager will
1105 * choke if we try to wait for another lock before doing this.
1110 * check the current transaction state
1112 if (s->state == TRANS_DISABLED)
1114 RESUME_INTERRUPTS();
1118 if (s->state != TRANS_INPROGRESS)
1119 elog(NOTICE, "AbortTransaction and not in in-progress state");
1122 * set the current transaction state information appropriately during
1123 * the abort processing
1125 s->state = TRANS_ABORT;
1128 * Reset user id which might have been changed transiently
1130 SetUserId(GetSessionUserId());
1133 * do abort processing
1135 DeferredTriggerAbortXact();
1136 lo_commit(false); /* 'false' means it's abort */
1140 RecordTransactionAbort();
1142 /* Count transaction abort in statistics collector */
1143 pgstat_count_xact_rollback();
1145 RelationPurgeLocalRelation(false);
1146 AtEOXact_temp_relations(false);
1147 smgrDoPendingDeletes(false);
1152 AtEOXact_CatCache(false);
1157 SharedBufferChanged = false;/* safest place to do it */
1160 * State remains TRANS_ABORT until CleanupTransaction().
1162 RESUME_INTERRUPTS();
1165 /* --------------------------------
1166 * CleanupTransaction
1168 * --------------------------------
1171 CleanupTransaction(void)
1173 TransactionState s = CurrentTransactionState;
1175 if (s->state == TRANS_DISABLED)
1179 * State should still be TRANS_ABORT from AbortTransaction().
1181 if (s->state != TRANS_ABORT)
1182 elog(FATAL, "CleanupTransaction and not in abort state");
1185 * do abort cleanup processing
1190 * done with abort processing, set current transaction state back to
1193 s->state = TRANS_DEFAULT;
1196 /* --------------------------------
1197 * StartTransactionCommand
1198 * --------------------------------
1201 StartTransactionCommand(void)
1203 TransactionState s = CurrentTransactionState;
1205 switch (s->blockState)
1209 * if we aren't in a transaction block, we just do our usual
1210 * start transaction.
1212 case TBLOCK_DEFAULT:
1217 * We should never experience this -- if we do it means the
1218 * BEGIN state was not changed in the previous
1219 * CommitTransactionCommand(). If we get it, we print a
1220 * warning and change to the in-progress state.
1223 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1224 s->blockState = TBLOCK_INPROGRESS;
1228 * This is the case when are somewhere in a transaction block
1229 * and about to start a new command. For now we do nothing
1230 * but someday we may do command-local resource
1233 case TBLOCK_INPROGRESS:
1237 * As with BEGIN, we should never experience this if we do it
1238 * means the END state was not changed in the previous
1239 * CommitTransactionCommand(). If we get it, we print a
1240 * warning, commit the transaction, start a new transaction
1241 * and change to the default state.
1244 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1245 s->blockState = TBLOCK_DEFAULT;
1246 CommitTransaction();
1251 * Here we are in the middle of a transaction block but one of
1252 * the commands caused an abort so we do nothing but remain in
1253 * the abort state. Eventually we will get to the "END
1254 * TRANSACTION" which will set things straight.
1260 * This means we somehow aborted and the last call to
1261 * CommitTransactionCommand() didn't clear the state so we
1262 * remain in the ENDABORT state and maybe next time we get to
1263 * CommitTransactionCommand() the state will get reset to
1266 case TBLOCK_ENDABORT:
1267 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1272 * We must switch to TransactionCommandContext before returning. This
1273 * is already done if we called StartTransaction, otherwise not.
1275 Assert(TransactionCommandContext != NULL);
1276 MemoryContextSwitchTo(TransactionCommandContext);
1279 /* --------------------------------
1280 * CommitTransactionCommand
1281 * --------------------------------
1284 CommitTransactionCommand(void)
1286 TransactionState s = CurrentTransactionState;
1288 switch (s->blockState)
1292 * if we aren't in a transaction block, we just do our usual
1293 * transaction commit
1295 case TBLOCK_DEFAULT:
1296 CommitTransaction();
1300 * This is the case right after we get a "BEGIN TRANSACTION"
1301 * command, but the user hasn't done anything else yet, so we
1302 * change to the "transaction block in progress" state and
1306 s->blockState = TBLOCK_INPROGRESS;
1310 * This is the case when we have finished executing a command
1311 * someplace within a transaction block. We increment the
1312 * command counter and return. Someday we may free resources
1313 * local to the command.
1315 * That someday is today, at least for memory allocated in
1316 * TransactionCommandContext. - vadim 03/25/97
1318 case TBLOCK_INPROGRESS:
1319 CommandCounterIncrement();
1320 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1324 * This is the case when we just got the "END TRANSACTION"
1325 * statement, so we commit the transaction and go back to the
1329 CommitTransaction();
1330 s->blockState = TBLOCK_DEFAULT;
1334 * Here we are in the middle of a transaction block but one of
1335 * the commands caused an abort so we do nothing but remain in
1336 * the abort state. Eventually we will get to the "END
1337 * TRANSACTION" which will set things straight.
1343 * Here we were in an aborted transaction block which just
1344 * processed the "END TRANSACTION" command from the user, so
1345 * clean up and return to the default state.
1347 case TBLOCK_ENDABORT:
1348 CleanupTransaction();
1349 s->blockState = TBLOCK_DEFAULT;
1354 /* --------------------------------
1355 * AbortCurrentTransaction
1356 * --------------------------------
1359 AbortCurrentTransaction(void)
1361 TransactionState s = CurrentTransactionState;
1363 switch (s->blockState)
1367 * if we aren't in a transaction block, we just do the basic
1368 * abort & cleanup transaction.
1370 case TBLOCK_DEFAULT:
1372 CleanupTransaction();
1376 * If we are in the TBLOCK_BEGIN it means something screwed up
1377 * right after reading "BEGIN TRANSACTION" so we enter the
1378 * abort state. Eventually an "END TRANSACTION" will fix
1382 s->blockState = TBLOCK_ABORT;
1384 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1388 * This is the case when are somewhere in a transaction block
1389 * which aborted so we abort the transaction and set the ABORT
1390 * state. Eventually an "END TRANSACTION" will fix things and
1391 * restore us to a normal state.
1393 case TBLOCK_INPROGRESS:
1394 s->blockState = TBLOCK_ABORT;
1396 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1400 * Here, the system was fouled up just after the user wanted
1401 * to end the transaction block so we abort the transaction
1402 * and put us back into the default state.
1405 s->blockState = TBLOCK_DEFAULT;
1407 CleanupTransaction();
1411 * Here, we are already in an aborted transaction state and
1412 * are waiting for an "END TRANSACTION" to come along and lo
1413 * and behold, we abort again! So we just remain in the abort
1420 * Here we were in an aborted transaction block which just
1421 * processed the "END TRANSACTION" command but somehow aborted
1422 * again.. since we must have done the abort processing, we
1423 * clean up and return to the default state.
1425 case TBLOCK_ENDABORT:
1426 CleanupTransaction();
1427 s->blockState = TBLOCK_DEFAULT;
1432 /* ----------------------------------------------------------------
1433 * transaction block support
1434 * ----------------------------------------------------------------
1436 /* --------------------------------
1437 * BeginTransactionBlock
1438 * --------------------------------
1441 BeginTransactionBlock(void)
1443 TransactionState s = CurrentTransactionState;
1446 * check the current transaction state
1448 if (s->state == TRANS_DISABLED)
1451 if (s->blockState != TBLOCK_DEFAULT)
1452 elog(NOTICE, "BEGIN: already a transaction in progress");
1455 * set the current transaction block state information appropriately
1456 * during begin processing
1458 s->blockState = TBLOCK_BEGIN;
1461 * do begin processing
1465 * done with begin processing, set block state to inprogress
1467 s->blockState = TBLOCK_INPROGRESS;
1470 /* --------------------------------
1471 * EndTransactionBlock
1472 * --------------------------------
1475 EndTransactionBlock(void)
1477 TransactionState s = CurrentTransactionState;
1480 * check the current transaction state
1482 if (s->state == TRANS_DISABLED)
1485 if (s->blockState == TBLOCK_INPROGRESS)
1489 * here we are in a transaction block which should commit when we
1490 * get to the upcoming CommitTransactionCommand() so we set the
1491 * state to "END". CommitTransactionCommand() will recognize this
1492 * and commit the transaction and return us to the default state
1494 s->blockState = TBLOCK_END;
1498 if (s->blockState == TBLOCK_ABORT)
1502 * here, we are in a transaction block which aborted and since the
1503 * AbortTransaction() was already done, we do whatever is needed
1504 * and change to the special "END ABORT" state. The upcoming
1505 * CommitTransactionCommand() will recognise this and then put us
1506 * back in the default state.
1508 s->blockState = TBLOCK_ENDABORT;
1513 * here, the user issued COMMIT when not inside a transaction. Issue a
1514 * notice and go to abort state. The upcoming call to
1515 * CommitTransactionCommand() will then put us back into the default
1518 elog(NOTICE, "COMMIT: no transaction in progress");
1520 s->blockState = TBLOCK_ENDABORT;
1523 /* --------------------------------
1524 * AbortTransactionBlock
1525 * --------------------------------
1529 AbortTransactionBlock(void)
1531 TransactionState s = CurrentTransactionState;
1534 * check the current transaction state
1536 if (s->state == TRANS_DISABLED)
1539 if (s->blockState == TBLOCK_INPROGRESS)
1543 * here we were inside a transaction block something screwed up
1544 * inside the system so we enter the abort state, do the abort
1545 * processing and then return. We remain in the abort state until
1546 * we see an END TRANSACTION command.
1548 s->blockState = TBLOCK_ABORT;
1554 * here, the user issued ABORT when not inside a transaction. Issue a
1555 * notice and go to abort state. The upcoming call to
1556 * CommitTransactionCommand() will then put us back into the default
1559 elog(NOTICE, "ROLLBACK: no transaction in progress");
1561 s->blockState = TBLOCK_ENDABORT;
1566 /* --------------------------------
1567 * UserAbortTransactionBlock
1568 * --------------------------------
1571 UserAbortTransactionBlock(void)
1573 TransactionState s = CurrentTransactionState;
1576 * check the current transaction state
1578 if (s->state == TRANS_DISABLED)
1582 * if the transaction has already been automatically aborted with an
1583 * error, and the user subsequently types 'abort', allow it. (the
1584 * behavior is the same as if they had typed 'end'.)
1586 if (s->blockState == TBLOCK_ABORT)
1588 s->blockState = TBLOCK_ENDABORT;
1592 if (s->blockState == TBLOCK_INPROGRESS)
1596 * here we were inside a transaction block and we got an abort
1597 * command from the user, so we move to the abort state, do the
1598 * abort processing and then change to the ENDABORT state so we
1599 * will end up in the default state after the upcoming
1600 * CommitTransactionCommand().
1602 s->blockState = TBLOCK_ABORT;
1604 s->blockState = TBLOCK_ENDABORT;
1609 * here, the user issued ABORT when not inside a transaction. Issue a
1610 * notice and go to abort state. The upcoming call to
1611 * CommitTransactionCommand() will then put us back into the default
1614 elog(NOTICE, "ROLLBACK: no transaction in progress");
1616 s->blockState = TBLOCK_ENDABORT;
1619 /* --------------------------------
1620 * AbortOutOfAnyTransaction
1622 * This routine is provided for error recovery purposes. It aborts any
1623 * active transaction or transaction block, leaving the system in a known
1625 * --------------------------------
1628 AbortOutOfAnyTransaction(void)
1630 TransactionState s = CurrentTransactionState;
1633 * Get out of any low-level transaction
1638 case TRANS_INPROGRESS:
1640 /* In a transaction, so clean up */
1642 CleanupTransaction();
1645 /* AbortTransaction already done, still need Cleanup */
1646 CleanupTransaction();
1649 case TRANS_DISABLED:
1650 /* Not in a transaction, do nothing */
1655 * Now reset the high-level state
1657 s->blockState = TBLOCK_DEFAULT;
1661 IsTransactionBlock(void)
1663 TransactionState s = CurrentTransactionState;
1665 if (s->blockState == TBLOCK_INPROGRESS
1666 || s->blockState == TBLOCK_ABORT
1667 || s->blockState == TBLOCK_ENDABORT)
1674 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1676 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1678 if (info == XLOG_XACT_COMMIT)
1680 TransactionIdCommit(record->xl_xid);
1681 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1683 else if (info == XLOG_XACT_ABORT)
1684 TransactionIdAbort(record->xl_xid);
1686 elog(STOP, "xact_redo: unknown op code %u", info);
1690 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1692 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1694 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1695 elog(STOP, "xact_undo: can't undo committed xaction");
1696 else if (info != XLOG_XACT_ABORT)
1697 elog(STOP, "xact_redo: unknown op code %u", info);
1701 xact_desc(char *buf, uint8 xl_info, char *rec)
1703 uint8 info = xl_info & ~XLR_INFO_MASK;
1705 if (info == XLOG_XACT_COMMIT)
1707 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1708 struct tm *tm = localtime(&xlrec->xtime);
1710 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1711 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1712 tm->tm_hour, tm->tm_min, tm->tm_sec);
1714 else if (info == XLOG_XACT_ABORT)
1716 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1717 struct tm *tm = localtime(&xlrec->xtime);
1719 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1720 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1721 tm->tm_hour, tm->tm_min, tm->tm_sec);
1724 strcat(buf, "UNKNOWN");
1728 XactPushRollback(void (*func) (void *), void *data)
1731 if (_RollbackFunc != NULL)
1732 elog(STOP, "XactPushRollback: already installed");
1735 _RollbackFunc = func;
1736 _RollbackData = data;
1740 XactPopRollback(void)
1742 _RollbackFunc = NULL;