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.109 2001/08/25 18:52:41 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/gistscan.h"
160 #include "access/hash.h"
161 #include "access/nbtree.h"
162 #include "access/rtree.h"
163 #include "access/xact.h"
164 #include "catalog/heap.h"
165 #include "catalog/index.h"
166 #include "commands/async.h"
167 #include "commands/sequence.h"
168 #include "commands/trigger.h"
169 #include "executor/spi.h"
170 #include "libpq/be-fsstubs.h"
171 #include "miscadmin.h"
172 #include "storage/proc.h"
173 #include "storage/sinval.h"
174 #include "storage/smgr.h"
175 #include "utils/inval.h"
176 #include "utils/memutils.h"
177 #include "utils/portal.h"
178 #include "utils/catcache.h"
179 #include "utils/relcache.h"
180 #include "utils/temprel.h"
184 extern bool SharedBufferChanged;
186 static void AbortTransaction(void);
187 static void AtAbort_Cache(void);
188 static void AtAbort_Locks(void);
189 static void AtAbort_Memory(void);
190 static void AtCleanup_Memory(void);
191 static void AtCommit_Cache(void);
192 static void AtCommit_LocalCache(void);
193 static void AtCommit_Locks(void);
194 static void AtCommit_Memory(void);
195 static void AtStart_Cache(void);
196 static void AtStart_Locks(void);
197 static void AtStart_Memory(void);
198 static void CleanupTransaction(void);
199 static void CommitTransaction(void);
200 static void RecordTransactionAbort(void);
201 static void StartTransaction(void);
204 * global variables holding the current transaction state.
207 static TransactionStateData CurrentTransactionStateData = {
208 0, /* transaction id */
209 FirstCommandId, /* command id */
210 0, /* scan command id */
211 0x0, /* start time */
212 TRANS_DEFAULT, /* transaction state */
213 TBLOCK_DEFAULT /* transaction block state */
216 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
219 * User-tweakable parameters
221 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
224 int CommitDelay = 0; /* precommit delay in microseconds */
225 int CommitSiblings = 5; /* number of concurrent xacts needed to
228 static void (*_RollbackFunc) (void *) = NULL;
229 static void *_RollbackData = NULL;
232 * catalog creation transaction bootstrapping flag.
233 * This should be eliminated and added to the transaction
234 * state stuff. -cim 3/19/90
237 bool AMI_OVERRIDE = false;
239 /* ----------------------------------------------------------------
240 * transaction state accessors
241 * ----------------------------------------------------------------
244 /* --------------------------------
245 * TranactionFlushEnabled()
246 * SetTransactionFlushEnabled()
248 * These are used to test and set the "TransactionFlushState"
249 * varable. If this variable is true (the default), then
250 * the system will flush all dirty buffers to disk at the end
251 * of each transaction. If false then we are assuming the
252 * buffer pool resides in stable main memory, in which case we
253 * only do writes as necessary.
254 * --------------------------------
256 static int TransactionFlushState = 1;
259 TransactionFlushEnabled(void)
261 return TransactionFlushState;
266 SetTransactionFlushEnabled(bool state)
268 TransactionFlushState = (state == true);
272 /* --------------------------------
275 * This returns true if we are currently running a query
276 * within an executing transaction.
277 * --------------------------------
280 IsTransactionState(void)
282 TransactionState s = CurrentTransactionState;
290 case TRANS_INPROGRESS:
299 * Shouldn't get here, but lint is not happy with this...
306 /* --------------------------------
307 * IsAbortedTransactionBlockState
309 * This returns true if we are currently running a query
310 * within an aborted transaction block.
311 * --------------------------------
314 IsAbortedTransactionBlockState(void)
316 TransactionState s = CurrentTransactionState;
318 if (s->blockState == TBLOCK_ABORT)
325 /* --------------------------------
326 * GetCurrentTransactionId
327 * --------------------------------
330 GetCurrentTransactionId(void)
332 TransactionState s = CurrentTransactionState;
334 return s->transactionIdData;
338 /* --------------------------------
339 * GetCurrentCommandId
340 * --------------------------------
343 GetCurrentCommandId(void)
345 TransactionState s = CurrentTransactionState;
351 GetScanCommandId(void)
353 TransactionState s = CurrentTransactionState;
355 return s->scanCommandId;
359 /* --------------------------------
360 * GetCurrentTransactionStartTime
361 * --------------------------------
364 GetCurrentTransactionStartTime(void)
366 TransactionState s = CurrentTransactionState;
372 /* --------------------------------
373 * TransactionIdIsCurrentTransactionId
374 * --------------------------------
377 TransactionIdIsCurrentTransactionId(TransactionId xid)
379 TransactionState s = CurrentTransactionState;
384 return TransactionIdEquals(xid, s->transactionIdData);
388 /* --------------------------------
389 * CommandIdIsCurrentCommandId
390 * --------------------------------
393 CommandIdIsCurrentCommandId(CommandId cid)
395 TransactionState s = CurrentTransactionState;
400 return (cid == s->commandId) ? true : false;
404 CommandIdGEScanCommandId(CommandId cid)
406 TransactionState s = CurrentTransactionState;
411 return (cid >= s->scanCommandId) ? true : false;
415 /* --------------------------------
416 * CommandCounterIncrement
417 * --------------------------------
420 CommandCounterIncrement(void)
422 CurrentTransactionStateData.commandId += 1;
423 if (CurrentTransactionStateData.commandId == FirstCommandId)
424 elog(ERROR, "You may only have 2^32-1 commands per transaction");
426 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
429 * make cache changes visible to me. AtCommit_LocalCache() instead of
430 * AtCommit_Cache() is called here.
432 AtCommit_LocalCache();
437 SetScanCommandId(CommandId savedId)
439 CurrentTransactionStateData.scanCommandId = savedId;
442 /* ----------------------------------------------------------------
443 * StartTransaction stuff
444 * ----------------------------------------------------------------
447 /* --------------------------------
449 * --------------------------------
454 AcceptInvalidationMessages();
457 /* --------------------------------
459 * --------------------------------
466 * at present, it is unknown to me what belongs here -cim 3/18/90
468 * There isn't anything to do at the start of a xact for locks. -mer
473 /* --------------------------------
475 * --------------------------------
482 * We shouldn't have any transaction contexts already.
484 Assert(TopTransactionContext == NULL);
485 Assert(TransactionCommandContext == NULL);
488 * Create a toplevel context for the transaction.
490 TopTransactionContext =
491 AllocSetContextCreate(TopMemoryContext,
492 "TopTransactionContext",
493 ALLOCSET_DEFAULT_MINSIZE,
494 ALLOCSET_DEFAULT_INITSIZE,
495 ALLOCSET_DEFAULT_MAXSIZE);
498 * Create a statement-level context and make it active.
500 TransactionCommandContext =
501 AllocSetContextCreate(TopTransactionContext,
502 "TransactionCommandContext",
503 ALLOCSET_DEFAULT_MINSIZE,
504 ALLOCSET_DEFAULT_INITSIZE,
505 ALLOCSET_DEFAULT_MAXSIZE);
506 MemoryContextSwitchTo(TransactionCommandContext);
510 /* ----------------------------------------------------------------
511 * CommitTransaction stuff
512 * ----------------------------------------------------------------
515 /* --------------------------------
516 * RecordTransactionCommit
518 * Note: the two calls to BufferManagerFlush() exist to ensure
519 * that data pages are written before log pages. These
520 * explicit calls should be replaced by a more efficient
521 * ordered page write scheme in the buffer manager
523 * --------------------------------
526 RecordTransactionCommit(void)
531 leak = BufferPoolCheckLeak();
533 xid = GetCurrentTransactionId();
536 * We needn't write anything in xlog or clog if the transaction was
537 * read-only, which we check by testing if it made any xlog entries.
539 if (MyLastRecPtr.xrecoff != 0)
542 xl_xact_commit xlrec;
547 xlrec.xtime = time(NULL);
548 rdata.buffer = InvalidBuffer;
549 rdata.data = (char *) (&xlrec);
550 rdata.len = SizeOfXactCommit;
553 START_CRIT_SECTION();
556 * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
558 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
561 * Sleep before commit! So we can flush more than one commit
562 * records per single fsync. (The idea is some other backend may
563 * do the XLogFlush while we're sleeping. This needs work still,
564 * because on most Unixen, the minimum select() delay is 10msec or
565 * more, which is way too long.)
567 * We do not sleep if enableFsync is not turned on, nor if there are
568 * fewer than CommitSiblings other backends with active
571 if (CommitDelay > 0 && enableFsync &&
572 CountActiveBackends() >= CommitSiblings)
574 struct timeval delay;
577 delay.tv_usec = CommitDelay;
578 (void) select(0, NULL, NULL, NULL, &delay);
583 /* Break the chain of back-links in the XLOG records I output */
584 MyLastRecPtr.xrecoff = 0;
586 /* Mark the transaction committed in clog */
587 TransactionIdCommit(xid);
592 /* Show myself as out of the transaction in PROC array */
593 MyProc->logRec.xrecoff = 0;
596 ResetBufferPool(true);
600 /* --------------------------------
602 * --------------------------------
608 * Make catalog changes visible to all backends.
610 AtEOXactInvalidationMessages(true);
613 /* --------------------------------
614 * AtCommit_LocalCache
615 * --------------------------------
618 AtCommit_LocalCache(void)
621 * Make catalog changes visible to me for the next command.
623 CommandEndInvalidationMessages(true);
626 /* --------------------------------
628 * --------------------------------
634 * XXX What if ProcReleaseLocks fails? (race condition?)
636 * Then you're up a creek! -mer 5/24/92
638 ProcReleaseLocks(true);
641 /* --------------------------------
643 * --------------------------------
646 AtCommit_Memory(void)
649 * Now that we're "out" of a transaction, have the system allocate
650 * things in the top memory context instead of per-transaction
653 MemoryContextSwitchTo(TopMemoryContext);
656 * Release all transaction-local memory.
658 Assert(TopTransactionContext != NULL);
659 MemoryContextDelete(TopTransactionContext);
660 TopTransactionContext = NULL;
661 TransactionCommandContext = NULL;
664 /* ----------------------------------------------------------------
665 * AbortTransaction stuff
666 * ----------------------------------------------------------------
669 /* --------------------------------
670 * RecordTransactionAbort
671 * --------------------------------
674 RecordTransactionAbort(void)
676 TransactionId xid = GetCurrentTransactionId();
679 * We needn't write anything in xlog or clog if the transaction was
680 * read-only, which we check by testing if it made any xlog entries.
682 * Extra check here is to catch case that we aborted partway through
683 * RecordTransactionCommit ...
685 if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
691 xlrec.xtime = time(NULL);
692 rdata.buffer = InvalidBuffer;
693 rdata.data = (char *) (&xlrec);
694 rdata.len = SizeOfXactAbort;
697 START_CRIT_SECTION();
700 * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
702 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
705 * There's no need for XLogFlush here, since the default assumption
706 * would be that we aborted, anyway.
709 /* Mark the transaction aborted in clog */
710 TransactionIdAbort(xid);
715 /* Break the chain of back-links in the XLOG records I output */
716 MyLastRecPtr.xrecoff = 0;
717 /* Show myself as out of the transaction in PROC array */
718 MyProc->logRec.xrecoff = 0;
721 * Tell bufmgr and smgr to release resources.
723 ResetBufferPool(false); /* false -> is abort */
726 /* --------------------------------
728 * --------------------------------
733 RelationCacheAbort();
734 AtEOXactInvalidationMessages(false);
737 /* --------------------------------
739 * --------------------------------
746 * XXX What if ProcReleaseLocks() fails? (race condition?)
748 * Then you're up a creek without a paddle! -mer
750 ProcReleaseLocks(false);
754 /* --------------------------------
756 * --------------------------------
763 * Make sure we are in a valid context (not a child of
764 * TransactionCommandContext...). Note that it is possible for this
765 * code to be called when we aren't in a transaction at all; go
766 * directly to TopMemoryContext in that case.
768 if (TransactionCommandContext != NULL)
770 MemoryContextSwitchTo(TransactionCommandContext);
773 * We do not want to destroy transaction contexts yet, but it
774 * should be OK to delete any command-local memory.
776 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
779 MemoryContextSwitchTo(TopMemoryContext);
783 /* ----------------------------------------------------------------
784 * CleanupTransaction stuff
785 * ----------------------------------------------------------------
788 /* --------------------------------
790 * --------------------------------
793 AtCleanup_Memory(void)
797 * Now that we're "out" of a transaction, have the system allocate
798 * things in the top memory context instead of per-transaction
801 MemoryContextSwitchTo(TopMemoryContext);
804 * Release all transaction-local memory.
806 if (TopTransactionContext != NULL)
807 MemoryContextDelete(TopTransactionContext);
808 TopTransactionContext = NULL;
809 TransactionCommandContext = NULL;
813 /* ----------------------------------------------------------------
815 * ----------------------------------------------------------------
818 /* --------------------------------
821 * --------------------------------
824 StartTransaction(void)
826 TransactionState s = CurrentTransactionState;
829 XactIsoLevel = DefaultXactIsoLevel;
832 * Check the current transaction state. If the transaction system is
833 * switched off, or if we're already in a transaction, do nothing.
834 * We're already in a transaction when the monitor sends a null
835 * command to the backend to flush the comm channel. This is a hacky
836 * fix to a communications problem, and we keep having to deal with it
837 * here. We should fix the comm channel code. mao 080891
839 if (s->state == TRANS_INPROGRESS)
843 * set the current transaction state information appropriately during
846 s->state = TRANS_START;
848 SetReindexProcessing(false);
851 * generate a new transaction id
853 s->transactionIdData = GetNewTransactionId();
855 XactLockTableInsert(s->transactionIdData);
858 * initialize current transaction state fields
860 s->commandId = FirstCommandId;
861 s->scanCommandId = FirstCommandId;
862 s->startTime = GetCurrentAbsoluteTime();
865 * initialize the various transaction subsystems
872 * Tell the trigger manager to we're starting a transaction
874 DeferredTriggerBeginXact();
877 * done with start processing, set current transaction state to "in
880 s->state = TRANS_INPROGRESS;
886 * Tell me if we are currently in progress
890 CurrentXactInProgress(void)
892 return CurrentTransactionState->state == TRANS_INPROGRESS;
897 /* --------------------------------
900 * --------------------------------
903 CommitTransaction(void)
905 TransactionState s = CurrentTransactionState;
908 * check the current transaction state
910 if (s->state != TRANS_INPROGRESS)
911 elog(NOTICE, "CommitTransaction and not in in-progress state");
914 * Tell the trigger manager that this transaction is about to be
915 * committed. He'll invoke all trigger deferred until XACT before we
916 * really start on committing the transaction.
918 DeferredTriggerEndXact();
920 /* Prevent cancel/die interrupt while cleaning up */
924 * set the current transaction state information appropriately during
925 * the abort processing
927 s->state = TRANS_COMMIT;
930 * do commit processing
933 /* handle commit for large objects [ PA, 7/17/98 ] */
936 /* NOTIFY commit must also come before lower-level cleanup */
942 /* Here is where we really truly commit. */
943 RecordTransactionCommit();
946 * Let others know about no transaction in progress by me. Note that
947 * this must be done _before_ releasing locks we hold and _after_
948 * RecordTransactionCommit.
950 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
951 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
952 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
953 * xid 0 as running as well or it will see two tuple versions - one
954 * deleted by xid 1 and one inserted by xid 0. See notes in
957 if (MyProc != (PROC *) NULL)
959 /* Lock SInvalLock because that's what GetSnapshotData uses. */
960 SpinAcquire(SInvalLock);
961 MyProc->xid = InvalidTransactionId;
962 MyProc->xmin = InvalidTransactionId;
963 SpinRelease(SInvalLock);
967 * This is all post-commit cleanup. Note that if an error is raised
968 * here, it's too late to abort the transaction. This should be just
969 * noncritical resource releasing.
972 RelationPurgeLocalRelation(true);
973 AtEOXact_temp_relations(true);
974 smgrDoPendingDeletes(true);
983 AtEOXact_CatCache(true);
987 SharedBufferChanged = false;/* safest place to do it */
989 /* Count transaction commit in statistics collector */
990 pgstat_count_xact_commit();
993 * done with commit processing, set current transaction state back to
996 s->state = TRANS_DEFAULT;
1001 /* --------------------------------
1004 * --------------------------------
1007 AbortTransaction(void)
1009 TransactionState s = CurrentTransactionState;
1011 /* Prevent cancel/die interrupt while cleaning up */
1015 * Release any spinlocks or buffer context locks we might be holding
1016 * as quickly as possible. (Real locks, however, must be held till we
1017 * finish aborting.) Releasing spinlocks is critical since we might
1018 * try to grab them again while cleaning up!
1020 ProcReleaseSpins(NULL);
1024 * Also clean up any open wait for lock, since the lock manager will
1025 * choke if we try to wait for another lock before doing this.
1030 * check the current transaction state
1032 if (s->state != TRANS_INPROGRESS)
1033 elog(NOTICE, "AbortTransaction and not in in-progress state");
1036 * set the current transaction state information appropriately during
1037 * the abort processing
1039 s->state = TRANS_ABORT;
1042 * Reset user id which might have been changed transiently
1044 SetUserId(GetSessionUserId());
1047 * do abort processing
1049 DeferredTriggerAbortXact();
1050 lo_commit(false); /* 'false' means it's abort */
1055 /* Advertise the fact that we aborted in pg_clog. */
1056 RecordTransactionAbort();
1059 * Let others know about no transaction in progress by me. Note that
1060 * this must be done _before_ releasing locks we hold and _after_
1061 * RecordTransactionAbort.
1063 if (MyProc != (PROC *) NULL)
1065 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1066 SpinAcquire(SInvalLock);
1067 MyProc->xid = InvalidTransactionId;
1068 MyProc->xmin = InvalidTransactionId;
1069 SpinRelease(SInvalLock);
1072 RelationPurgeLocalRelation(false);
1073 AtEOXact_temp_relations(false);
1074 smgrDoPendingDeletes(false);
1082 AtEOXact_CatCache(false);
1087 SharedBufferChanged = false;/* safest place to do it */
1089 /* Count transaction abort in statistics collector */
1090 pgstat_count_xact_rollback();
1093 * State remains TRANS_ABORT until CleanupTransaction().
1095 RESUME_INTERRUPTS();
1098 /* --------------------------------
1099 * CleanupTransaction
1101 * --------------------------------
1104 CleanupTransaction(void)
1106 TransactionState s = CurrentTransactionState;
1109 * State should still be TRANS_ABORT from AbortTransaction().
1111 if (s->state != TRANS_ABORT)
1112 elog(FATAL, "CleanupTransaction and not in abort state");
1115 * do abort cleanup processing
1120 * done with abort processing, set current transaction state back to
1123 s->state = TRANS_DEFAULT;
1126 /* --------------------------------
1127 * StartTransactionCommand
1128 * --------------------------------
1131 StartTransactionCommand(void)
1133 TransactionState s = CurrentTransactionState;
1135 switch (s->blockState)
1139 * if we aren't in a transaction block, we just do our usual
1140 * start transaction.
1142 case TBLOCK_DEFAULT:
1147 * We should never experience this -- if we do it means the
1148 * BEGIN state was not changed in the previous
1149 * CommitTransactionCommand(). If we get it, we print a
1150 * warning and change to the in-progress state.
1153 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1154 s->blockState = TBLOCK_INPROGRESS;
1158 * This is the case when are somewhere in a transaction block
1159 * and about to start a new command. For now we do nothing
1160 * but someday we may do command-local resource
1163 case TBLOCK_INPROGRESS:
1167 * As with BEGIN, we should never experience this if we do it
1168 * means the END state was not changed in the previous
1169 * CommitTransactionCommand(). If we get it, we print a
1170 * warning, commit the transaction, start a new transaction
1171 * and change to the default state.
1174 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1175 s->blockState = TBLOCK_DEFAULT;
1176 CommitTransaction();
1181 * Here we are in the middle of a transaction block but one of
1182 * the commands caused an abort so we do nothing but remain in
1183 * the abort state. Eventually we will get to the "END
1184 * TRANSACTION" which will set things straight.
1190 * This means we somehow aborted and the last call to
1191 * CommitTransactionCommand() didn't clear the state so we
1192 * remain in the ENDABORT state and maybe next time we get to
1193 * CommitTransactionCommand() the state will get reset to
1196 case TBLOCK_ENDABORT:
1197 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1202 * We must switch to TransactionCommandContext before returning. This
1203 * is already done if we called StartTransaction, otherwise not.
1205 Assert(TransactionCommandContext != NULL);
1206 MemoryContextSwitchTo(TransactionCommandContext);
1209 /* --------------------------------
1210 * CommitTransactionCommand
1211 * --------------------------------
1214 CommitTransactionCommand(void)
1216 TransactionState s = CurrentTransactionState;
1218 switch (s->blockState)
1222 * if we aren't in a transaction block, we just do our usual
1223 * transaction commit
1225 case TBLOCK_DEFAULT:
1226 CommitTransaction();
1230 * This is the case right after we get a "BEGIN TRANSACTION"
1231 * command, but the user hasn't done anything else yet, so we
1232 * change to the "transaction block in progress" state and
1236 s->blockState = TBLOCK_INPROGRESS;
1240 * This is the case when we have finished executing a command
1241 * someplace within a transaction block. We increment the
1242 * command counter and return. Someday we may free resources
1243 * local to the command.
1245 * That someday is today, at least for memory allocated in
1246 * TransactionCommandContext. - vadim 03/25/97
1248 case TBLOCK_INPROGRESS:
1249 CommandCounterIncrement();
1250 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1254 * This is the case when we just got the "END TRANSACTION"
1255 * statement, so we commit the transaction and go back to the
1259 CommitTransaction();
1260 s->blockState = TBLOCK_DEFAULT;
1264 * Here we are in the middle of a transaction block but one of
1265 * the commands caused an abort so we do nothing but remain in
1266 * the abort state. Eventually we will get to the "END
1267 * TRANSACTION" which will set things straight.
1273 * Here we were in an aborted transaction block which just
1274 * processed the "END TRANSACTION" command from the user, so
1275 * clean up and return to the default state.
1277 case TBLOCK_ENDABORT:
1278 CleanupTransaction();
1279 s->blockState = TBLOCK_DEFAULT;
1284 /* --------------------------------
1285 * AbortCurrentTransaction
1286 * --------------------------------
1289 AbortCurrentTransaction(void)
1291 TransactionState s = CurrentTransactionState;
1293 switch (s->blockState)
1297 * if we aren't in a transaction block, we just do the basic
1298 * abort & cleanup transaction.
1300 case TBLOCK_DEFAULT:
1302 CleanupTransaction();
1306 * If we are in the TBLOCK_BEGIN it means something screwed up
1307 * right after reading "BEGIN TRANSACTION" so we enter the
1308 * abort state. Eventually an "END TRANSACTION" will fix
1312 s->blockState = TBLOCK_ABORT;
1314 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1318 * This is the case when are somewhere in a transaction block
1319 * which aborted so we abort the transaction and set the ABORT
1320 * state. Eventually an "END TRANSACTION" will fix things and
1321 * restore us to a normal state.
1323 case TBLOCK_INPROGRESS:
1324 s->blockState = TBLOCK_ABORT;
1326 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1330 * Here, the system was fouled up just after the user wanted
1331 * to end the transaction block so we abort the transaction
1332 * and put us back into the default state.
1335 s->blockState = TBLOCK_DEFAULT;
1337 CleanupTransaction();
1341 * Here, we are already in an aborted transaction state and
1342 * are waiting for an "END TRANSACTION" to come along and lo
1343 * and behold, we abort again! So we just remain in the abort
1350 * Here we were in an aborted transaction block which just
1351 * processed the "END TRANSACTION" command but somehow aborted
1352 * again.. since we must have done the abort processing, we
1353 * clean up and return to the default state.
1355 case TBLOCK_ENDABORT:
1356 CleanupTransaction();
1357 s->blockState = TBLOCK_DEFAULT;
1362 /* ----------------------------------------------------------------
1363 * transaction block support
1364 * ----------------------------------------------------------------
1366 /* --------------------------------
1367 * BeginTransactionBlock
1368 * --------------------------------
1371 BeginTransactionBlock(void)
1373 TransactionState s = CurrentTransactionState;
1376 * check the current transaction state
1378 if (s->blockState != TBLOCK_DEFAULT)
1379 elog(NOTICE, "BEGIN: already a transaction in progress");
1382 * set the current transaction block state information appropriately
1383 * during begin processing
1385 s->blockState = TBLOCK_BEGIN;
1388 * do begin processing
1392 * done with begin processing, set block state to inprogress
1394 s->blockState = TBLOCK_INPROGRESS;
1397 /* --------------------------------
1398 * EndTransactionBlock
1399 * --------------------------------
1402 EndTransactionBlock(void)
1404 TransactionState s = CurrentTransactionState;
1407 * check the current transaction state
1409 if (s->blockState == TBLOCK_INPROGRESS)
1413 * here we are in a transaction block which should commit when we
1414 * get to the upcoming CommitTransactionCommand() so we set the
1415 * state to "END". CommitTransactionCommand() will recognize this
1416 * and commit the transaction and return us to the default state
1418 s->blockState = TBLOCK_END;
1422 if (s->blockState == TBLOCK_ABORT)
1426 * here, we are in a transaction block which aborted and since the
1427 * AbortTransaction() was already done, we do whatever is needed
1428 * and change to the special "END ABORT" state. The upcoming
1429 * CommitTransactionCommand() will recognise this and then put us
1430 * back in the default state.
1432 s->blockState = TBLOCK_ENDABORT;
1437 * here, the user issued COMMIT when not inside a transaction. Issue a
1438 * notice and go to abort state. The upcoming call to
1439 * CommitTransactionCommand() will then put us back into the default
1442 elog(NOTICE, "COMMIT: no transaction in progress");
1444 s->blockState = TBLOCK_ENDABORT;
1447 /* --------------------------------
1448 * AbortTransactionBlock
1449 * --------------------------------
1453 AbortTransactionBlock(void)
1455 TransactionState s = CurrentTransactionState;
1458 * check the current transaction state
1460 if (s->blockState == TBLOCK_INPROGRESS)
1464 * here we were inside a transaction block something screwed up
1465 * inside the system so we enter the abort state, do the abort
1466 * processing and then return. We remain in the abort state until
1467 * we see an END TRANSACTION command.
1469 s->blockState = TBLOCK_ABORT;
1475 * here, the user issued ABORT when not inside a transaction. Issue a
1476 * notice and go to abort state. The upcoming call to
1477 * CommitTransactionCommand() will then put us back into the default
1480 elog(NOTICE, "ROLLBACK: no transaction in progress");
1482 s->blockState = TBLOCK_ENDABORT;
1487 /* --------------------------------
1488 * UserAbortTransactionBlock
1489 * --------------------------------
1492 UserAbortTransactionBlock(void)
1494 TransactionState s = CurrentTransactionState;
1497 * if the transaction has already been automatically aborted with an
1498 * error, and the user subsequently types 'abort', allow it. (the
1499 * behavior is the same as if they had typed 'end'.)
1501 if (s->blockState == TBLOCK_ABORT)
1503 s->blockState = TBLOCK_ENDABORT;
1507 if (s->blockState == TBLOCK_INPROGRESS)
1511 * here we were inside a transaction block and we got an abort
1512 * command from the user, so we move to the abort state, do the
1513 * abort processing and then change to the ENDABORT state so we
1514 * will end up in the default state after the upcoming
1515 * CommitTransactionCommand().
1517 s->blockState = TBLOCK_ABORT;
1519 s->blockState = TBLOCK_ENDABORT;
1524 * here, the user issued ABORT when not inside a transaction. Issue a
1525 * notice and go to abort state. The upcoming call to
1526 * CommitTransactionCommand() will then put us back into the default
1529 elog(NOTICE, "ROLLBACK: no transaction in progress");
1531 s->blockState = TBLOCK_ENDABORT;
1534 /* --------------------------------
1535 * AbortOutOfAnyTransaction
1537 * This routine is provided for error recovery purposes. It aborts any
1538 * active transaction or transaction block, leaving the system in a known
1540 * --------------------------------
1543 AbortOutOfAnyTransaction(void)
1545 TransactionState s = CurrentTransactionState;
1548 * Get out of any low-level transaction
1553 case TRANS_INPROGRESS:
1555 /* In a transaction, so clean up */
1557 CleanupTransaction();
1560 /* AbortTransaction already done, still need Cleanup */
1561 CleanupTransaction();
1564 /* Not in a transaction, do nothing */
1569 * Now reset the high-level state
1571 s->blockState = TBLOCK_DEFAULT;
1575 IsTransactionBlock(void)
1577 TransactionState s = CurrentTransactionState;
1579 if (s->blockState == TBLOCK_INPROGRESS
1580 || s->blockState == TBLOCK_ABORT
1581 || s->blockState == TBLOCK_ENDABORT)
1588 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1590 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1592 if (info == XLOG_XACT_COMMIT)
1594 TransactionIdCommit(record->xl_xid);
1595 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1597 else if (info == XLOG_XACT_ABORT)
1599 TransactionIdAbort(record->xl_xid);
1600 /* SHOULD REMOVE FILES OF ALL FAILED-TO-BE-CREATED RELATIONS */
1603 elog(STOP, "xact_redo: unknown op code %u", info);
1607 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1609 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1611 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1612 elog(STOP, "xact_undo: can't undo committed xaction");
1613 else if (info != XLOG_XACT_ABORT)
1614 elog(STOP, "xact_redo: unknown op code %u", info);
1618 xact_desc(char *buf, uint8 xl_info, char *rec)
1620 uint8 info = xl_info & ~XLR_INFO_MASK;
1622 if (info == XLOG_XACT_COMMIT)
1624 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1625 struct tm *tm = localtime(&xlrec->xtime);
1627 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1628 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1629 tm->tm_hour, tm->tm_min, tm->tm_sec);
1631 else if (info == XLOG_XACT_ABORT)
1633 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1634 struct tm *tm = localtime(&xlrec->xtime);
1636 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1637 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1638 tm->tm_hour, tm->tm_min, tm->tm_sec);
1641 strcat(buf, "UNKNOWN");
1645 XactPushRollback(void (*func) (void *), void *data)
1648 if (_RollbackFunc != NULL)
1649 elog(STOP, "XactPushRollback: already installed");
1652 _RollbackFunc = func;
1653 _RollbackData = data;
1657 XactPopRollback(void)
1659 _RollbackFunc = NULL;