1 /*-------------------------------------------------------------------------
4 * top level transaction system support routines
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.91 2000/12/28 13:00:08 vadim 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;
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;
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 ");
1019 * Tell the trigger manager that this transaction is about to be
1020 * committed. He'll invoke all trigger deferred until XACT before
1021 * we really start on committing the transaction.
1024 DeferredTriggerEndXact();
1027 * set the current transaction state information
1028 * appropriately during the abort processing
1031 s->state = TRANS_COMMIT;
1034 * do commit processing
1038 /* handle commit for large objects [ PA, 7/17/98 ] */
1041 /* NOTIFY commit must also come before lower-level cleanup */
1046 RecordTransactionCommit();
1049 * Let others know about no transaction in progress by me. Note that
1050 * this must be done _before_ releasing locks we hold and
1051 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1052 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1053 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1054 * xid 0 as running as well or it will see two tuple versions - one
1055 * deleted by xid 1 and one inserted by xid 0.
1057 if (MyProc != (PROC *) NULL)
1059 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1060 SpinAcquire(SInvalLock);
1061 MyProc->xid = InvalidTransactionId;
1062 MyProc->xmin = InvalidTransactionId;
1063 SpinRelease(SInvalLock);
1066 RelationPurgeLocalRelation(true);
1067 AtEOXact_temp_relations(true);
1068 smgrDoPendingDeletes(true);
1074 AtEOXact_CatCache(true);
1078 SharedBufferChanged = false; /* safest place to do it */
1081 * done with commit processing, set current transaction
1082 * state back to default
1085 s->state = TRANS_DEFAULT;
1088 /* --------------------------------
1091 * --------------------------------
1094 AbortTransaction(void)
1096 TransactionState s = CurrentTransactionState;
1099 * Let others to know about no transaction in progress - vadim
1102 if (MyProc != (PROC *) NULL)
1104 MyProc->xid = InvalidTransactionId;
1105 MyProc->xmin = InvalidTransactionId;
1109 * Release any spinlocks or buffer context locks we might be holding
1110 * as quickly as possible. (Real locks, however, must be held till
1111 * we finish aborting.) Releasing spinlocks is critical since we
1112 * might try to grab them again while cleaning up!
1114 ProcReleaseSpins(NULL);
1118 * check the current transaction state
1121 if (s->state == TRANS_DISABLED)
1124 if (s->state != TRANS_INPROGRESS)
1125 elog(NOTICE, "AbortTransaction and not in in-progress state");
1128 * set the current transaction state information
1129 * appropriately during the abort processing
1132 s->state = TRANS_ABORT;
1135 * Reset user id which might have been changed transiently
1137 SetUserId(GetSessionUserId());
1140 * do abort processing
1143 DeferredTriggerAbortXact();
1144 lo_commit(false); /* 'false' means it's abort */
1148 RecordTransactionAbort();
1150 RelationPurgeLocalRelation(false);
1151 AtEOXact_temp_relations(false);
1152 smgrDoPendingDeletes(false);
1157 AtEOXact_CatCache(false);
1161 /* Here we'll rollback xaction changes */
1162 MyLastRecPtr.xrecoff = 0;
1166 SharedBufferChanged = false; /* safest place to do it */
1169 * State remains TRANS_ABORT until CleanupTransaction().
1174 /* --------------------------------
1175 * CleanupTransaction
1177 * --------------------------------
1180 CleanupTransaction(void)
1182 TransactionState s = CurrentTransactionState;
1184 if (s->state == TRANS_DISABLED)
1188 * State should still be TRANS_ABORT from AbortTransaction().
1191 if (s->state != TRANS_ABORT)
1192 elog(FATAL, "CleanupTransaction and not in abort state");
1195 * do abort cleanup processing
1201 * done with abort processing, set current transaction
1202 * state back to default
1205 s->state = TRANS_DEFAULT;
1208 /* --------------------------------
1209 * StartTransactionCommand
1210 * --------------------------------
1213 StartTransactionCommand(void)
1215 TransactionState s = CurrentTransactionState;
1217 switch (s->blockState)
1220 * if we aren't in a transaction block, we
1221 * just do our usual start transaction.
1224 case TBLOCK_DEFAULT:
1229 * We should never experience this -- if we do it
1230 * means the BEGIN state was not changed in the previous
1231 * CommitTransactionCommand(). If we get it, we print
1232 * a warning and change to the in-progress state.
1236 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1237 s->blockState = TBLOCK_INPROGRESS;
1241 * This is the case when are somewhere in a transaction
1242 * block and about to start a new command. For now we
1243 * do nothing but someday we may do command-local resource
1247 case TBLOCK_INPROGRESS:
1251 * As with BEGIN, we should never experience this
1252 * if we do it means the END state was not changed in the
1253 * previous CommitTransactionCommand(). If we get it, we
1254 * print a warning, commit the transaction, start a new
1255 * transaction and change to the default state.
1259 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1260 s->blockState = TBLOCK_DEFAULT;
1261 CommitTransaction();
1266 * Here we are in the middle of a transaction block but
1267 * one of the commands caused an abort so we do nothing
1268 * but remain in the abort state. Eventually we will get
1269 * to the "END TRANSACTION" which will set things straight.
1276 * This means we somehow aborted and the last call to
1277 * CommitTransactionCommand() didn't clear the state so
1278 * we remain in the ENDABORT state and maybe next time
1279 * we get to CommitTransactionCommand() the state will
1280 * get reset to default.
1283 case TBLOCK_ENDABORT:
1284 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1289 * We must switch to TransactionCommandContext before returning.
1290 * This is already done if we called StartTransaction, otherwise not.
1292 Assert(TransactionCommandContext != NULL);
1293 MemoryContextSwitchTo(TransactionCommandContext);
1296 /* --------------------------------
1297 * CommitTransactionCommand
1298 * --------------------------------
1301 CommitTransactionCommand(void)
1303 TransactionState s = CurrentTransactionState;
1305 switch (s->blockState)
1308 * if we aren't in a transaction block, we
1309 * just do our usual transaction commit
1312 case TBLOCK_DEFAULT:
1313 CommitTransaction();
1317 * This is the case right after we get a "BEGIN TRANSACTION"
1318 * command, but the user hasn't done anything else yet, so
1319 * we change to the "transaction block in progress" state
1324 s->blockState = TBLOCK_INPROGRESS;
1328 * This is the case when we have finished executing a command
1329 * someplace within a transaction block. We increment the
1330 * command counter and return. Someday we may free resources
1331 * local to the command.
1333 * That someday is today, at least for memory allocated in
1334 * TransactionCommandContext.
1338 case TBLOCK_INPROGRESS:
1339 CommandCounterIncrement();
1340 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1344 * This is the case when we just got the "END TRANSACTION"
1345 * statement, so we commit the transaction and go back to
1346 * the default state.
1350 CommitTransaction();
1351 s->blockState = TBLOCK_DEFAULT;
1355 * Here we are in the middle of a transaction block but
1356 * one of the commands caused an abort so we do nothing
1357 * but remain in the abort state. Eventually we will get
1358 * to the "END TRANSACTION" which will set things straight.
1365 * Here we were in an aborted transaction block which
1366 * just processed the "END TRANSACTION" command from the
1367 * user, so clean up and return to the default state.
1370 case TBLOCK_ENDABORT:
1371 CleanupTransaction();
1372 s->blockState = TBLOCK_DEFAULT;
1377 /* --------------------------------
1378 * AbortCurrentTransaction
1379 * --------------------------------
1382 AbortCurrentTransaction(void)
1384 TransactionState s = CurrentTransactionState;
1386 switch (s->blockState)
1389 * if we aren't in a transaction block, we
1390 * just do the basic abort & cleanup transaction.
1393 case TBLOCK_DEFAULT:
1395 CleanupTransaction();
1399 * If we are in the TBLOCK_BEGIN it means something
1400 * screwed up right after reading "BEGIN TRANSACTION"
1401 * so we enter the abort state. Eventually an "END
1402 * TRANSACTION" will fix things.
1406 s->blockState = TBLOCK_ABORT;
1408 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1412 * This is the case when are somewhere in a transaction
1413 * block which aborted so we abort the transaction and
1414 * set the ABORT state. Eventually an "END TRANSACTION"
1415 * will fix things and restore us to a normal state.
1418 case TBLOCK_INPROGRESS:
1419 s->blockState = TBLOCK_ABORT;
1421 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1425 * Here, the system was fouled up just after the
1426 * user wanted to end the transaction block so we
1427 * abort the transaction and put us back into the
1432 s->blockState = TBLOCK_DEFAULT;
1434 CleanupTransaction();
1438 * Here, we are already in an aborted transaction
1439 * state and are waiting for an "END TRANSACTION" to
1440 * come along and lo and behold, we abort again!
1441 * So we just remain in the abort state.
1448 * Here we were in an aborted transaction block which
1449 * just processed the "END TRANSACTION" command but somehow
1450 * aborted again.. since we must have done the abort
1451 * processing, we clean up and return to the default state.
1454 case TBLOCK_ENDABORT:
1455 CleanupTransaction();
1456 s->blockState = TBLOCK_DEFAULT;
1461 /* ----------------------------------------------------------------
1462 * transaction block support
1463 * ----------------------------------------------------------------
1465 /* --------------------------------
1466 * BeginTransactionBlock
1467 * --------------------------------
1470 BeginTransactionBlock(void)
1472 TransactionState s = CurrentTransactionState;
1475 * check the current transaction state
1478 if (s->state == TRANS_DISABLED)
1481 if (s->blockState != TBLOCK_DEFAULT)
1482 elog(NOTICE, "BEGIN: already a transaction in progress");
1485 * set the current transaction block state information
1486 * appropriately during begin processing
1489 s->blockState = TBLOCK_BEGIN;
1492 * do begin processing
1497 * done with begin processing, set block state to inprogress
1500 s->blockState = TBLOCK_INPROGRESS;
1503 /* --------------------------------
1504 * EndTransactionBlock
1505 * --------------------------------
1508 EndTransactionBlock(void)
1510 TransactionState s = CurrentTransactionState;
1513 * check the current transaction state
1516 if (s->state == TRANS_DISABLED)
1519 if (s->blockState == TBLOCK_INPROGRESS)
1522 * here we are in a transaction block which should commit
1523 * when we get to the upcoming CommitTransactionCommand()
1524 * so we set the state to "END". CommitTransactionCommand()
1525 * will recognize this and commit the transaction and return
1526 * us to the default state
1529 s->blockState = TBLOCK_END;
1533 if (s->blockState == TBLOCK_ABORT)
1536 * here, we are in a transaction block which aborted
1537 * and since the AbortTransaction() was already done,
1538 * we do whatever is needed and change to the special
1539 * "END ABORT" state. The upcoming CommitTransactionCommand()
1540 * will recognise this and then put us back in the default
1544 s->blockState = TBLOCK_ENDABORT;
1549 * here, the user issued COMMIT when not inside a transaction.
1550 * Issue a notice and go to abort state. The upcoming call to
1551 * CommitTransactionCommand() will then put us back into the
1555 elog(NOTICE, "COMMIT: no transaction in progress");
1557 s->blockState = TBLOCK_ENDABORT;
1560 /* --------------------------------
1561 * AbortTransactionBlock
1562 * --------------------------------
1566 AbortTransactionBlock(void)
1568 TransactionState s = CurrentTransactionState;
1571 * check the current transaction state
1574 if (s->state == TRANS_DISABLED)
1577 if (s->blockState == TBLOCK_INPROGRESS)
1580 * here we were inside a transaction block something
1581 * screwed up inside the system so we enter the abort state,
1582 * do the abort processing and then return.
1583 * We remain in the abort state until we see an
1584 * END TRANSACTION command.
1587 s->blockState = TBLOCK_ABORT;
1593 * here, the user issued ABORT when not inside a transaction.
1594 * Issue a notice and go to abort state. The upcoming call to
1595 * CommitTransactionCommand() will then put us back into the
1599 elog(NOTICE, "ROLLBACK: no transaction in progress");
1601 s->blockState = TBLOCK_ENDABORT;
1606 /* --------------------------------
1607 * UserAbortTransactionBlock
1608 * --------------------------------
1611 UserAbortTransactionBlock(void)
1613 TransactionState s = CurrentTransactionState;
1616 * check the current transaction state
1619 if (s->state == TRANS_DISABLED)
1623 * if the transaction has already been automatically aborted with an
1624 * error, and the user subsequently types 'abort', allow it. (the
1625 * behavior is the same as if they had typed 'end'.)
1627 if (s->blockState == TBLOCK_ABORT)
1629 s->blockState = TBLOCK_ENDABORT;
1633 if (s->blockState == TBLOCK_INPROGRESS)
1636 * here we were inside a transaction block and we
1637 * got an abort command from the user, so we move to
1638 * the abort state, do the abort processing and
1639 * then change to the ENDABORT state so we will end up
1640 * in the default state after the upcoming
1641 * CommitTransactionCommand().
1644 s->blockState = TBLOCK_ABORT;
1646 s->blockState = TBLOCK_ENDABORT;
1651 * here, the user issued ABORT when not inside a transaction.
1652 * Issue a notice and go to abort state. The upcoming call to
1653 * CommitTransactionCommand() will then put us back into the
1657 elog(NOTICE, "ROLLBACK: no transaction in progress");
1659 s->blockState = TBLOCK_ENDABORT;
1662 /* --------------------------------
1663 * AbortOutOfAnyTransaction
1665 * This routine is provided for error recovery purposes. It aborts any
1666 * active transaction or transaction block, leaving the system in a known
1668 * --------------------------------
1671 AbortOutOfAnyTransaction(void)
1673 TransactionState s = CurrentTransactionState;
1676 * Get out of any low-level transaction
1681 case TRANS_INPROGRESS:
1683 /* In a transaction, so clean up */
1685 CleanupTransaction();
1688 /* AbortTransaction already done, still need Cleanup */
1689 CleanupTransaction();
1692 case TRANS_DISABLED:
1693 /* Not in a transaction, do nothing */
1698 * Now reset the high-level state
1700 s->blockState = TBLOCK_DEFAULT;
1704 IsTransactionBlock(void)
1706 TransactionState s = CurrentTransactionState;
1708 if (s->blockState == TBLOCK_INPROGRESS
1709 || s->blockState == TBLOCK_ABORT
1710 || s->blockState == TBLOCK_ENDABORT)
1717 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1719 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1721 if (info == XLOG_XACT_COMMIT)
1723 xl_xact_commit *xlrec = (xl_xact_commit*) XLogRecGetData(record);
1725 TransactionIdCommit(record->xl_xid);
1726 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1728 else if (info == XLOG_XACT_ABORT)
1730 TransactionIdAbort(record->xl_xid);
1733 elog(STOP, "xact_redo: unknown op code %u", info);
1737 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1739 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1741 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1742 elog(STOP, "xact_undo: can't undo committed xaction");
1743 else if (info != XLOG_XACT_ABORT)
1744 elog(STOP, "xact_redo: unknown op code %u", info);
1748 xact_desc(char *buf, uint8 xl_info, char* rec)
1750 uint8 info = xl_info & ~XLR_INFO_MASK;
1752 if (info == XLOG_XACT_COMMIT)
1754 xl_xact_commit *xlrec = (xl_xact_commit*) rec;
1755 struct tm *tm = localtime(&xlrec->xtime);
1757 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1758 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1759 tm->tm_hour, tm->tm_min, tm->tm_sec);
1761 else if (info == XLOG_XACT_ABORT)
1763 xl_xact_abort *xlrec = (xl_xact_abort*) rec;
1764 struct tm *tm = localtime(&xlrec->xtime);
1766 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1767 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1768 tm->tm_hour, tm->tm_min, tm->tm_sec);
1771 strcat(buf, "UNKNOWN");
1775 XactPushRollback(void (*func) (void *), void* data)
1778 if (_RollbackFunc != NULL)
1779 elog(STOP, "XactPushRollback: already installed");
1782 _RollbackFunc = func;
1783 _RollbackData = data;
1787 XactPopRollback(void)
1789 _RollbackFunc = NULL;