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