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.96 2001/01/24 19:42:51 momjian 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 "catalog/heap.h"
161 #include "catalog/index.h"
162 #include "commands/async.h"
163 #include "commands/sequence.h"
164 #include "commands/trigger.h"
165 #include "executor/spi.h"
166 #include "libpq/be-fsstubs.h"
167 #include "miscadmin.h"
168 #include "storage/proc.h"
169 #include "storage/sinval.h"
170 #include "storage/smgr.h"
171 #include "utils/inval.h"
172 #include "utils/memutils.h"
173 #include "utils/portal.h"
174 #include "utils/catcache.h"
175 #include "utils/relcache.h"
176 #include "utils/temprel.h"
178 extern bool SharedBufferChanged;
180 void RecordTransactionCommit(void);
182 static void AbortTransaction(void);
183 static void AtAbort_Cache(void);
184 static void AtAbort_Locks(void);
185 static void AtAbort_Memory(void);
186 static void AtCleanup_Memory(void);
187 static void AtCommit_Cache(void);
188 static void AtCommit_LocalCache(void);
189 static void AtCommit_Locks(void);
190 static void AtCommit_Memory(void);
191 static void AtStart_Cache(void);
192 static void AtStart_Locks(void);
193 static void AtStart_Memory(void);
194 static void CleanupTransaction(void);
195 static void CommitTransaction(void);
196 static void RecordTransactionAbort(void);
197 static void StartTransaction(void);
200 * global variables holding the current transaction state.
202 * Note: when we are running several slave processes, the
203 * current transaction state data is copied into shared memory
204 * and the CurrentTransactionState pointer changed to
205 * point to the shared copy. All this occurrs in slaves.c
208 TransactionStateData CurrentTransactionStateData = {
209 0, /* transaction id */
210 FirstCommandId, /* command id */
211 0, /* scan command id */
212 0x0, /* start time */
213 TRANS_DEFAULT, /* transaction state */
214 TBLOCK_DEFAULT /* transaction block state */
217 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
219 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
222 #include "access/xlogutils.h"
224 int CommitDelay = 5; /* 1/200000 sec */
226 static void (*_RollbackFunc)(void*) = NULL;
227 static void *_RollbackData = NULL;
230 * info returned when the system is disabled
232 * 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
239 * Note: I have no idea what the significance of the
240 * 1073741823 in DisabledStartTime.. I just carried
241 * this over when converting things from the old
242 * V1 transaction system. -cim 3/18/90
245 TransactionId DisabledTransactionId = (TransactionId) -1;
247 CommandId DisabledCommandId = (CommandId) -1;
249 AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
255 bool CommandIdCounterOverflowFlag;
258 * catalog creation transaction bootstrapping flag.
259 * This should be eliminated and added to the transaction
260 * state stuff. -cim 3/19/90
263 bool AMI_OVERRIDE = false;
265 /* ----------------------------------------------------------------
266 * transaction state accessors
267 * ----------------------------------------------------------------
270 /* --------------------------------
271 * TranactionFlushEnabled()
272 * SetTransactionFlushEnabled()
274 * These are used to test and set the "TransactionFlushState"
275 * varable. If this variable is true (the default), then
276 * the system will flush all dirty buffers to disk at the end
277 * of each transaction. If false then we are assuming the
278 * buffer pool resides in stable main memory, in which case we
279 * only do writes as necessary.
280 * --------------------------------
282 static int TransactionFlushState = 1;
285 TransactionFlushEnabled(void)
287 return TransactionFlushState;
292 SetTransactionFlushEnabled(bool state)
294 TransactionFlushState = (state == true);
298 /* --------------------------------
301 * This returns true if we are currently running a query
302 * within an executing transaction.
303 * --------------------------------
306 IsTransactionState(void)
308 TransactionState s = CurrentTransactionState;
316 case TRANS_INPROGRESS:
327 * Shouldn't get here, but lint is not happy with this...
334 /* --------------------------------
335 * IsAbortedTransactionBlockState
337 * This returns true if we are currently running a query
338 * within an aborted transaction block.
339 * --------------------------------
342 IsAbortedTransactionBlockState(void)
344 TransactionState s = CurrentTransactionState;
346 if (s->blockState == TBLOCK_ABORT)
352 /* --------------------------------
353 * OverrideTransactionSystem
355 * This is used to temporarily disable the transaction
356 * processing system in order to do initialization of
357 * the transaction system data structures and relations
359 * --------------------------------
361 int SavedTransactionState;
364 OverrideTransactionSystem(bool flag)
366 TransactionState s = CurrentTransactionState;
370 if (s->state == TRANS_DISABLED)
373 SavedTransactionState = s->state;
374 s->state = TRANS_DISABLED;
378 if (s->state != TRANS_DISABLED)
381 s->state = SavedTransactionState;
385 /* --------------------------------
386 * GetCurrentTransactionId
388 * This returns the id of the current transaction, or
389 * the id of the "disabled" transaction.
390 * --------------------------------
393 GetCurrentTransactionId(void)
395 TransactionState s = CurrentTransactionState;
398 * if the transaction system is disabled, we return
399 * the special "disabled" transaction id.
402 if (s->state == TRANS_DISABLED)
403 return (TransactionId) DisabledTransactionId;
406 * otherwise return the current transaction id.
409 return (TransactionId) s->transactionIdData;
413 /* --------------------------------
414 * GetCurrentCommandId
415 * --------------------------------
418 GetCurrentCommandId(void)
420 TransactionState s = CurrentTransactionState;
423 * if the transaction system is disabled, we return
424 * the special "disabled" command id.
427 if (s->state == TRANS_DISABLED)
428 return (CommandId) DisabledCommandId;
434 GetScanCommandId(void)
436 TransactionState s = CurrentTransactionState;
439 * if the transaction system is disabled, we return
440 * the special "disabled" command id.
443 if (s->state == TRANS_DISABLED)
444 return (CommandId) DisabledCommandId;
446 return s->scanCommandId;
450 /* --------------------------------
451 * GetCurrentTransactionStartTime
452 * --------------------------------
455 GetCurrentTransactionStartTime(void)
457 TransactionState s = CurrentTransactionState;
460 * if the transaction system is disabled, we return
461 * the special "disabled" starting time.
464 if (s->state == TRANS_DISABLED)
465 return (AbsoluteTime) DisabledStartTime;
471 /* --------------------------------
472 * TransactionIdIsCurrentTransactionId
473 * --------------------------------
476 TransactionIdIsCurrentTransactionId(TransactionId xid)
478 TransactionState s = CurrentTransactionState;
484 TransactionIdEquals(xid, s->transactionIdData);
488 /* --------------------------------
489 * CommandIdIsCurrentCommandId
490 * --------------------------------
493 CommandIdIsCurrentCommandId(CommandId cid)
495 TransactionState s = CurrentTransactionState;
500 return (cid == s->commandId) ? true : false;
504 CommandIdGEScanCommandId(CommandId cid)
506 TransactionState s = CurrentTransactionState;
511 return (cid >= s->scanCommandId) ? true : false;
515 /* --------------------------------
516 * ClearCommandIdCounterOverflowFlag
517 * --------------------------------
521 ClearCommandIdCounterOverflowFlag(void)
523 CommandIdCounterOverflowFlag = false;
528 /* --------------------------------
529 * CommandCounterIncrement
530 * --------------------------------
533 CommandCounterIncrement(void)
535 CurrentTransactionStateData.commandId += 1;
536 if (CurrentTransactionStateData.commandId == FirstCommandId)
538 CommandIdCounterOverflowFlag = true;
539 elog(ERROR, "You may only have 2^32-1 commands per transaction");
542 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
545 * make cache changes visible to me. AtCommit_LocalCache() instead of
546 * AtCommit_Cache() is called here.
548 AtCommit_LocalCache();
554 SetScanCommandId(CommandId savedId)
557 CurrentTransactionStateData.scanCommandId = savedId;
561 /* ----------------------------------------------------------------
562 * initialization stuff
563 * ----------------------------------------------------------------
566 InitializeTransactionSystem(void)
568 InitializeTransactionLog();
571 /* ----------------------------------------------------------------
572 * StartTransaction stuff
573 * ----------------------------------------------------------------
576 /* --------------------------------
578 * --------------------------------
586 /* --------------------------------
588 * --------------------------------
595 * at present, it is unknown to me what belongs here -cim 3/18/90
597 * There isn't anything to do at the start of a xact for locks. -mer
602 /* --------------------------------
604 * --------------------------------
610 * We shouldn't have any transaction contexts already.
613 Assert(TopTransactionContext == NULL);
614 Assert(TransactionCommandContext == NULL);
617 * Create a toplevel context for the transaction.
620 TopTransactionContext =
621 AllocSetContextCreate(TopMemoryContext,
622 "TopTransactionContext",
623 ALLOCSET_DEFAULT_MINSIZE,
624 ALLOCSET_DEFAULT_INITSIZE,
625 ALLOCSET_DEFAULT_MAXSIZE);
628 * Create a statement-level context and make it active.
631 TransactionCommandContext =
632 AllocSetContextCreate(TopTransactionContext,
633 "TransactionCommandContext",
634 ALLOCSET_DEFAULT_MINSIZE,
635 ALLOCSET_DEFAULT_INITSIZE,
636 ALLOCSET_DEFAULT_MAXSIZE);
637 MemoryContextSwitchTo(TransactionCommandContext);
641 /* ----------------------------------------------------------------
642 * CommitTransaction stuff
643 * ----------------------------------------------------------------
646 /* --------------------------------
647 * RecordTransactionCommit
649 * Note: the two calls to BufferManagerFlush() exist to ensure
650 * that data pages are written before log pages. These
651 * explicit calls should be replaced by a more efficient
652 * ordered page write scheme in the buffer manager
654 * --------------------------------
657 RecordTransactionCommit()
662 xid = GetCurrentTransactionId();
664 leak = BufferPoolCheckLeak();
666 if (MyLastRecPtr.xrecoff != 0)
669 xl_xact_commit xlrec;
670 struct timeval delay;
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.
692 delay.tv_usec = CommitDelay;
693 (void) select(0, NULL, NULL, NULL, &delay);
695 MyLastRecPtr.xrecoff = 0;
697 TransactionIdCommit(xid);
699 MyProc->logRec.xrecoff = 0;
704 ResetBufferPool(true);
708 /* --------------------------------
710 * --------------------------------
716 * Make catalog changes visible to all backend.
719 RegisterInvalid(true);
722 /* --------------------------------
723 * AtCommit_LocalCache
724 * --------------------------------
727 AtCommit_LocalCache(void)
730 * Make catalog changes visible to me for the next command.
733 ImmediateLocalInvalidation(true);
736 /* --------------------------------
738 * --------------------------------
744 * XXX What if ProcReleaseLocks fails? (race condition?)
746 * Then you're up a creek! -mer 5/24/92
749 ProcReleaseLocks(true);
752 /* --------------------------------
754 * --------------------------------
757 AtCommit_Memory(void)
760 * Now that we're "out" of a transaction, have the
761 * system allocate things in the top memory context instead
762 * of per-transaction contexts.
765 MemoryContextSwitchTo(TopMemoryContext);
768 * Release all transaction-local memory.
771 Assert(TopTransactionContext != NULL);
772 MemoryContextDelete(TopTransactionContext);
773 TopTransactionContext = NULL;
774 TransactionCommandContext = NULL;
777 /* ----------------------------------------------------------------
778 * AbortTransaction stuff
779 * ----------------------------------------------------------------
782 /* --------------------------------
783 * RecordTransactionAbort
784 * --------------------------------
787 RecordTransactionAbort(void)
789 TransactionId xid = GetCurrentTransactionId();
791 if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
797 xlrec.xtime = time(NULL);
798 rdata.buffer = InvalidBuffer;
799 rdata.data = (char *)(&xlrec);
800 rdata.len = SizeOfXactAbort;
803 START_CRIT_SECTION();
804 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
806 TransactionIdAbort(xid);
807 MyProc->logRec.xrecoff = 0;
812 * Tell bufmgr and smgr to release resources.
814 ResetBufferPool(false); /* false -> is abort */
817 /* --------------------------------
819 * --------------------------------
824 RelationCacheAbort();
825 RegisterInvalid(false);
828 /* --------------------------------
830 * --------------------------------
836 * XXX What if ProcReleaseLocks() fails? (race condition?)
838 * Then you're up a creek without a paddle! -mer
841 ProcReleaseLocks(false);
845 /* --------------------------------
847 * --------------------------------
853 * Make sure we are in a valid context (not a child of
854 * TransactionCommandContext...). Note that it is possible
855 * for this code to be called when we aren't in a transaction
856 * at all; go directly to TopMemoryContext in that case.
859 if (TransactionCommandContext != NULL)
861 MemoryContextSwitchTo(TransactionCommandContext);
864 * We do not want to destroy transaction contexts yet,
865 * but it should be OK to delete any command-local memory.
868 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
872 MemoryContextSwitchTo(TopMemoryContext);
877 /* ----------------------------------------------------------------
878 * CleanupTransaction stuff
879 * ----------------------------------------------------------------
882 /* --------------------------------
884 * --------------------------------
887 AtCleanup_Memory(void)
890 * Now that we're "out" of a transaction, have the
891 * system allocate things in the top memory context instead
892 * of per-transaction contexts.
895 MemoryContextSwitchTo(TopMemoryContext);
898 * Release all transaction-local memory.
901 if (TopTransactionContext != NULL)
902 MemoryContextDelete(TopTransactionContext);
903 TopTransactionContext = NULL;
904 TransactionCommandContext = NULL;
908 /* ----------------------------------------------------------------
910 * ----------------------------------------------------------------
913 /* --------------------------------
916 * --------------------------------
919 StartTransaction(void)
921 TransactionState s = CurrentTransactionState;
924 XactIsoLevel = DefaultXactIsoLevel;
927 * Check the current transaction state. If the transaction system
928 * is switched off, or if we're already in a transaction, do nothing.
929 * We're already in a transaction when the monitor sends a null
930 * command to the backend to flush the comm channel. This is a
931 * hacky fix to a communications problem, and we keep having to
932 * deal with it here. We should fix the comm channel code. mao 080891
935 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
939 * set the current transaction state information
940 * appropriately during start processing
943 s->state = TRANS_START;
945 SetReindexProcessing(false);
948 * generate a new transaction id
951 GetNewTransactionId(&(s->transactionIdData));
953 XactLockTableInsert(s->transactionIdData);
956 * initialize current transaction state fields
959 s->commandId = FirstCommandId;
960 s->scanCommandId = FirstCommandId;
961 s->startTime = GetCurrentAbsoluteTime();
964 * initialize the various transaction subsystems
972 * Tell the trigger manager to we're starting a transaction
975 DeferredTriggerBeginXact();
978 * done with start processing, set current transaction
979 * state to "in progress"
982 s->state = TRANS_INPROGRESS;
988 * Tell me if we are currently in progress
992 CurrentXactInProgress(void)
994 return CurrentTransactionState->state == TRANS_INPROGRESS;
998 /* --------------------------------
1001 * --------------------------------
1004 CommitTransaction(void)
1006 TransactionState s = CurrentTransactionState;
1009 * check the current transaction state
1012 if (s->state == TRANS_DISABLED)
1015 if (s->state != TRANS_INPROGRESS)
1016 elog(NOTICE, "CommitTransaction and not in in-progress state ");
1018 /* Prevent cancel/die interrupt while cleaning up */
1022 * Tell the trigger manager that this transaction is about to be
1023 * committed. He'll invoke all trigger deferred until XACT before
1024 * we really start on committing the transaction.
1027 DeferredTriggerEndXact();
1030 * set the current transaction state information
1031 * appropriately during the abort processing
1034 s->state = TRANS_COMMIT;
1037 * do commit processing
1041 /* handle commit for large objects [ PA, 7/17/98 ] */
1044 /* NOTIFY commit must also come before lower-level cleanup */
1049 RecordTransactionCommit();
1052 * Let others know about no transaction in progress by me. Note that
1053 * this must be done _before_ releasing locks we hold and
1054 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1055 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1056 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1057 * xid 0 as running as well or it will see two tuple versions - one
1058 * deleted by xid 1 and one inserted by xid 0.
1060 if (MyProc != (PROC *) NULL)
1062 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1063 SpinAcquire(SInvalLock);
1064 MyProc->xid = InvalidTransactionId;
1065 MyProc->xmin = InvalidTransactionId;
1066 SpinRelease(SInvalLock);
1069 RelationPurgeLocalRelation(true);
1070 AtEOXact_temp_relations(true);
1071 smgrDoPendingDeletes(true);
1077 AtEOXact_CatCache(true);
1081 SharedBufferChanged = false; /* safest place to do it */
1084 * done with commit processing, set current transaction
1085 * state back to default
1088 s->state = TRANS_DEFAULT;
1090 RESUME_INTERRUPTS();
1093 /* --------------------------------
1096 * --------------------------------
1099 AbortTransaction(void)
1101 TransactionState s = CurrentTransactionState;
1103 /* Prevent cancel/die interrupt while cleaning up */
1107 * Let others to know about no transaction in progress - vadim
1110 if (MyProc != (PROC *) NULL)
1112 MyProc->xid = InvalidTransactionId;
1113 MyProc->xmin = InvalidTransactionId;
1117 * Release any spinlocks or buffer context locks we might be holding
1118 * as quickly as possible. (Real locks, however, must be held till
1119 * we finish aborting.) Releasing spinlocks is critical since we
1120 * might try to grab them again while cleaning up!
1122 ProcReleaseSpins(NULL);
1125 * Also clean up any open wait for lock, since the lock manager
1126 * will choke if we try to wait for another lock before doing this.
1131 * check the current transaction state
1134 if (s->state == TRANS_DISABLED)
1136 RESUME_INTERRUPTS();
1140 if (s->state != TRANS_INPROGRESS)
1141 elog(NOTICE, "AbortTransaction and not in in-progress state");
1144 * set the current transaction state information
1145 * appropriately during the abort processing
1148 s->state = TRANS_ABORT;
1151 * Reset user id which might have been changed transiently
1153 SetUserId(GetSessionUserId());
1156 * do abort processing
1159 DeferredTriggerAbortXact();
1160 lo_commit(false); /* 'false' means it's abort */
1164 RecordTransactionAbort();
1166 RelationPurgeLocalRelation(false);
1167 AtEOXact_temp_relations(false);
1168 smgrDoPendingDeletes(false);
1173 AtEOXact_CatCache(false);
1177 /* Here we'll rollback xaction changes */
1178 MyLastRecPtr.xrecoff = 0;
1182 SharedBufferChanged = false; /* safest place to do it */
1185 * State remains TRANS_ABORT until CleanupTransaction().
1188 RESUME_INTERRUPTS();
1191 /* --------------------------------
1192 * CleanupTransaction
1194 * --------------------------------
1197 CleanupTransaction(void)
1199 TransactionState s = CurrentTransactionState;
1201 if (s->state == TRANS_DISABLED)
1205 * State should still be TRANS_ABORT from AbortTransaction().
1208 if (s->state != TRANS_ABORT)
1209 elog(FATAL, "CleanupTransaction and not in abort state");
1212 * do abort cleanup processing
1218 * done with abort processing, set current transaction
1219 * state back to default
1222 s->state = TRANS_DEFAULT;
1225 /* --------------------------------
1226 * StartTransactionCommand
1227 * --------------------------------
1230 StartTransactionCommand(void)
1232 TransactionState s = CurrentTransactionState;
1234 switch (s->blockState)
1237 * if we aren't in a transaction block, we
1238 * just do our usual start transaction.
1241 case TBLOCK_DEFAULT:
1246 * We should never experience this -- if we do it
1247 * means the BEGIN state was not changed in the previous
1248 * CommitTransactionCommand(). If we get it, we print
1249 * a warning and change to the in-progress state.
1253 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1254 s->blockState = TBLOCK_INPROGRESS;
1258 * This is the case when are somewhere in a transaction
1259 * block and about to start a new command. For now we
1260 * do nothing but someday we may do command-local resource
1264 case TBLOCK_INPROGRESS:
1268 * As with BEGIN, we should never experience this
1269 * if we do it means the END state was not changed in the
1270 * previous CommitTransactionCommand(). If we get it, we
1271 * print a warning, commit the transaction, start a new
1272 * transaction and change to the default state.
1276 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1277 s->blockState = TBLOCK_DEFAULT;
1278 CommitTransaction();
1283 * Here we are in the middle of a transaction block but
1284 * one of the commands caused an abort so we do nothing
1285 * but remain in the abort state. Eventually we will get
1286 * to the "END TRANSACTION" which will set things straight.
1293 * This means we somehow aborted and the last call to
1294 * CommitTransactionCommand() didn't clear the state so
1295 * we remain in the ENDABORT state and maybe next time
1296 * we get to CommitTransactionCommand() the state will
1297 * get reset to default.
1300 case TBLOCK_ENDABORT:
1301 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1306 * We must switch to TransactionCommandContext before returning.
1307 * This is already done if we called StartTransaction, otherwise not.
1309 Assert(TransactionCommandContext != NULL);
1310 MemoryContextSwitchTo(TransactionCommandContext);
1313 /* --------------------------------
1314 * CommitTransactionCommand
1315 * --------------------------------
1318 CommitTransactionCommand(void)
1320 TransactionState s = CurrentTransactionState;
1322 switch (s->blockState)
1325 * if we aren't in a transaction block, we
1326 * just do our usual transaction commit
1329 case TBLOCK_DEFAULT:
1330 CommitTransaction();
1334 * This is the case right after we get a "BEGIN TRANSACTION"
1335 * command, but the user hasn't done anything else yet, so
1336 * we change to the "transaction block in progress" state
1341 s->blockState = TBLOCK_INPROGRESS;
1345 * This is the case when we have finished executing a command
1346 * someplace within a transaction block. We increment the
1347 * command counter and return. Someday we may free resources
1348 * local to the command.
1350 * That someday is today, at least for memory allocated in
1351 * TransactionCommandContext.
1355 case TBLOCK_INPROGRESS:
1356 CommandCounterIncrement();
1357 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1361 * This is the case when we just got the "END TRANSACTION"
1362 * statement, so we commit the transaction and go back to
1363 * the default state.
1367 CommitTransaction();
1368 s->blockState = TBLOCK_DEFAULT;
1372 * Here we are in the middle of a transaction block but
1373 * one of the commands caused an abort so we do nothing
1374 * but remain in the abort state. Eventually we will get
1375 * to the "END TRANSACTION" which will set things straight.
1382 * Here we were in an aborted transaction block which
1383 * just processed the "END TRANSACTION" command from the
1384 * user, so clean up and return to the default state.
1387 case TBLOCK_ENDABORT:
1388 CleanupTransaction();
1389 s->blockState = TBLOCK_DEFAULT;
1394 /* --------------------------------
1395 * AbortCurrentTransaction
1396 * --------------------------------
1399 AbortCurrentTransaction(void)
1401 TransactionState s = CurrentTransactionState;
1403 switch (s->blockState)
1406 * if we aren't in a transaction block, we
1407 * just do the basic abort & cleanup transaction.
1410 case TBLOCK_DEFAULT:
1412 CleanupTransaction();
1416 * If we are in the TBLOCK_BEGIN it means something
1417 * screwed up right after reading "BEGIN TRANSACTION"
1418 * so we enter the abort state. Eventually an "END
1419 * TRANSACTION" will fix things.
1423 s->blockState = TBLOCK_ABORT;
1425 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1429 * This is the case when are somewhere in a transaction
1430 * block which aborted so we abort the transaction and
1431 * set the ABORT state. Eventually an "END TRANSACTION"
1432 * will fix things and restore us to a normal state.
1435 case TBLOCK_INPROGRESS:
1436 s->blockState = TBLOCK_ABORT;
1438 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1442 * Here, the system was fouled up just after the
1443 * user wanted to end the transaction block so we
1444 * abort the transaction and put us back into the
1449 s->blockState = TBLOCK_DEFAULT;
1451 CleanupTransaction();
1455 * Here, we are already in an aborted transaction
1456 * state and are waiting for an "END TRANSACTION" to
1457 * come along and lo and behold, we abort again!
1458 * So we just remain in the abort state.
1465 * Here we were in an aborted transaction block which
1466 * just processed the "END TRANSACTION" command but somehow
1467 * aborted again.. since we must have done the abort
1468 * processing, we clean up and return to the default state.
1471 case TBLOCK_ENDABORT:
1472 CleanupTransaction();
1473 s->blockState = TBLOCK_DEFAULT;
1478 /* ----------------------------------------------------------------
1479 * transaction block support
1480 * ----------------------------------------------------------------
1482 /* --------------------------------
1483 * BeginTransactionBlock
1484 * --------------------------------
1487 BeginTransactionBlock(void)
1489 TransactionState s = CurrentTransactionState;
1492 * check the current transaction state
1495 if (s->state == TRANS_DISABLED)
1498 if (s->blockState != TBLOCK_DEFAULT)
1499 elog(NOTICE, "BEGIN: already a transaction in progress");
1502 * set the current transaction block state information
1503 * appropriately during begin processing
1506 s->blockState = TBLOCK_BEGIN;
1509 * do begin processing
1514 * done with begin processing, set block state to inprogress
1517 s->blockState = TBLOCK_INPROGRESS;
1520 /* --------------------------------
1521 * EndTransactionBlock
1522 * --------------------------------
1525 EndTransactionBlock(void)
1527 TransactionState s = CurrentTransactionState;
1530 * check the current transaction state
1533 if (s->state == TRANS_DISABLED)
1536 if (s->blockState == TBLOCK_INPROGRESS)
1539 * here we are in a transaction block which should commit
1540 * when we get to the upcoming CommitTransactionCommand()
1541 * so we set the state to "END". CommitTransactionCommand()
1542 * will recognize this and commit the transaction and return
1543 * us to the default state
1546 s->blockState = TBLOCK_END;
1550 if (s->blockState == TBLOCK_ABORT)
1553 * here, we are in a transaction block which aborted
1554 * and since the AbortTransaction() was already done,
1555 * we do whatever is needed and change to the special
1556 * "END ABORT" state. The upcoming CommitTransactionCommand()
1557 * will recognise this and then put us back in the default
1561 s->blockState = TBLOCK_ENDABORT;
1566 * here, the user issued COMMIT when not inside a transaction.
1567 * Issue a notice and go to abort state. The upcoming call to
1568 * CommitTransactionCommand() will then put us back into the
1572 elog(NOTICE, "COMMIT: no transaction in progress");
1574 s->blockState = TBLOCK_ENDABORT;
1577 /* --------------------------------
1578 * AbortTransactionBlock
1579 * --------------------------------
1583 AbortTransactionBlock(void)
1585 TransactionState s = CurrentTransactionState;
1588 * check the current transaction state
1591 if (s->state == TRANS_DISABLED)
1594 if (s->blockState == TBLOCK_INPROGRESS)
1597 * here we were inside a transaction block something
1598 * screwed up inside the system so we enter the abort state,
1599 * do the abort processing and then return.
1600 * We remain in the abort state until we see an
1601 * END TRANSACTION command.
1604 s->blockState = TBLOCK_ABORT;
1610 * here, the user issued ABORT when not inside a transaction.
1611 * Issue a notice and go to abort state. The upcoming call to
1612 * CommitTransactionCommand() will then put us back into the
1616 elog(NOTICE, "ROLLBACK: no transaction in progress");
1618 s->blockState = TBLOCK_ENDABORT;
1623 /* --------------------------------
1624 * UserAbortTransactionBlock
1625 * --------------------------------
1628 UserAbortTransactionBlock(void)
1630 TransactionState s = CurrentTransactionState;
1633 * check the current transaction state
1636 if (s->state == TRANS_DISABLED)
1640 * if the transaction has already been automatically aborted with an
1641 * error, and the user subsequently types 'abort', allow it. (the
1642 * behavior is the same as if they had typed 'end'.)
1644 if (s->blockState == TBLOCK_ABORT)
1646 s->blockState = TBLOCK_ENDABORT;
1650 if (s->blockState == TBLOCK_INPROGRESS)
1653 * here we were inside a transaction block and we
1654 * got an abort command from the user, so we move to
1655 * the abort state, do the abort processing and
1656 * then change to the ENDABORT state so we will end up
1657 * in the default state after the upcoming
1658 * CommitTransactionCommand().
1661 s->blockState = TBLOCK_ABORT;
1663 s->blockState = TBLOCK_ENDABORT;
1668 * here, the user issued ABORT when not inside a transaction.
1669 * Issue a notice and go to abort state. The upcoming call to
1670 * CommitTransactionCommand() will then put us back into the
1674 elog(NOTICE, "ROLLBACK: no transaction in progress");
1676 s->blockState = TBLOCK_ENDABORT;
1679 /* --------------------------------
1680 * AbortOutOfAnyTransaction
1682 * This routine is provided for error recovery purposes. It aborts any
1683 * active transaction or transaction block, leaving the system in a known
1685 * --------------------------------
1688 AbortOutOfAnyTransaction(void)
1690 TransactionState s = CurrentTransactionState;
1693 * Get out of any low-level transaction
1698 case TRANS_INPROGRESS:
1700 /* In a transaction, so clean up */
1702 CleanupTransaction();
1705 /* AbortTransaction already done, still need Cleanup */
1706 CleanupTransaction();
1709 case TRANS_DISABLED:
1710 /* Not in a transaction, do nothing */
1715 * Now reset the high-level state
1717 s->blockState = TBLOCK_DEFAULT;
1721 IsTransactionBlock(void)
1723 TransactionState s = CurrentTransactionState;
1725 if (s->blockState == TBLOCK_INPROGRESS
1726 || s->blockState == TBLOCK_ABORT
1727 || s->blockState == TBLOCK_ENDABORT)
1734 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1736 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1738 if (info == XLOG_XACT_COMMIT)
1740 TransactionIdCommit(record->xl_xid);
1741 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1743 else if (info == XLOG_XACT_ABORT)
1745 TransactionIdAbort(record->xl_xid);
1748 elog(STOP, "xact_redo: unknown op code %u", info);
1752 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1754 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1756 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1757 elog(STOP, "xact_undo: can't undo committed xaction");
1758 else if (info != XLOG_XACT_ABORT)
1759 elog(STOP, "xact_redo: unknown op code %u", info);
1763 xact_desc(char *buf, uint8 xl_info, char* rec)
1765 uint8 info = xl_info & ~XLR_INFO_MASK;
1767 if (info == XLOG_XACT_COMMIT)
1769 xl_xact_commit *xlrec = (xl_xact_commit*) rec;
1770 struct tm *tm = localtime(&xlrec->xtime);
1772 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1773 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1774 tm->tm_hour, tm->tm_min, tm->tm_sec);
1776 else if (info == XLOG_XACT_ABORT)
1778 xl_xact_abort *xlrec = (xl_xact_abort*) rec;
1779 struct tm *tm = localtime(&xlrec->xtime);
1781 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1782 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1783 tm->tm_hour, tm->tm_min, tm->tm_sec);
1786 strcat(buf, "UNKNOWN");
1790 XactPushRollback(void (*func) (void *), void* data)
1793 if (_RollbackFunc != NULL)
1794 elog(STOP, "XactPushRollback: already installed");
1797 _RollbackFunc = func;
1798 _RollbackData = data;
1802 XactPopRollback(void)
1804 _RollbackFunc = NULL;