]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xact.c
Repair two problems with WAL logging of sequence nextvalI() ops, as
[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.118 2002/03/15 19:20:29 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 "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 only need to log the commit in xlog and clog if the transaction made
550          * any transaction-controlled XLOG entries.  (Otherwise, its XID appears
551          * nowhere in permanent storage, so no one will ever care if it
552          * committed.)  However, we must flush XLOG to disk if we made any XLOG
553          * entries, whether in or out of transaction control.  For example, if we
554          * reported a nextval() result to the client, this ensures that any XLOG
555          * record generated by nextval will hit the disk before we report the
556          * transaction committed.
557          */
558         if (MyXactMadeXLogEntry)
559         {
560                 XLogRecPtr      recptr;
561
562                 BufmgrCommit();
563
564                 START_CRIT_SECTION();
565
566                 if (MyLastRecPtr.xrecoff != 0)
567                 {
568                         /* Need to emit a commit record */
569                         XLogRecData rdata;
570                         xl_xact_commit xlrec;
571
572                         xlrec.xtime = time(NULL);
573                         rdata.buffer = InvalidBuffer;
574                         rdata.data = (char *) (&xlrec);
575                         rdata.len = SizeOfXactCommit;
576                         rdata.next = NULL;
577
578                         /*
579                          * XXX SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
580                          */
581                         recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
582                 }
583                 else
584                 {
585                         /* Just flush through last record written by me */
586                         recptr = ProcLastRecEnd;
587                 }
588
589                 /*
590                  * Sleep before flush! So we can flush more than one commit
591                  * records per single fsync.  (The idea is some other backend may
592                  * do the XLogFlush while we're sleeping.  This needs work still,
593                  * because on most Unixen, the minimum select() delay is 10msec or
594                  * more, which is way too long.)
595                  *
596                  * We do not sleep if enableFsync is not turned on, nor if there are
597                  * fewer than CommitSiblings other backends with active
598                  * transactions.
599                  */
600                 if (CommitDelay > 0 && enableFsync &&
601                         CountActiveBackends() >= CommitSiblings)
602                 {
603                         struct timeval delay;
604
605                         delay.tv_sec = 0;
606                         delay.tv_usec = CommitDelay;
607                         (void) select(0, NULL, NULL, NULL, &delay);
608                 }
609
610                 XLogFlush(recptr);
611
612                 /* Mark the transaction committed in clog, if needed */
613                 if (MyLastRecPtr.xrecoff != 0)
614                         TransactionIdCommit(xid);
615
616                 END_CRIT_SECTION();
617         }
618
619         /* Break the chain of back-links in the XLOG records I output */
620         MyLastRecPtr.xrecoff = 0;
621         MyXactMadeXLogEntry = false;
622
623         /* Show myself as out of the transaction in PROC array */
624         MyProc->logRec.xrecoff = 0;
625
626         if (leak)
627                 ResetBufferPool(true);
628 }
629
630
631 /* --------------------------------
632  *              AtCommit_Cache
633  * --------------------------------
634  */
635 static void
636 AtCommit_Cache(void)
637 {
638         /*
639          * Make catalog changes visible to all backends.
640          */
641         AtEOXactInvalidationMessages(true);
642 }
643
644 /* --------------------------------
645  *              AtCommit_LocalCache
646  * --------------------------------
647  */
648 static void
649 AtCommit_LocalCache(void)
650 {
651         /*
652          * Make catalog changes visible to me for the next command.
653          */
654         CommandEndInvalidationMessages(true);
655 }
656
657 /* --------------------------------
658  *              AtCommit_Locks
659  * --------------------------------
660  */
661 static void
662 AtCommit_Locks(void)
663 {
664         /*
665          * XXX What if ProcReleaseLocks fails?  (race condition?)
666          *
667          * Then you're up a creek! -mer 5/24/92
668          */
669         ProcReleaseLocks(true);
670 }
671
672 /* --------------------------------
673  *              AtCommit_Memory
674  * --------------------------------
675  */
676 static void
677 AtCommit_Memory(void)
678 {
679         /*
680          * Now that we're "out" of a transaction, have the system allocate
681          * things in the top memory context instead of per-transaction
682          * contexts.
683          */
684         MemoryContextSwitchTo(TopMemoryContext);
685
686         /*
687          * Release all transaction-local memory.
688          */
689         Assert(TopTransactionContext != NULL);
690         MemoryContextDelete(TopTransactionContext);
691         TopTransactionContext = NULL;
692         TransactionCommandContext = NULL;
693 }
694
695 /* ----------------------------------------------------------------
696  *                                              AbortTransaction stuff
697  * ----------------------------------------------------------------
698  */
699
700 /* --------------------------------
701  *              RecordTransactionAbort
702  * --------------------------------
703  */
704 static void
705 RecordTransactionAbort(void)
706 {
707         TransactionId xid = GetCurrentTransactionId();
708
709         /*
710          * We only need to log the abort in xlog and clog if the transaction made
711          * any transaction-controlled XLOG entries.  (Otherwise, its XID appears
712          * nowhere in permanent storage, so no one will ever care if it
713          * committed.)  We do not flush XLOG to disk in any case, since the
714          * default assumption after a crash would be that we aborted, anyway.
715          *
716          * Extra check here is to catch case that we aborted partway through
717          * RecordTransactionCommit ...
718          */
719         if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
720         {
721                 XLogRecData rdata;
722                 xl_xact_abort xlrec;
723                 XLogRecPtr      recptr;
724
725                 xlrec.xtime = time(NULL);
726                 rdata.buffer = InvalidBuffer;
727                 rdata.data = (char *) (&xlrec);
728                 rdata.len = SizeOfXactAbort;
729                 rdata.next = NULL;
730
731                 START_CRIT_SECTION();
732
733                 /*
734                  * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
735                  */
736                 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
737
738                 /* Mark the transaction aborted in clog */
739                 TransactionIdAbort(xid);
740
741                 END_CRIT_SECTION();
742         }
743
744         /* Break the chain of back-links in the XLOG records I output */
745         MyLastRecPtr.xrecoff = 0;
746         MyXactMadeXLogEntry = false;
747
748         /* Show myself as out of the transaction in PROC array */
749         MyProc->logRec.xrecoff = 0;
750
751         /*
752          * Tell bufmgr and smgr to release resources.
753          */
754         ResetBufferPool(false);         /* false -> is abort */
755 }
756
757 /* --------------------------------
758  *              AtAbort_Cache
759  * --------------------------------
760  */
761 static void
762 AtAbort_Cache(void)
763 {
764         RelationCacheAbort();
765         AtEOXactInvalidationMessages(false);
766 }
767
768 /* --------------------------------
769  *              AtAbort_Locks
770  * --------------------------------
771  */
772 static void
773 AtAbort_Locks(void)
774 {
775         /*
776          * XXX What if ProcReleaseLocks() fails?  (race condition?)
777          *
778          * Then you're up a creek without a paddle! -mer
779          */
780         ProcReleaseLocks(false);
781 }
782
783
784 /* --------------------------------
785  *              AtAbort_Memory
786  * --------------------------------
787  */
788 static void
789 AtAbort_Memory(void)
790 {
791         /*
792          * Make sure we are in a valid context (not a child of
793          * TransactionCommandContext...).  Note that it is possible for this
794          * code to be called when we aren't in a transaction at all; go
795          * directly to TopMemoryContext in that case.
796          */
797         if (TransactionCommandContext != NULL)
798         {
799                 MemoryContextSwitchTo(TransactionCommandContext);
800
801                 /*
802                  * We do not want to destroy transaction contexts yet, but it
803                  * should be OK to delete any command-local memory.
804                  */
805                 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
806         }
807         else
808                 MemoryContextSwitchTo(TopMemoryContext);
809 }
810
811
812 /* ----------------------------------------------------------------
813  *                                              CleanupTransaction stuff
814  * ----------------------------------------------------------------
815  */
816
817 /* --------------------------------
818  *              AtCleanup_Memory
819  * --------------------------------
820  */
821 static void
822 AtCleanup_Memory(void)
823 {
824         /*
825          * Now that we're "out" of a transaction, have the system allocate
826          * things in the top memory context instead of per-transaction
827          * contexts.
828          */
829         MemoryContextSwitchTo(TopMemoryContext);
830
831         /*
832          * Release all transaction-local memory.
833          */
834         if (TopTransactionContext != NULL)
835                 MemoryContextDelete(TopTransactionContext);
836         TopTransactionContext = NULL;
837         TransactionCommandContext = NULL;
838 }
839
840
841 /* ----------------------------------------------------------------
842  *                                              interface routines
843  * ----------------------------------------------------------------
844  */
845
846 /* --------------------------------
847  *              StartTransaction
848  *
849  * --------------------------------
850  */
851 static void
852 StartTransaction(void)
853 {
854         TransactionState s = CurrentTransactionState;
855
856         FreeXactSnapshot();
857         XactIsoLevel = DefaultXactIsoLevel;
858
859         /*
860          * Check the current transaction state.  If the transaction system is
861          * switched off, or if we're already in a transaction, do nothing.
862          * We're already in a transaction when the monitor sends a null
863          * command to the backend to flush the comm channel.  This is a hacky
864          * fix to a communications problem, and we keep having to deal with it
865          * here.  We should fix the comm channel code.  mao 080891
866          */
867         if (s->state == TRANS_INPROGRESS)
868                 return;
869
870         /*
871          * set the current transaction state information appropriately during
872          * start processing
873          */
874         s->state = TRANS_START;
875
876         SetReindexProcessing(false);
877
878         /*
879          * generate a new transaction id
880          */
881         s->transactionIdData = GetNewTransactionId();
882
883         XactLockTableInsert(s->transactionIdData);
884
885         /*
886          * initialize current transaction state fields
887          */
888         s->commandId = FirstCommandId;
889         s->scanCommandId = FirstCommandId;
890 #if NOT_USED
891         s->startTime = GetCurrentAbsoluteTime();
892 #endif
893         s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec));
894
895         /*
896          * initialize the various transaction subsystems
897          */
898         AtStart_Memory();
899         AtStart_Cache();
900         AtStart_Locks();
901
902         /*
903          * Tell the trigger manager to we're starting a transaction
904          */
905         DeferredTriggerBeginXact();
906
907         /*
908          * done with start processing, set current transaction state to "in
909          * progress"
910          */
911         s->state = TRANS_INPROGRESS;
912
913 }
914
915 #ifdef NOT_USED
916 /* ---------------
917  * Tell me if we are currently in progress
918  * ---------------
919  */
920 bool
921 CurrentXactInProgress(void)
922 {
923         return CurrentTransactionState->state == TRANS_INPROGRESS;
924 }
925 #endif
926
927 /* --------------------------------
928  *              CommitTransaction
929  *
930  * --------------------------------
931  */
932 static void
933 CommitTransaction(void)
934 {
935         TransactionState s = CurrentTransactionState;
936
937         /*
938          * check the current transaction state
939          */
940         if (s->state != TRANS_INPROGRESS)
941                 elog(WARNING, "CommitTransaction and not in in-progress state");
942
943         /*
944          * Tell the trigger manager that this transaction is about to be
945          * committed. He'll invoke all trigger deferred until XACT before we
946          * really start on committing the transaction.
947          */
948         DeferredTriggerEndXact();
949
950         /* Prevent cancel/die interrupt while cleaning up */
951         HOLD_INTERRUPTS();
952
953         /*
954          * set the current transaction state information appropriately during
955          * the abort processing
956          */
957         s->state = TRANS_COMMIT;
958
959         /*
960          * do commit processing
961          */
962
963         /* handle commit for large objects [ PA, 7/17/98 ] */
964         lo_commit(true);
965
966         /* NOTIFY commit must also come before lower-level cleanup */
967         AtCommit_Notify();
968
969         CloseSequences();
970         AtEOXact_portals();
971
972         /* Here is where we really truly commit. */
973         RecordTransactionCommit();
974
975         /*
976          * Let others know about no transaction in progress by me. Note that
977          * this must be done _before_ releasing locks we hold and _after_
978          * RecordTransactionCommit.
979          *
980          * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by
981          * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if
982          * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0
983          * as running as well or it will see two tuple versions - one deleted
984          * by xid 1 and one inserted by xid 0.  See notes in GetSnapshotData.
985          */
986         if (MyProc != (PROC *) NULL)
987         {
988                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
989                 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
990                 MyProc->xid = InvalidTransactionId;
991                 MyProc->xmin = InvalidTransactionId;
992                 LWLockRelease(SInvalLock);
993         }
994
995         /*
996          * This is all post-commit cleanup.  Note that if an error is raised
997          * here, it's too late to abort the transaction.  This should be just
998          * noncritical resource releasing.
999          */
1000
1001         RelationPurgeLocalRelation(true);
1002         AtEOXact_temp_relations(true);
1003         smgrDoPendingDeletes(true);
1004
1005         AtEOXact_SPI();
1006         AtEOXact_gist();
1007         AtEOXact_hash();
1008         AtEOXact_nbtree();
1009         AtEOXact_rtree();
1010         AtCommit_Cache();
1011         AtCommit_Locks();
1012         AtEOXact_CatCache(true);
1013         AtCommit_Memory();
1014         AtEOXact_Files();
1015
1016         SharedBufferChanged = false;    /* safest place to do it */
1017
1018         /* Count transaction commit in statistics collector */
1019         pgstat_count_xact_commit();
1020
1021         /*
1022          * done with commit processing, set current transaction state back to
1023          * default
1024          */
1025         s->state = TRANS_DEFAULT;
1026
1027         RESUME_INTERRUPTS();
1028 }
1029
1030 /* --------------------------------
1031  *              AbortTransaction
1032  *
1033  * --------------------------------
1034  */
1035 static void
1036 AbortTransaction(void)
1037 {
1038         TransactionState s = CurrentTransactionState;
1039
1040         /* Prevent cancel/die interrupt while cleaning up */
1041         HOLD_INTERRUPTS();
1042
1043         /*
1044          * Release any LW locks we might be holding as quickly as possible.
1045          * (Regular locks, however, must be held till we finish aborting.)
1046          * Releasing LW locks is critical since we might try to grab them
1047          * again while cleaning up!
1048          */
1049         LWLockReleaseAll();
1050
1051         /* Clean up buffer I/O and buffer context locks, too */
1052         AbortBufferIO();
1053         UnlockBuffers();
1054
1055         /*
1056          * Also clean up any open wait for lock, since the lock manager will
1057          * choke if we try to wait for another lock before doing this.
1058          */
1059         LockWaitCancel();
1060
1061         /*
1062          * check the current transaction state
1063          */
1064         if (s->state != TRANS_INPROGRESS)
1065                 elog(WARNING, "AbortTransaction and not in in-progress state");
1066
1067         /*
1068          * set the current transaction state information appropriately during
1069          * the abort processing
1070          */
1071         s->state = TRANS_ABORT;
1072
1073         /*
1074          * Reset user id which might have been changed transiently
1075          */
1076         SetUserId(GetSessionUserId());
1077
1078         /*
1079          * do abort processing
1080          */
1081         DeferredTriggerAbortXact();
1082         lo_commit(false);                       /* 'false' means it's abort */
1083         AtAbort_Notify();
1084         CloseSequences();
1085         AtEOXact_portals();
1086
1087         /* Advertise the fact that we aborted in pg_clog. */
1088         RecordTransactionAbort();
1089
1090         /*
1091          * Let others know about no transaction in progress by me. Note that
1092          * this must be done _before_ releasing locks we hold and _after_
1093          * RecordTransactionAbort.
1094          */
1095         if (MyProc != (PROC *) NULL)
1096         {
1097                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1098                 LWLockAcquire(SInvalLock, LW_EXCLUSIVE);
1099                 MyProc->xid = InvalidTransactionId;
1100                 MyProc->xmin = InvalidTransactionId;
1101                 LWLockRelease(SInvalLock);
1102         }
1103
1104         RelationPurgeLocalRelation(false);
1105         AtEOXact_temp_relations(false);
1106         smgrDoPendingDeletes(false);
1107
1108         AtEOXact_SPI();
1109         AtEOXact_gist();
1110         AtEOXact_hash();
1111         AtEOXact_nbtree();
1112         AtEOXact_rtree();
1113         AtAbort_Cache();
1114         AtEOXact_CatCache(false);
1115         AtAbort_Memory();
1116         AtEOXact_Files();
1117         AtAbort_Locks();
1118
1119         SharedBufferChanged = false;    /* safest place to do it */
1120
1121         /* Count transaction abort in statistics collector */
1122         pgstat_count_xact_rollback();
1123
1124         /*
1125          * State remains TRANS_ABORT until CleanupTransaction().
1126          */
1127         RESUME_INTERRUPTS();
1128 }
1129
1130 /* --------------------------------
1131  *              CleanupTransaction
1132  *
1133  * --------------------------------
1134  */
1135 static void
1136 CleanupTransaction(void)
1137 {
1138         TransactionState s = CurrentTransactionState;
1139
1140         /*
1141          * State should still be TRANS_ABORT from AbortTransaction().
1142          */
1143         if (s->state != TRANS_ABORT)
1144                 elog(FATAL, "CleanupTransaction and not in abort state");
1145
1146         /*
1147          * do abort cleanup processing
1148          */
1149         AtCleanup_Memory();
1150
1151         /*
1152          * done with abort processing, set current transaction state back to
1153          * default
1154          */
1155         s->state = TRANS_DEFAULT;
1156 }
1157
1158 /* --------------------------------
1159  *              StartTransactionCommand
1160  * --------------------------------
1161  */
1162 void
1163 StartTransactionCommand(void)
1164 {
1165         TransactionState s = CurrentTransactionState;
1166
1167         switch (s->blockState)
1168         {
1169                         /*
1170                          * if we aren't in a transaction block, we just do our usual
1171                          * start transaction.
1172                          */
1173                 case TBLOCK_DEFAULT:
1174                         StartTransaction();
1175                         break;
1176
1177                         /*
1178                          * We should never experience this -- if we do it means the
1179                          * BEGIN state was not changed in the previous
1180                          * CommitTransactionCommand().  If we get it, we print a
1181                          * warning and change to the in-progress state.
1182                          */
1183                 case TBLOCK_BEGIN:
1184                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1185                         s->blockState = TBLOCK_INPROGRESS;
1186                         break;
1187
1188                         /*
1189                          * This is the case when are somewhere in a transaction block
1190                          * and about to start a new command.  For now we do nothing
1191                          * but someday we may do command-local resource
1192                          * initialization.
1193                          */
1194                 case TBLOCK_INPROGRESS:
1195                         break;
1196
1197                         /*
1198                          * As with BEGIN, we should never experience this if we do it
1199                          * means the END state was not changed in the previous
1200                          * CommitTransactionCommand().  If we get it, we print a
1201                          * warning, commit the transaction, start a new transaction
1202                          * and change to the default state.
1203                          */
1204                 case TBLOCK_END:
1205                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_END");
1206                         s->blockState = TBLOCK_DEFAULT;
1207                         CommitTransaction();
1208                         StartTransaction();
1209                         break;
1210
1211                         /*
1212                          * Here we are in the middle of a transaction block but one of
1213                          * the commands caused an abort so we do nothing but remain in
1214                          * the abort state.  Eventually we will get to the "END
1215                          * TRANSACTION" which will set things straight.
1216                          */
1217                 case TBLOCK_ABORT:
1218                         break;
1219
1220                         /*
1221                          * This means we somehow aborted and the last call to
1222                          * CommitTransactionCommand() didn't clear the state so we
1223                          * remain in the ENDABORT state and maybe next time we get to
1224                          * CommitTransactionCommand() the state will get reset to
1225                          * default.
1226                          */
1227                 case TBLOCK_ENDABORT:
1228                         elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1229                         break;
1230         }
1231
1232         /*
1233          * We must switch to TransactionCommandContext before returning. This
1234          * is already done if we called StartTransaction, otherwise not.
1235          */
1236         Assert(TransactionCommandContext != NULL);
1237         MemoryContextSwitchTo(TransactionCommandContext);
1238 }
1239
1240 /* --------------------------------
1241  *              CommitTransactionCommand
1242  * --------------------------------
1243  */
1244 void
1245 CommitTransactionCommand(void)
1246 {
1247         TransactionState s = CurrentTransactionState;
1248
1249         switch (s->blockState)
1250         {
1251                         /*
1252                          * if we aren't in a transaction block, we just do our usual
1253                          * transaction commit
1254                          */
1255                 case TBLOCK_DEFAULT:
1256                         CommitTransaction();
1257                         break;
1258
1259                         /*
1260                          * This is the case right after we get a "BEGIN TRANSACTION"
1261                          * command, but the user hasn't done anything else yet, so we
1262                          * change to the "transaction block in progress" state and
1263                          * return.
1264                          */
1265                 case TBLOCK_BEGIN:
1266                         s->blockState = TBLOCK_INPROGRESS;
1267                         break;
1268
1269                         /*
1270                          * This is the case when we have finished executing a command
1271                          * someplace within a transaction block.  We increment the
1272                          * command counter and return.  Someday we may free resources
1273                          * local to the command.
1274                          *
1275                          * That someday is today, at least for memory allocated in
1276                          * TransactionCommandContext. - vadim 03/25/97
1277                          */
1278                 case TBLOCK_INPROGRESS:
1279                         CommandCounterIncrement();
1280                         MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1281                         break;
1282
1283                         /*
1284                          * This is the case when we just got the "END TRANSACTION"
1285                          * statement, so we commit the transaction and go back to the
1286                          * default state.
1287                          */
1288                 case TBLOCK_END:
1289                         CommitTransaction();
1290                         s->blockState = TBLOCK_DEFAULT;
1291                         break;
1292
1293                         /*
1294                          * Here we are in the middle of a transaction block but one of
1295                          * the commands caused an abort so we do nothing but remain in
1296                          * the abort state.  Eventually we will get to the "END
1297                          * TRANSACTION" which will set things straight.
1298                          */
1299                 case TBLOCK_ABORT:
1300                         break;
1301
1302                         /*
1303                          * Here we were in an aborted transaction block which just
1304                          * processed the "END TRANSACTION" command from the user, so
1305                          * clean up and return to the default state.
1306                          */
1307                 case TBLOCK_ENDABORT:
1308                         CleanupTransaction();
1309                         s->blockState = TBLOCK_DEFAULT;
1310                         break;
1311         }
1312 }
1313
1314 /* --------------------------------
1315  *              AbortCurrentTransaction
1316  * --------------------------------
1317  */
1318 void
1319 AbortCurrentTransaction(void)
1320 {
1321         TransactionState s = CurrentTransactionState;
1322
1323         switch (s->blockState)
1324         {
1325                         /*
1326                          * if we aren't in a transaction block, we just do the basic
1327                          * abort & cleanup transaction.
1328                          */
1329                 case TBLOCK_DEFAULT:
1330                         AbortTransaction();
1331                         CleanupTransaction();
1332                         break;
1333
1334                         /*
1335                          * If we are in the TBLOCK_BEGIN it means something screwed up
1336                          * right after reading "BEGIN TRANSACTION" so we enter the
1337                          * abort state.  Eventually an "END TRANSACTION" will fix
1338                          * things.
1339                          */
1340                 case TBLOCK_BEGIN:
1341                         s->blockState = TBLOCK_ABORT;
1342                         AbortTransaction();
1343                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1344                         break;
1345
1346                         /*
1347                          * This is the case when are somewhere in a transaction block
1348                          * which aborted so we abort the transaction and set the ABORT
1349                          * state.  Eventually an "END TRANSACTION" will fix things and
1350                          * restore us to a normal state.
1351                          */
1352                 case TBLOCK_INPROGRESS:
1353                         s->blockState = TBLOCK_ABORT;
1354                         AbortTransaction();
1355                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1356                         break;
1357
1358                         /*
1359                          * Here, the system was fouled up just after the user wanted
1360                          * to end the transaction block so we abort the transaction
1361                          * and put us back into the default state.
1362                          */
1363                 case TBLOCK_END:
1364                         s->blockState = TBLOCK_DEFAULT;
1365                         AbortTransaction();
1366                         CleanupTransaction();
1367                         break;
1368
1369                         /*
1370                          * Here, we are already in an aborted transaction state and
1371                          * are waiting for an "END TRANSACTION" to come along and lo
1372                          * and behold, we abort again! So we just remain in the abort
1373                          * state.
1374                          */
1375                 case TBLOCK_ABORT:
1376                         break;
1377
1378                         /*
1379                          * Here we were in an aborted transaction block which just
1380                          * processed the "END TRANSACTION" command but somehow aborted
1381                          * again.. since we must have done the abort processing, we
1382                          * clean up and return to the default state.
1383                          */
1384                 case TBLOCK_ENDABORT:
1385                         CleanupTransaction();
1386                         s->blockState = TBLOCK_DEFAULT;
1387                         break;
1388         }
1389 }
1390
1391 /* ----------------------------------------------------------------
1392  *                                         transaction block support
1393  * ----------------------------------------------------------------
1394  */
1395 /* --------------------------------
1396  *              BeginTransactionBlock
1397  * --------------------------------
1398  */
1399 void
1400 BeginTransactionBlock(void)
1401 {
1402         TransactionState s = CurrentTransactionState;
1403
1404         /*
1405          * check the current transaction state
1406          */
1407         if (s->blockState != TBLOCK_DEFAULT)
1408                 elog(WARNING, "BEGIN: already a transaction in progress");
1409
1410         /*
1411          * set the current transaction block state information appropriately
1412          * during begin processing
1413          */
1414         s->blockState = TBLOCK_BEGIN;
1415
1416         /*
1417          * do begin processing
1418          */
1419
1420         /*
1421          * done with begin processing, set block state to inprogress
1422          */
1423         s->blockState = TBLOCK_INPROGRESS;
1424 }
1425
1426 /* --------------------------------
1427  *              EndTransactionBlock
1428  * --------------------------------
1429  */
1430 void
1431 EndTransactionBlock(void)
1432 {
1433         TransactionState s = CurrentTransactionState;
1434
1435         /*
1436          * check the current transaction state
1437          */
1438         if (s->blockState == TBLOCK_INPROGRESS)
1439         {
1440                 /*
1441                  * here we are in a transaction block which should commit when we
1442                  * get to the upcoming CommitTransactionCommand() so we set the
1443                  * state to "END".      CommitTransactionCommand() will recognize this
1444                  * and commit the transaction and return us to the default state
1445                  */
1446                 s->blockState = TBLOCK_END;
1447                 return;
1448         }
1449
1450         if (s->blockState == TBLOCK_ABORT)
1451         {
1452                 /*
1453                  * here, we are in a transaction block which aborted and since the
1454                  * AbortTransaction() was already done, we do whatever is needed
1455                  * and change to the special "END ABORT" state.  The upcoming
1456                  * CommitTransactionCommand() will recognise this and then put us
1457                  * back in the default state.
1458                  */
1459                 s->blockState = TBLOCK_ENDABORT;
1460                 return;
1461         }
1462
1463         /*
1464          * here, the user issued COMMIT when not inside a transaction. Issue a
1465          * WARNING and go to abort state.  The upcoming call to
1466          * CommitTransactionCommand() will then put us back into the default
1467          * state.
1468          */
1469         elog(WARNING, "COMMIT: no transaction in progress");
1470         AbortTransaction();
1471         s->blockState = TBLOCK_ENDABORT;
1472 }
1473
1474 /* --------------------------------
1475  *              AbortTransactionBlock
1476  * --------------------------------
1477  */
1478 #ifdef NOT_USED
1479 static void
1480 AbortTransactionBlock(void)
1481 {
1482         TransactionState s = CurrentTransactionState;
1483
1484         /*
1485          * check the current transaction state
1486          */
1487         if (s->blockState == TBLOCK_INPROGRESS)
1488         {
1489                 /*
1490                  * here we were inside a transaction block something screwed up
1491                  * inside the system so we enter the abort state, do the abort
1492                  * processing and then return. We remain in the abort state until
1493                  * we see an END TRANSACTION command.
1494                  */
1495                 s->blockState = TBLOCK_ABORT;
1496                 AbortTransaction();
1497                 return;
1498         }
1499
1500         /*
1501          * here, the user issued ABORT when not inside a transaction. Issue a
1502          * WARNING and go to abort state.  The upcoming call to
1503          * CommitTransactionCommand() will then put us back into the default
1504          * state.
1505          */
1506         elog(WARNING, "ROLLBACK: no transaction in progress");
1507         AbortTransaction();
1508         s->blockState = TBLOCK_ENDABORT;
1509 }
1510 #endif
1511
1512 /* --------------------------------
1513  *              UserAbortTransactionBlock
1514  * --------------------------------
1515  */
1516 void
1517 UserAbortTransactionBlock(void)
1518 {
1519         TransactionState s = CurrentTransactionState;
1520
1521         /*
1522          * if the transaction has already been automatically aborted with an
1523          * error, and the user subsequently types 'abort', allow it.  (the
1524          * behavior is the same as if they had typed 'end'.)
1525          */
1526         if (s->blockState == TBLOCK_ABORT)
1527         {
1528                 s->blockState = TBLOCK_ENDABORT;
1529                 return;
1530         }
1531
1532         if (s->blockState == TBLOCK_INPROGRESS)
1533         {
1534                 /*
1535                  * here we were inside a transaction block and we got an abort
1536                  * command from the user, so we move to the abort state, do the
1537                  * abort processing and then change to the ENDABORT state so we
1538                  * will end up in the default state after the upcoming
1539                  * CommitTransactionCommand().
1540                  */
1541                 s->blockState = TBLOCK_ABORT;
1542                 AbortTransaction();
1543                 s->blockState = TBLOCK_ENDABORT;
1544                 return;
1545         }
1546
1547         /*
1548          * here, the user issued ABORT when not inside a transaction. Issue a
1549          * WARNING and go to abort state.  The upcoming call to
1550          * CommitTransactionCommand() will then put us back into the default
1551          * state.
1552          */
1553         elog(WARNING, "ROLLBACK: no transaction in progress");
1554         AbortTransaction();
1555         s->blockState = TBLOCK_ENDABORT;
1556 }
1557
1558 /* --------------------------------
1559  *              AbortOutOfAnyTransaction
1560  *
1561  * This routine is provided for error recovery purposes.  It aborts any
1562  * active transaction or transaction block, leaving the system in a known
1563  * idle state.
1564  * --------------------------------
1565  */
1566 void
1567 AbortOutOfAnyTransaction(void)
1568 {
1569         TransactionState s = CurrentTransactionState;
1570
1571         /*
1572          * Get out of any low-level transaction
1573          */
1574         switch (s->state)
1575         {
1576                 case TRANS_START:
1577                 case TRANS_INPROGRESS:
1578                 case TRANS_COMMIT:
1579                         /* In a transaction, so clean up */
1580                         AbortTransaction();
1581                         CleanupTransaction();
1582                         break;
1583                 case TRANS_ABORT:
1584                         /* AbortTransaction already done, still need Cleanup */
1585                         CleanupTransaction();
1586                         break;
1587                 case TRANS_DEFAULT:
1588                         /* Not in a transaction, do nothing */
1589                         break;
1590         }
1591
1592         /*
1593          * Now reset the high-level state
1594          */
1595         s->blockState = TBLOCK_DEFAULT;
1596 }
1597
1598 bool
1599 IsTransactionBlock(void)
1600 {
1601         TransactionState s = CurrentTransactionState;
1602
1603         if (s->blockState == TBLOCK_INPROGRESS
1604                 || s->blockState == TBLOCK_ABORT
1605                 || s->blockState == TBLOCK_ENDABORT)
1606                 return true;
1607
1608         return false;
1609 }
1610
1611 void
1612 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1613 {
1614         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1615
1616         if (info == XLOG_XACT_COMMIT)
1617         {
1618                 TransactionIdCommit(record->xl_xid);
1619                 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1620         }
1621         else if (info == XLOG_XACT_ABORT)
1622         {
1623                 TransactionIdAbort(record->xl_xid);
1624                 /* SHOULD REMOVE FILES OF ALL FAILED-TO-BE-CREATED RELATIONS */
1625         }
1626         else
1627                 elog(PANIC, "xact_redo: unknown op code %u", info);
1628 }
1629
1630 void
1631 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1632 {
1633         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1634
1635         if (info == XLOG_XACT_COMMIT)           /* shouldn't be called by XLOG */
1636                 elog(PANIC, "xact_undo: can't undo committed xaction");
1637         else if (info != XLOG_XACT_ABORT)
1638                 elog(PANIC, "xact_redo: unknown op code %u", info);
1639 }
1640
1641 void
1642 xact_desc(char *buf, uint8 xl_info, char *rec)
1643 {
1644         uint8           info = xl_info & ~XLR_INFO_MASK;
1645
1646         if (info == XLOG_XACT_COMMIT)
1647         {
1648                 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1649                 struct tm  *tm = localtime(&xlrec->xtime);
1650
1651                 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1652                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1653                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1654         }
1655         else if (info == XLOG_XACT_ABORT)
1656         {
1657                 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1658                 struct tm  *tm = localtime(&xlrec->xtime);
1659
1660                 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1661                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1662                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1663         }
1664         else
1665                 strcat(buf, "UNKNOWN");
1666 }
1667
1668 void
1669                         XactPushRollback(void (*func) (void *), void *data)
1670 {
1671 #ifdef XLOG_II
1672         if (_RollbackFunc != NULL)
1673                 elog(PANIC, "XactPushRollback: already installed");
1674 #endif
1675
1676         _RollbackFunc = func;
1677         _RollbackData = data;
1678 }
1679
1680 void
1681 XactPopRollback(void)
1682 {
1683         _RollbackFunc = NULL;
1684 }