OSDN Git Service

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