]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xact.c
Remove global variable scanCommandId in favor of storing a command ID
[postgresql] / src / backend / access / transam / xact.c
1 /*-------------------------------------------------------------------------
2  *
3  * xact.c
4  *        top level transaction system support routines
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.123 2002/05/21 22:05:53 tgl Exp $
12  *
13  * NOTES
14  *              Transaction aborts can now occur two ways:
15  *
16  *              1)      system dies from some internal cause  (Assert, etc..)
17  *              2)      user types abort
18  *
19  *              These two cases used to be treated identically, but now
20  *              we need to distinguish them.  Why?      consider the following
21  *              two situations:
22  *
23  *                              case 1                                                  case 2
24  *                              ------                                                  ------
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
29  *
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)
35  *
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:
41  *
42  *              * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
43  *              * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
44  *
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.
53  *
54  *       NOTES
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)
62  *
63  *              The essential aspects of the transaction system are:
64  *
65  *                              o  transaction id generation
66  *                              o  transaction log updating
67  *                              o  memory cleanup
68  *                              o  cache invalidation
69  *                              o  lock cleanup
70  *
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.
76  *
77  *              The code is layered as follows:
78  *
79  *                              StartTransaction
80  *                              CommitTransaction
81  *                              AbortTransaction
82  *                              CleanupTransaction
83  *
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:
87  *
88  *                              StartTransactionCommand
89  *                              CommitTransactionCommand
90  *                              AbortCurrentTransaction
91  *
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.
95  *
96  *              Support for transaction blocks is provided via the functions:
97  *
98  *                              StartTransactionBlock
99  *                              CommitTransactionBlock
100  *                              AbortTransactionBlock
101  *
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().
106  *
107  *              For example, consider the following sequence of user commands:
108  *
109  *              1)              begin
110  *              2)              retrieve (foo.all)
111  *              3)              append foo (bar = baz)
112  *              4)              end
113  *
114  *              in the main processing loop, this results in the following
115  *              transaction sequence:
116  *
117  *                      /       StartTransactionCommand();
118  *              1) /    ProcessUtility();                               << begin
119  *                 \            StartTransactionBlock();
120  *                      \       CommitTransactionCommand();
121  *
122  *                      /       StartTransactionCommand();
123  *              2) <    ProcessQuery();                                 << retrieve (foo.all)
124  *                      \       CommitTransactionCommand();
125  *
126  *                      /       StartTransactionCommand();
127  *              3) <    ProcessQuery();                                 << append foo (bar = baz)
128  *                      \       CommitTransactionCommand();
129  *
130  *                      /       StartTransactionCommand();
131  *              4) /    ProcessUtility();                               << end
132  *                 \            CommitTransactionBlock();
133  *                      \       CommitTransactionCommand();
134  *
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
140  *              processing.
141  *
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".
145  *              -cim 3/23/90
146  *
147  *-------------------------------------------------------------------------
148  */
149
150 /*
151  * Large object clean up added in CommitTransaction() to prevent buffer leaks.
152  * [PA, 7/17/98]
153  * [PA] is Pascal AndrĂ© <andre@via.ecp.fr>
154  */
155 #include "postgres.h"
156
157 #include <unistd.h>
158 #include <sys/time.h>
159
160 #include "access/gistscan.h"
161 #include "access/hash.h"
162 #include "access/nbtree.h"
163 #include "access/rtree.h"
164 #include "access/xact.h"
165 #include "catalog/heap.h"
166 #include "catalog/index.h"
167 #include "catalog/namespace.h"
168 #include "commands/async.h"
169 #include "commands/sequence.h"
170 #include "commands/trigger.h"
171 #include "executor/spi.h"
172 #include "libpq/be-fsstubs.h"
173 #include "miscadmin.h"
174 #include "storage/proc.h"
175 #include "storage/sinval.h"
176 #include "storage/smgr.h"
177 #include "utils/guc.h"
178 #include "utils/inval.h"
179 #include "utils/memutils.h"
180 #include "utils/portal.h"
181 #include "utils/catcache.h"
182 #include "utils/relcache.h"
183 #include "pgstat.h"
184
185
186 extern bool SharedBufferChanged;
187
188 static void AbortTransaction(void);
189 static void AtAbort_Cache(void);
190 static void AtAbort_Locks(void);
191 static void AtAbort_Memory(void);
192 static void AtCleanup_Memory(void);
193 static void AtCommit_Cache(void);
194 static void AtCommit_LocalCache(void);
195 static void AtCommit_Locks(void);
196 static void AtCommit_Memory(void);
197 static void AtStart_Cache(void);
198 static void AtStart_Locks(void);
199 static void AtStart_Memory(void);
200 static void CleanupTransaction(void);
201 static void CommitTransaction(void);
202 static void RecordTransactionAbort(void);
203 static void StartTransaction(void);
204
205 /* ----------------
206  *              global variables holding the current transaction state.
207  * ----------------
208  */
209 static TransactionStateData CurrentTransactionStateData = {
210         0,                                                      /* transaction id */
211         FirstCommandId,                         /* command id */
212         0,                                                      /* scan command id */
213         0x0,                                            /* start time */
214         TRANS_DEFAULT,                          /* transaction state */
215         TBLOCK_DEFAULT                          /* transaction block state */
216 };
217
218 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
219
220 /*
221  * User-tweakable parameters
222  */
223 int                     DefaultXactIsoLevel = XACT_READ_COMMITTED;
224 int                     XactIsoLevel;
225
226 int                     CommitDelay = 0;        /* precommit delay in microseconds */
227 int                     CommitSiblings = 5; /* number of concurrent xacts needed to
228                                                                  * sleep */
229
230 static void (*_RollbackFunc) (void *) = NULL;
231 static void *_RollbackData = NULL;
232
233 /* ----------------
234  *              catalog creation transaction bootstrapping flag.
235  *              This should be eliminated and added to the transaction
236  *              state stuff.  -cim 3/19/90
237  * ----------------
238  */
239 bool            AMI_OVERRIDE = false;
240
241 /* ----------------------------------------------------------------
242  *                                       transaction state accessors
243  * ----------------------------------------------------------------
244  */
245
246 #ifdef NOT_USED
247
248 /* --------------------------------
249  *              TransactionFlushEnabled()
250  *              SetTransactionFlushEnabled()
251  *
252  *              These are used to test and set the "TransactionFlushState"
253  *              varable.  If this variable is true (the default), then
254  *              the system will flush all dirty buffers to disk at the end
255  *              of each transaction.   If false then we are assuming the
256  *              buffer pool resides in stable main memory, in which case we
257  *              only do writes as necessary.
258  * --------------------------------
259  */
260 static int      TransactionFlushState = 1;
261
262 int
263 TransactionFlushEnabled(void)
264 {
265         return TransactionFlushState;
266 }
267
268 void
269 SetTransactionFlushEnabled(bool state)
270 {
271         TransactionFlushState = (state == true);
272 }
273
274 #endif
275
276
277 /* --------------------------------
278  *              IsTransactionState
279  *
280  *              This returns true if we are currently running a query
281  *              within an executing transaction.
282  * --------------------------------
283  */
284 bool
285 IsTransactionState(void)
286 {
287         TransactionState s = CurrentTransactionState;
288
289         switch (s->state)
290         {
291                 case TRANS_DEFAULT:
292                         return false;
293                 case TRANS_START:
294                         return true;
295                 case TRANS_INPROGRESS:
296                         return true;
297                 case TRANS_COMMIT:
298                         return true;
299                 case TRANS_ABORT:
300                         return true;
301         }
302
303         /*
304          * Shouldn't get here, but lint is not happy with this...
305          */
306         return false;
307 }
308
309 /* --------------------------------
310  *              IsAbortedTransactionBlockState
311  *
312  *              This returns true if we are currently running a query
313  *              within an aborted transaction block.
314  * --------------------------------
315  */
316 bool
317 IsAbortedTransactionBlockState(void)
318 {
319         TransactionState s = CurrentTransactionState;
320
321         if (s->blockState == TBLOCK_ABORT)
322                 return true;
323
324         return false;
325 }
326
327
328 /* --------------------------------
329  *              GetCurrentTransactionId
330  * --------------------------------
331  */
332 TransactionId
333 GetCurrentTransactionId(void)
334 {
335         TransactionState s = CurrentTransactionState;
336
337         return s->transactionIdData;
338 }
339
340
341 /* --------------------------------
342  *              GetCurrentCommandId
343  * --------------------------------
344  */
345 CommandId
346 GetCurrentCommandId(void)
347 {
348         TransactionState s = CurrentTransactionState;
349
350         return s->commandId;
351 }
352
353
354 /* --------------------------------
355  *              GetCurrentTransactionStartTime
356  * --------------------------------
357  */
358 AbsoluteTime
359 GetCurrentTransactionStartTime(void)
360 {
361         TransactionState s = CurrentTransactionState;
362
363         return s->startTime;
364 }
365
366
367 /* --------------------------------
368  *              GetCurrentTransactionStartTimeUsec
369  * --------------------------------
370  */
371 AbsoluteTime
372 GetCurrentTransactionStartTimeUsec(int *msec)
373 {
374         TransactionState s = CurrentTransactionState;
375
376         *msec = s->startTimeUsec;
377
378         return s->startTime;
379 }
380
381
382 /* --------------------------------
383  *              TransactionIdIsCurrentTransactionId
384  * --------------------------------
385  */
386 bool
387 TransactionIdIsCurrentTransactionId(TransactionId xid)
388 {
389         TransactionState s = CurrentTransactionState;
390
391         if (AMI_OVERRIDE)
392                 return false;
393
394         return TransactionIdEquals(xid, s->transactionIdData);
395 }
396
397
398 /* --------------------------------
399  *              CommandIdIsCurrentCommandId
400  * --------------------------------
401  */
402 bool
403 CommandIdIsCurrentCommandId(CommandId cid)
404 {
405         TransactionState s = CurrentTransactionState;
406
407         if (AMI_OVERRIDE)
408                 return false;
409
410         return (cid == s->commandId) ? true : false;
411 }
412
413
414 /* --------------------------------
415  *              CommandCounterIncrement
416  * --------------------------------
417  */
418 void
419 CommandCounterIncrement(void)
420 {
421         TransactionState s = CurrentTransactionState;
422
423         s->commandId += 1;
424         if (s->commandId == FirstCommandId)     /* check for overflow */
425                 elog(ERROR, "You may only have 2^32-1 commands per transaction");
426
427         /* Propagate new command ID into query snapshots, if set */
428         if (QuerySnapshot)
429                 QuerySnapshot->curcid = s->commandId;
430         if (SerializableSnapshot)
431                 SerializableSnapshot->curcid = s->commandId;
432
433         /*
434          * make cache changes visible to me.  AtCommit_LocalCache() instead of
435          * AtCommit_Cache() is called here.
436          */
437         AtCommit_LocalCache();
438         AtStart_Cache();
439 }
440
441
442 /* ----------------------------------------------------------------
443  *                                              StartTransaction stuff
444  * ----------------------------------------------------------------
445  */
446
447 /* --------------------------------
448  *              AtStart_Cache
449  * --------------------------------
450  */
451 static void
452 AtStart_Cache(void)
453 {
454         AcceptInvalidationMessages();
455 }
456
457 /* --------------------------------
458  *              AtStart_Locks
459  * --------------------------------
460  */
461 static void
462 AtStart_Locks(void)
463 {
464         /*
465          * at present, it is unknown to me what belongs here -cim 3/18/90
466          *
467          * There isn't anything to do at the start of a xact for locks. -mer
468          * 5/24/92
469          */
470 }
471
472 /* --------------------------------
473  *              AtStart_Memory
474  * --------------------------------
475  */
476 static void
477 AtStart_Memory(void)
478 {
479         /*
480          * We shouldn't have any transaction contexts already.
481          */
482         Assert(TopTransactionContext == NULL);
483         Assert(TransactionCommandContext == NULL);
484
485         /*
486          * Create a toplevel context for the transaction.
487          */
488         TopTransactionContext =
489                 AllocSetContextCreate(TopMemoryContext,
490                                                           "TopTransactionContext",
491                                                           ALLOCSET_DEFAULT_MINSIZE,
492                                                           ALLOCSET_DEFAULT_INITSIZE,
493                                                           ALLOCSET_DEFAULT_MAXSIZE);
494
495         /*
496          * Create a statement-level context and make it active.
497          */
498         TransactionCommandContext =
499                 AllocSetContextCreate(TopTransactionContext,
500                                                           "TransactionCommandContext",
501                                                           ALLOCSET_DEFAULT_MINSIZE,
502                                                           ALLOCSET_DEFAULT_INITSIZE,
503                                                           ALLOCSET_DEFAULT_MAXSIZE);
504         MemoryContextSwitchTo(TransactionCommandContext);
505 }
506
507
508 /* ----------------------------------------------------------------
509  *                                              CommitTransaction stuff
510  * ----------------------------------------------------------------
511  */
512
513 /* --------------------------------
514  *              RecordTransactionCommit
515  *
516  *              Note: the two calls to BufferManagerFlush() exist to ensure
517  *                        that data pages are written before log pages.  These
518  *                        explicit calls should be replaced by a more efficient
519  *                        ordered page write scheme in the buffer manager
520  *                        -cim 3/18/90
521  * --------------------------------
522  */
523 void
524 RecordTransactionCommit(void)
525 {
526         TransactionId xid;
527         bool            leak;
528
529         leak = BufferPoolCheckLeak();
530
531         xid = GetCurrentTransactionId();
532
533         /*
534          * We only need to log the commit in xlog and clog if the transaction made
535          * any transaction-controlled XLOG entries.  (Otherwise, its XID appears
536          * nowhere in permanent storage, so no one will ever care if it
537          * committed.)  However, we must flush XLOG to disk if we made any XLOG
538          * entries, whether in or out of transaction control.  For example, if we
539          * reported a nextval() result to the client, this ensures that any XLOG
540          * record generated by nextval will hit the disk before we report the
541          * transaction committed.
542          */
543         if (MyXactMadeXLogEntry)
544         {
545                 XLogRecPtr      recptr;
546
547                 BufmgrCommit();
548
549                 START_CRIT_SECTION();
550
551                 if (MyLastRecPtr.xrecoff != 0)
552                 {
553                         /* Need to emit a commit record */
554                         XLogRecData rdata;
555                         xl_xact_commit xlrec;
556
557                         xlrec.xtime = time(NULL);
558                         rdata.buffer = InvalidBuffer;
559                         rdata.data = (char *) (&xlrec);
560                         rdata.len = SizeOfXactCommit;
561                         rdata.next = NULL;
562
563                         /*
564                          * XXX SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
565                          */
566                         recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
567                 }
568                 else
569                 {
570                         /* Just flush through last record written by me */
571                         recptr = ProcLastRecEnd;
572                 }
573
574                 /*
575                  * Sleep before flush! So we can flush more than one commit
576                  * records per single fsync.  (The idea is some other backend may
577                  * do the XLogFlush while we're sleeping.  This needs work still,
578                  * because on most Unixen, the minimum select() delay is 10msec or
579                  * more, which is way too long.)
580                  *
581                  * We do not sleep if enableFsync is not turned on, nor if there are
582                  * fewer than CommitSiblings other backends with active
583                  * transactions.
584                  */
585                 if (CommitDelay > 0 && enableFsync &&
586                         CountActiveBackends() >= CommitSiblings)
587                 {
588                         struct timeval delay;
589
590                         delay.tv_sec = 0;
591                         delay.tv_usec = CommitDelay;
592                         (void) select(0, NULL, NULL, NULL, &delay);
593                 }
594
595                 XLogFlush(recptr);
596
597                 /* Mark the transaction committed in clog, if needed */
598                 if (MyLastRecPtr.xrecoff != 0)
599                         TransactionIdCommit(xid);
600
601                 END_CRIT_SECTION();
602         }
603
604         /* Break the chain of back-links in the XLOG records I output */
605         MyLastRecPtr.xrecoff = 0;
606         MyXactMadeXLogEntry = false;
607
608         /* Show myself as out of the transaction in PROC array */
609         MyProc->logRec.xrecoff = 0;
610
611         if (leak)
612                 ResetBufferPool(true);
613 }
614
615
616 /* --------------------------------
617  *              AtCommit_Cache
618  * --------------------------------
619  */
620 static void
621 AtCommit_Cache(void)
622 {
623         /*
624          * Make catalog changes visible to all backends.
625          */
626         AtEOXactInvalidationMessages(true);
627 }
628
629 /* --------------------------------
630  *              AtCommit_LocalCache
631  * --------------------------------
632  */
633 static void
634 AtCommit_LocalCache(void)
635 {
636         /*
637          * Make catalog changes visible to me for the next command.
638          */
639         CommandEndInvalidationMessages(true);
640 }
641
642 /* --------------------------------
643  *              AtCommit_Locks
644  * --------------------------------
645  */
646 static void
647 AtCommit_Locks(void)
648 {
649         /*
650          * XXX What if ProcReleaseLocks fails?  (race condition?)
651          *
652          * Then you're up a creek! -mer 5/24/92
653          */
654         ProcReleaseLocks(true);
655 }
656
657 /* --------------------------------
658  *              AtCommit_Memory
659  * --------------------------------
660  */
661 static void
662 AtCommit_Memory(void)
663 {
664         /*
665          * Now that we're "out" of a transaction, have the system allocate
666          * things in the top memory context instead of per-transaction
667          * contexts.
668          */
669         MemoryContextSwitchTo(TopMemoryContext);
670
671         /*
672          * Release all transaction-local memory.
673          */
674         Assert(TopTransactionContext != NULL);
675         MemoryContextDelete(TopTransactionContext);
676         TopTransactionContext = NULL;
677         TransactionCommandContext = NULL;
678 }
679
680 /* ----------------------------------------------------------------
681  *                                              AbortTransaction stuff
682  * ----------------------------------------------------------------
683  */
684
685 /* --------------------------------
686  *              RecordTransactionAbort
687  * --------------------------------
688  */
689 static void
690 RecordTransactionAbort(void)
691 {
692         TransactionId xid = GetCurrentTransactionId();
693
694         /*
695          * We only need to log the abort in xlog and clog if the transaction made
696          * any transaction-controlled XLOG entries.  (Otherwise, its XID appears
697          * nowhere in permanent storage, so no one will ever care if it
698          * committed.)  We do not flush XLOG to disk in any case, since the
699          * default assumption after a crash would be that we aborted, anyway.
700          *
701          * Extra check here is to catch case that we aborted partway through
702          * RecordTransactionCommit ...
703          */
704         if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
705         {
706                 XLogRecData rdata;
707                 xl_xact_abort xlrec;
708                 XLogRecPtr      recptr;
709
710                 xlrec.xtime = time(NULL);
711                 rdata.buffer = InvalidBuffer;
712                 rdata.data = (char *) (&xlrec);
713                 rdata.len = SizeOfXactAbort;
714                 rdata.next = NULL;
715
716                 START_CRIT_SECTION();
717
718                 /*
719                  * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
720                  */
721                 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
722
723                 /* Mark the transaction aborted in clog */
724                 TransactionIdAbort(xid);
725
726                 END_CRIT_SECTION();
727         }
728
729         /* Break the chain of back-links in the XLOG records I output */
730         MyLastRecPtr.xrecoff = 0;
731         MyXactMadeXLogEntry = false;
732
733         /* Show myself as out of the transaction in PROC array */
734         MyProc->logRec.xrecoff = 0;
735
736         /*
737          * Tell bufmgr and smgr to release resources.
738          */
739         ResetBufferPool(false);         /* false -> is abort */
740 }
741
742 /* --------------------------------
743  *              AtAbort_Cache
744  * --------------------------------
745  */
746 static void
747 AtAbort_Cache(void)
748 {
749         RelationCacheAbort();
750         AtEOXactInvalidationMessages(false);
751 }
752
753 /* --------------------------------
754  *              AtAbort_Locks
755  * --------------------------------
756  */
757 static void
758 AtAbort_Locks(void)
759 {
760         /*
761          * XXX What if ProcReleaseLocks() fails?  (race condition?)
762          *
763          * Then you're up a creek without a paddle! -mer
764          */
765         ProcReleaseLocks(false);
766 }
767
768
769 /* --------------------------------
770  *              AtAbort_Memory
771  * --------------------------------
772  */
773 static void
774 AtAbort_Memory(void)
775 {
776         /*
777          * Make sure we are in a valid context (not a child of
778          * TransactionCommandContext...).  Note that it is possible for this
779          * code to be called when we aren't in a transaction at all; go
780          * directly to TopMemoryContext in that case.
781          */
782         if (TransactionCommandContext != NULL)
783         {
784                 MemoryContextSwitchTo(TransactionCommandContext);
785
786                 /*
787                  * We do not want to destroy transaction contexts yet, but it
788                  * should be OK to delete any command-local memory.
789                  */
790                 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
791         }
792         else
793                 MemoryContextSwitchTo(TopMemoryContext);
794 }
795
796
797 /* ----------------------------------------------------------------
798  *                                              CleanupTransaction stuff
799  * ----------------------------------------------------------------
800  */
801
802 /* --------------------------------
803  *              AtCleanup_Memory
804  * --------------------------------
805  */
806 static void
807 AtCleanup_Memory(void)
808 {
809         /*
810          * Now that we're "out" of a transaction, have the system allocate
811          * things in the top memory context instead of per-transaction
812          * contexts.
813          */
814         MemoryContextSwitchTo(TopMemoryContext);
815
816         /*
817          * Release all transaction-local memory.
818          */
819         if (TopTransactionContext != NULL)
820                 MemoryContextDelete(TopTransactionContext);
821         TopTransactionContext = NULL;
822         TransactionCommandContext = NULL;
823 }
824
825
826 /* ----------------------------------------------------------------
827  *                                              interface routines
828  * ----------------------------------------------------------------
829  */
830
831 /* --------------------------------
832  *              StartTransaction
833  *
834  * --------------------------------
835  */
836 static void
837 StartTransaction(void)
838 {
839         TransactionState s = CurrentTransactionState;
840
841         FreeXactSnapshot();
842         XactIsoLevel = DefaultXactIsoLevel;
843
844         /*
845          * Check the current transaction state.  If the transaction system is
846          * switched off, or if we're already in a transaction, do nothing.
847          * We're already in a transaction when the monitor sends a null
848          * command to the backend to flush the comm channel.  This is a hacky
849          * fix to a communications problem, and we keep having to deal with it
850          * here.  We should fix the comm channel code.  mao 080891
851          */
852         if (s->state == TRANS_INPROGRESS)
853                 return;
854
855         /*
856          * set the current transaction state information appropriately during
857          * start processing
858          */
859         s->state = TRANS_START;
860
861         SetReindexProcessing(false);
862
863         /*
864          * generate a new transaction id
865          */
866         s->transactionIdData = GetNewTransactionId();
867
868         XactLockTableInsert(s->transactionIdData);
869
870         /*
871          * initialize current transaction state fields
872          */
873         s->commandId = FirstCommandId;
874         s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
875
876         /*
877          * initialize the various transaction subsystems
878          */
879         AtStart_Memory();
880         AtStart_Cache();
881         AtStart_Locks();
882
883         /*
884          * Tell the trigger manager to we're starting a transaction
885          */
886         DeferredTriggerBeginXact();
887
888         /*
889          * done with start processing, set current transaction state to "in
890          * progress"
891          */
892         s->state = TRANS_INPROGRESS;
893
894 }
895
896 #ifdef NOT_USED
897 /* ---------------
898  * Tell me if we are currently in progress
899  * ---------------
900  */
901 bool
902 CurrentXactInProgress(void)
903 {
904         return CurrentTransactionState->state == TRANS_INPROGRESS;
905 }
906 #endif
907
908 /* --------------------------------
909  *              CommitTransaction
910  *
911  * --------------------------------
912  */
913 static void
914 CommitTransaction(void)
915 {
916         TransactionState s = CurrentTransactionState;
917
918         /*
919          * check the current transaction state
920          */
921         if (s->state != TRANS_INPROGRESS)
922                 elog(WARNING, "CommitTransaction and not in in-progress state");
923
924         /*
925          * Tell the trigger manager that this transaction is about to be
926          * committed. He'll invoke all trigger deferred until XACT before we
927          * really start on committing the transaction.
928          */
929         DeferredTriggerEndXact();
930
931         /* Prevent cancel/die interrupt while cleaning up */
932         HOLD_INTERRUPTS();
933
934         /*
935          * set the current transaction state information appropriately during
936          * the abort processing
937          */
938         s->state = TRANS_COMMIT;
939
940         /*
941          * do commit processing
942          */
943
944         /* handle commit for large objects [ PA, 7/17/98 ] */
945         lo_commit(true);
946
947         /* NOTIFY commit must also come before lower-level cleanup */
948         AtCommit_Notify();
949
950         CloseSequences();
951         AtEOXact_portals();
952
953         /* Here is where we really truly commit. */
954         RecordTransactionCommit();
955
956         /*
957          * Let others know about no transaction in progress by me. Note that
958          * this must be done _before_ releasing locks we hold and _after_
959          * RecordTransactionCommit.
960          *
961          * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by
962          * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if
963          * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0
964          * as running as well or it will see two tuple versions - one deleted
965          * by xid 1 and one inserted by xid 0.  See notes in GetSnapshotData.
966          */
967         if (MyProc != (PROC *) NULL)
968         {
969                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
970                 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
971                 MyProc->xid = InvalidTransactionId;
972                 MyProc->xmin = InvalidTransactionId;
973                 LWLockRelease(SInvalLock);
974         }
975
976         /*
977          * This is all post-commit cleanup.  Note that if an error is raised
978          * here, it's too late to abort the transaction.  This should be just
979          * noncritical resource releasing.
980          */
981
982         RelationPurgeLocalRelation(true);
983         smgrDoPendingDeletes(true);
984
985         AtEOXact_GUC(true);
986         AtEOXact_SPI();
987         AtEOXact_gist();
988         AtEOXact_hash();
989         AtEOXact_nbtree();
990         AtEOXact_rtree();
991         AtEOXact_Namespace(true);
992         AtCommit_Cache();
993         AtCommit_Locks();
994         AtEOXact_CatCache(true);
995         AtCommit_Memory();
996         AtEOXact_Files();
997
998         SharedBufferChanged = false;    /* safest place to do it */
999
1000         /* Count transaction commit in statistics collector */
1001         pgstat_count_xact_commit();
1002
1003         /*
1004          * done with commit processing, set current transaction state back to
1005          * default
1006          */
1007         s->state = TRANS_DEFAULT;
1008
1009         RESUME_INTERRUPTS();
1010 }
1011
1012 /* --------------------------------
1013  *              AbortTransaction
1014  *
1015  * --------------------------------
1016  */
1017 static void
1018 AbortTransaction(void)
1019 {
1020         TransactionState s = CurrentTransactionState;
1021
1022         /* Prevent cancel/die interrupt while cleaning up */
1023         HOLD_INTERRUPTS();
1024
1025         /*
1026          * Release any LW locks we might be holding as quickly as possible.
1027          * (Regular locks, however, must be held till we finish aborting.)
1028          * Releasing LW locks is critical since we might try to grab them
1029          * again while cleaning up!
1030          */
1031         LWLockReleaseAll();
1032
1033         /* Clean up buffer I/O and buffer context locks, too */
1034         AbortBufferIO();
1035         UnlockBuffers();
1036
1037         /*
1038          * Also clean up any open wait for lock, since the lock manager will
1039          * choke if we try to wait for another lock before doing this.
1040          */
1041         LockWaitCancel();
1042
1043         /*
1044          * check the current transaction state
1045          */
1046         if (s->state != TRANS_INPROGRESS)
1047                 elog(WARNING, "AbortTransaction and not in in-progress state");
1048
1049         /*
1050          * set the current transaction state information appropriately during
1051          * the abort processing
1052          */
1053         s->state = TRANS_ABORT;
1054
1055         /*
1056          * Reset user id which might have been changed transiently
1057          */
1058         SetUserId(GetSessionUserId());
1059
1060         /*
1061          * do abort processing
1062          */
1063         DeferredTriggerAbortXact();
1064         lo_commit(false);                       /* 'false' means it's abort */
1065         AtAbort_Notify();
1066         CloseSequences();
1067         AtEOXact_portals();
1068
1069         /* Advertise the fact that we aborted in pg_clog. */
1070         RecordTransactionAbort();
1071
1072         /*
1073          * Let others know about no transaction in progress by me. Note that
1074          * this must be done _before_ releasing locks we hold and _after_
1075          * RecordTransactionAbort.
1076          */
1077         if (MyProc != (PROC *) NULL)
1078         {
1079                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1080                 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
1081                 MyProc->xid = InvalidTransactionId;
1082                 MyProc->xmin = InvalidTransactionId;
1083                 LWLockRelease(SInvalLock);
1084         }
1085
1086         RelationPurgeLocalRelation(false);
1087         smgrDoPendingDeletes(false);
1088
1089         AtEOXact_GUC(false);
1090         AtEOXact_SPI();
1091         AtEOXact_gist();
1092         AtEOXact_hash();
1093         AtEOXact_nbtree();
1094         AtEOXact_rtree();
1095         AtEOXact_Namespace(false);
1096         AtAbort_Cache();
1097         AtEOXact_CatCache(false);
1098         AtAbort_Memory();
1099         AtEOXact_Files();
1100         AtAbort_Locks();
1101
1102         SharedBufferChanged = false;    /* safest place to do it */
1103
1104         /* Count transaction abort in statistics collector */
1105         pgstat_count_xact_rollback();
1106
1107         /*
1108          * State remains TRANS_ABORT until CleanupTransaction().
1109          */
1110         RESUME_INTERRUPTS();
1111 }
1112
1113 /* --------------------------------
1114  *              CleanupTransaction
1115  *
1116  * --------------------------------
1117  */
1118 static void
1119 CleanupTransaction(void)
1120 {
1121         TransactionState s = CurrentTransactionState;
1122
1123         /*
1124          * State should still be TRANS_ABORT from AbortTransaction().
1125          */
1126         if (s->state != TRANS_ABORT)
1127                 elog(FATAL, "CleanupTransaction and not in abort state");
1128
1129         /*
1130          * do abort cleanup processing
1131          */
1132         AtCleanup_Memory();
1133
1134         /*
1135          * done with abort processing, set current transaction state back to
1136          * default
1137          */
1138         s->state = TRANS_DEFAULT;
1139 }
1140
1141 /* --------------------------------
1142  *              StartTransactionCommand
1143  * --------------------------------
1144  */
1145 void
1146 StartTransactionCommand(void)
1147 {
1148         TransactionState s = CurrentTransactionState;
1149
1150         switch (s->blockState)
1151         {
1152                         /*
1153                          * if we aren't in a transaction block, we just do our usual
1154                          * start transaction.
1155                          */
1156                 case TBLOCK_DEFAULT:
1157                         StartTransaction();
1158                         break;
1159
1160                         /*
1161                          * We should never experience this -- if we do it means the
1162                          * BEGIN state was not changed in the previous
1163                          * CommitTransactionCommand().  If we get it, we print a
1164                          * warning and change to the in-progress state.
1165                          */
1166                 case TBLOCK_BEGIN:
1167                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1168                         s->blockState = TBLOCK_INPROGRESS;
1169                         break;
1170
1171                         /*
1172                          * This is the case when are somewhere in a transaction block
1173                          * and about to start a new command.  For now we do nothing
1174                          * but someday we may do command-local resource
1175                          * initialization.
1176                          */
1177                 case TBLOCK_INPROGRESS:
1178                         break;
1179
1180                         /*
1181                          * As with BEGIN, we should never experience this if we do it
1182                          * means the END state was not changed in the previous
1183                          * CommitTransactionCommand().  If we get it, we print a
1184                          * warning, commit the transaction, start a new transaction
1185                          * and change to the default state.
1186                          */
1187                 case TBLOCK_END:
1188                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_END");
1189                         s->blockState = TBLOCK_DEFAULT;
1190                         CommitTransaction();
1191                         StartTransaction();
1192                         break;
1193
1194                         /*
1195                          * Here we are in the middle of a transaction block but one of
1196                          * the commands caused an abort so we do nothing but remain in
1197                          * the abort state.  Eventually we will get to the "END
1198                          * TRANSACTION" which will set things straight.
1199                          */
1200                 case TBLOCK_ABORT:
1201                         break;
1202
1203                         /*
1204                          * This means we somehow aborted and the last call to
1205                          * CommitTransactionCommand() didn't clear the state so we
1206                          * remain in the ENDABORT state and maybe next time we get to
1207                          * CommitTransactionCommand() the state will get reset to
1208                          * default.
1209                          */
1210                 case TBLOCK_ENDABORT:
1211                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1212                         break;
1213         }
1214
1215         /*
1216          * We must switch to TransactionCommandContext before returning. This
1217          * is already done if we called StartTransaction, otherwise not.
1218          */
1219         Assert(TransactionCommandContext != NULL);
1220         MemoryContextSwitchTo(TransactionCommandContext);
1221 }
1222
1223 /* --------------------------------
1224  *              CommitTransactionCommand
1225  * --------------------------------
1226  */
1227 void
1228 CommitTransactionCommand(void)
1229 {
1230         TransactionState s = CurrentTransactionState;
1231
1232         switch (s->blockState)
1233         {
1234                         /*
1235                          * if we aren't in a transaction block, we just do our usual
1236                          * transaction commit
1237                          */
1238                 case TBLOCK_DEFAULT:
1239                         CommitTransaction();
1240                         break;
1241
1242                         /*
1243                          * This is the case right after we get a "BEGIN TRANSACTION"
1244                          * command, but the user hasn't done anything else yet, so we
1245                          * change to the "transaction block in progress" state and
1246                          * return.
1247                          */
1248                 case TBLOCK_BEGIN:
1249                         s->blockState = TBLOCK_INPROGRESS;
1250                         break;
1251
1252                         /*
1253                          * This is the case when we have finished executing a command
1254                          * someplace within a transaction block.  We increment the
1255                          * command counter and return.  Someday we may free resources
1256                          * local to the command.
1257                          *
1258                          * That someday is today, at least for memory allocated in
1259                          * TransactionCommandContext. - vadim 03/25/97
1260                          */
1261                 case TBLOCK_INPROGRESS:
1262                         CommandCounterIncrement();
1263                         MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1264                         break;
1265
1266                         /*
1267                          * This is the case when we just got the "END TRANSACTION"
1268                          * statement, so we commit the transaction and go back to the
1269                          * default state.
1270                          */
1271                 case TBLOCK_END:
1272                         CommitTransaction();
1273                         s->blockState = TBLOCK_DEFAULT;
1274                         break;
1275
1276                         /*
1277                          * Here we are in the middle of a transaction block but one of
1278                          * the commands caused an abort so we do nothing but remain in
1279                          * the abort state.  Eventually we will get to the "END
1280                          * TRANSACTION" which will set things straight.
1281                          */
1282                 case TBLOCK_ABORT:
1283                         break;
1284
1285                         /*
1286                          * Here we were in an aborted transaction block which just
1287                          * processed the "END TRANSACTION" command from the user, so
1288                          * clean up and return to the default state.
1289                          */
1290                 case TBLOCK_ENDABORT:
1291                         CleanupTransaction();
1292                         s->blockState = TBLOCK_DEFAULT;
1293                         break;
1294         }
1295 }
1296
1297 /* --------------------------------
1298  *              AbortCurrentTransaction
1299  * --------------------------------
1300  */
1301 void
1302 AbortCurrentTransaction(void)
1303 {
1304         TransactionState s = CurrentTransactionState;
1305
1306         switch (s->blockState)
1307         {
1308                         /*
1309                          * if we aren't in a transaction block, we just do the basic
1310                          * abort & cleanup transaction.
1311                          */
1312                 case TBLOCK_DEFAULT:
1313                         AbortTransaction();
1314                         CleanupTransaction();
1315                         break;
1316
1317                         /*
1318                          * If we are in the TBLOCK_BEGIN it means something screwed up
1319                          * right after reading "BEGIN TRANSACTION" so we enter the
1320                          * abort state.  Eventually an "END TRANSACTION" will fix
1321                          * things.
1322                          */
1323                 case TBLOCK_BEGIN:
1324                         s->blockState = TBLOCK_ABORT;
1325                         AbortTransaction();
1326                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1327                         break;
1328
1329                         /*
1330                          * This is the case when are somewhere in a transaction block
1331                          * which aborted so we abort the transaction and set the ABORT
1332                          * state.  Eventually an "END TRANSACTION" will fix things and
1333                          * restore us to a normal state.
1334                          */
1335                 case TBLOCK_INPROGRESS:
1336                         s->blockState = TBLOCK_ABORT;
1337                         AbortTransaction();
1338                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1339                         break;
1340
1341                         /*
1342                          * Here, the system was fouled up just after the user wanted
1343                          * to end the transaction block so we abort the transaction
1344                          * and put us back into the default state.
1345                          */
1346                 case TBLOCK_END:
1347                         s->blockState = TBLOCK_DEFAULT;
1348                         AbortTransaction();
1349                         CleanupTransaction();
1350                         break;
1351
1352                         /*
1353                          * Here, we are already in an aborted transaction state and
1354                          * are waiting for an "END TRANSACTION" to come along and lo
1355                          * and behold, we abort again! So we just remain in the abort
1356                          * state.
1357                          */
1358                 case TBLOCK_ABORT:
1359                         break;
1360
1361                         /*
1362                          * Here we were in an aborted transaction block which just
1363                          * processed the "END TRANSACTION" command but somehow aborted
1364                          * again.. since we must have done the abort processing, we
1365                          * clean up and return to the default state.
1366                          */
1367                 case TBLOCK_ENDABORT:
1368                         CleanupTransaction();
1369                         s->blockState = TBLOCK_DEFAULT;
1370                         break;
1371         }
1372 }
1373
1374 /* ----------------------------------------------------------------
1375  *                                         transaction block support
1376  * ----------------------------------------------------------------
1377  */
1378 /* --------------------------------
1379  *              BeginTransactionBlock
1380  * --------------------------------
1381  */
1382 void
1383 BeginTransactionBlock(void)
1384 {
1385         TransactionState s = CurrentTransactionState;
1386
1387         /*
1388          * check the current transaction state
1389          */
1390         if (s->blockState != TBLOCK_DEFAULT)
1391                 elog(WARNING, "BEGIN: already a transaction in progress");
1392
1393         /*
1394          * set the current transaction block state information appropriately
1395          * during begin processing
1396          */
1397         s->blockState = TBLOCK_BEGIN;
1398
1399         /*
1400          * do begin processing
1401          */
1402
1403         /*
1404          * done with begin processing, set block state to inprogress
1405          */
1406         s->blockState = TBLOCK_INPROGRESS;
1407 }
1408
1409 /* --------------------------------
1410  *              EndTransactionBlock
1411  * --------------------------------
1412  */
1413 void
1414 EndTransactionBlock(void)
1415 {
1416         TransactionState s = CurrentTransactionState;
1417
1418         /*
1419          * check the current transaction state
1420          */
1421         if (s->blockState == TBLOCK_INPROGRESS)
1422         {
1423                 /*
1424                  * here we are in a transaction block which should commit when we
1425                  * get to the upcoming CommitTransactionCommand() so we set the
1426                  * state to "END".      CommitTransactionCommand() will recognize this
1427                  * and commit the transaction and return us to the default state
1428                  */
1429                 s->blockState = TBLOCK_END;
1430                 return;
1431         }
1432
1433         if (s->blockState == TBLOCK_ABORT)
1434         {
1435                 /*
1436                  * here, we are in a transaction block which aborted and since the
1437                  * AbortTransaction() was already done, we do whatever is needed
1438                  * and change to the special "END ABORT" state.  The upcoming
1439                  * CommitTransactionCommand() will recognise this and then put us
1440                  * back in the default state.
1441                  */
1442                 s->blockState = TBLOCK_ENDABORT;
1443                 return;
1444         }
1445
1446         /*
1447          * here, the user issued COMMIT when not inside a transaction. Issue a
1448          * WARNING and go to abort state.  The upcoming call to
1449          * CommitTransactionCommand() will then put us back into the default
1450          * state.
1451          */
1452         elog(WARNING, "COMMIT: no transaction in progress");
1453         AbortTransaction();
1454         s->blockState = TBLOCK_ENDABORT;
1455 }
1456
1457 /* --------------------------------
1458  *              AbortTransactionBlock
1459  * --------------------------------
1460  */
1461 #ifdef NOT_USED
1462 static void
1463 AbortTransactionBlock(void)
1464 {
1465         TransactionState s = CurrentTransactionState;
1466
1467         /*
1468          * check the current transaction state
1469          */
1470         if (s->blockState == TBLOCK_INPROGRESS)
1471         {
1472                 /*
1473                  * here we were inside a transaction block something screwed up
1474                  * inside the system so we enter the abort state, do the abort
1475                  * processing and then return. We remain in the abort state until
1476                  * we see an END TRANSACTION command.
1477                  */
1478                 s->blockState = TBLOCK_ABORT;
1479                 AbortTransaction();
1480                 return;
1481         }
1482
1483         /*
1484          * here, the user issued ABORT when not inside a transaction. Issue a
1485          * WARNING and go to abort state.  The upcoming call to
1486          * CommitTransactionCommand() will then put us back into the default
1487          * state.
1488          */
1489         elog(WARNING, "ROLLBACK: no transaction in progress");
1490         AbortTransaction();
1491         s->blockState = TBLOCK_ENDABORT;
1492 }
1493 #endif
1494
1495 /* --------------------------------
1496  *              UserAbortTransactionBlock
1497  * --------------------------------
1498  */
1499 void
1500 UserAbortTransactionBlock(void)
1501 {
1502         TransactionState s = CurrentTransactionState;
1503
1504         /*
1505          * if the transaction has already been automatically aborted with an
1506          * error, and the user subsequently types 'abort', allow it.  (the
1507          * behavior is the same as if they had typed 'end'.)
1508          */
1509         if (s->blockState == TBLOCK_ABORT)
1510         {
1511                 s->blockState = TBLOCK_ENDABORT;
1512                 return;
1513         }
1514
1515         if (s->blockState == TBLOCK_INPROGRESS)
1516         {
1517                 /*
1518                  * here we were inside a transaction block and we got an abort
1519                  * command from the user, so we move to the abort state, do the
1520                  * abort processing and then change to the ENDABORT state so we
1521                  * will end up in the default state after the upcoming
1522                  * CommitTransactionCommand().
1523                  */
1524                 s->blockState = TBLOCK_ABORT;
1525                 AbortTransaction();
1526                 s->blockState = TBLOCK_ENDABORT;
1527                 return;
1528         }
1529
1530         /*
1531          * here, the user issued ABORT when not inside a transaction. Issue a
1532          * WARNING and go to abort state.  The upcoming call to
1533          * CommitTransactionCommand() will then put us back into the default
1534          * state.
1535          */
1536         elog(WARNING, "ROLLBACK: no transaction in progress");
1537         AbortTransaction();
1538         s->blockState = TBLOCK_ENDABORT;
1539 }
1540
1541 /* --------------------------------
1542  *              AbortOutOfAnyTransaction
1543  *
1544  * This routine is provided for error recovery purposes.  It aborts any
1545  * active transaction or transaction block, leaving the system in a known
1546  * idle state.
1547  * --------------------------------
1548  */
1549 void
1550 AbortOutOfAnyTransaction(void)
1551 {
1552         TransactionState s = CurrentTransactionState;
1553
1554         /*
1555          * Get out of any low-level transaction
1556          */
1557         switch (s->state)
1558         {
1559                 case TRANS_START:
1560                 case TRANS_INPROGRESS:
1561                 case TRANS_COMMIT:
1562                         /* In a transaction, so clean up */
1563                         AbortTransaction();
1564                         CleanupTransaction();
1565                         break;
1566                 case TRANS_ABORT:
1567                         /* AbortTransaction already done, still need Cleanup */
1568                         CleanupTransaction();
1569                         break;
1570                 case TRANS_DEFAULT:
1571                         /* Not in a transaction, do nothing */
1572                         break;
1573         }
1574
1575         /*
1576          * Now reset the high-level state
1577          */
1578         s->blockState = TBLOCK_DEFAULT;
1579 }
1580
1581 bool
1582 IsTransactionBlock(void)
1583 {
1584         TransactionState s = CurrentTransactionState;
1585
1586         if (s->blockState == TBLOCK_INPROGRESS
1587                 || s->blockState == TBLOCK_ABORT
1588                 || s->blockState == TBLOCK_ENDABORT)
1589                 return true;
1590
1591         return false;
1592 }
1593
1594 void
1595 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1596 {
1597         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1598
1599         if (info == XLOG_XACT_COMMIT)
1600         {
1601                 TransactionIdCommit(record->xl_xid);
1602                 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1603         }
1604         else if (info == XLOG_XACT_ABORT)
1605         {
1606                 TransactionIdAbort(record->xl_xid);
1607                 /* SHOULD REMOVE FILES OF ALL FAILED-TO-BE-CREATED RELATIONS */
1608         }
1609         else
1610                 elog(PANIC, "xact_redo: unknown op code %u", info);
1611 }
1612
1613 void
1614 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1615 {
1616         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1617
1618         if (info == XLOG_XACT_COMMIT)           /* shouldn't be called by XLOG */
1619                 elog(PANIC, "xact_undo: can't undo committed xaction");
1620         else if (info != XLOG_XACT_ABORT)
1621                 elog(PANIC, "xact_redo: unknown op code %u", info);
1622 }
1623
1624 void
1625 xact_desc(char *buf, uint8 xl_info, char *rec)
1626 {
1627         uint8           info = xl_info & ~XLR_INFO_MASK;
1628
1629         if (info == XLOG_XACT_COMMIT)
1630         {
1631                 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1632                 struct tm  *tm = localtime(&xlrec->xtime);
1633
1634                 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1635                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1636                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1637         }
1638         else if (info == XLOG_XACT_ABORT)
1639         {
1640                 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1641                 struct tm  *tm = localtime(&xlrec->xtime);
1642
1643                 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1644                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1645                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1646         }
1647         else
1648                 strcat(buf, "UNKNOWN");
1649 }
1650
1651 void
1652                         XactPushRollback(void (*func) (void *), void *data)
1653 {
1654 #ifdef XLOG_II
1655         if (_RollbackFunc != NULL)
1656                 elog(PANIC, "XactPushRollback: already installed");
1657 #endif
1658
1659         _RollbackFunc = func;
1660         _RollbackData = data;
1661 }
1662
1663 void
1664 XactPopRollback(void)
1665 {
1666         _RollbackFunc = NULL;
1667 }