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