77 * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
88 * Portions Copyright (c) 1994, Regents of the University of California
99 *
10- * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.89 2002/03/06 06:09:22 momjian Exp $
10+ * $Header: /cvsroot/pgsql/src/backend/access/transam/xlog.c,v 1.90 2002/03/15 19:20:30 tgl Exp $
1111 *
1212 *-------------------------------------------------------------------------
1313 */
@@ -131,27 +131,36 @@ boolInRecovery = false;
131131
132132/*
133133 * MyLastRecPtr points to the start of the last XLOG record inserted by the
134- * current transaction. If MyLastRecPtr.xrecoff == 0, thenwe are not in
135- *a transaction or the transaction has not yetmade anyloggable changes .
134+ * current transaction. If MyLastRecPtr.xrecoff == 0, thenthe current
135+ *xact hasn't yetinserted anytransaction-controlled XLOG records .
136136 *
137137 * Note that XLOG records inserted outside transaction control are not
138- * reflected into MyLastRecPtr.
138+ * reflected into MyLastRecPtr. They do, however, cause MyXactMadeXLogEntry
139+ * to be set true. The latter can be used to test whether the current xact
140+ * made any loggable changes (including out-of-xact changes, such as
141+ * sequence updates).
139142 */
140143XLogRecPtr MyLastRecPtr = {0 ,0 };
141144
145+ bool MyXactMadeXLogEntry = false;
146+
142147/*
143148 * ProcLastRecPtr points to the start of the last XLOG record inserted by the
144149 * current backend. It is updated for all inserts, transaction-controlled
145- * or not.
150+ * or not. ProcLastRecEnd is similar but points to end+1 of last record.
146151 */
147152static XLogRecPtr ProcLastRecPtr = {0 ,0 };
148153
154+ XLogRecPtr ProcLastRecEnd = {0 ,0 };
155+
149156/*
150157 * RedoRecPtr is this backend's local copy of the REDO record pointer
151158 * (which is almost but not quite the same as a pointer to the most recent
152159 * CHECKPOINT record).We update this from the shared-memory copy,
153160 * XLogCtl->Insert.RedoRecPtr, whenever we can safely do so (ie, when we
154- * hold the Insert lock). See XLogInsert for details.
161+ * hold the Insert lock). See XLogInsert for details. We are also allowed
162+ * to update from XLogCtl->Insert.RedoRecPtr if we hold the info_lck;
163+ * see GetRedoRecPtr.
155164 */
156165static XLogRecPtr RedoRecPtr ;
157166
@@ -272,7 +281,8 @@ typedef struct XLogCtlData
272281StartUpID ThisStartUpID ;
273282
274283/* This value is not protected by *any* lock... */
275- XLogRecPtr RedoRecPtr ;/* see SetRedoRecPtr/GetRedoRecPtr */
284+ /* see SetSavedRedoRecPtr/GetSavedRedoRecPtr */
285+ XLogRecPtr SavedRedoRecPtr ;
276286
277287slock_t info_lck ;/* locks shared LogwrtRqst/LogwrtResult */
278288}XLogCtlData ;
@@ -777,6 +787,7 @@ begin:;
777787MyLastRecPtr = RecPtr ;
778788ProcLastRecPtr = RecPtr ;
779789Insert -> PrevRecord = RecPtr ;
790+ MyXactMadeXLogEntry = true;
780791
781792Insert -> currpos += SizeOfXLogRecord ;
782793freespace -= SizeOfXLogRecord ;
@@ -855,6 +866,8 @@ begin:;
855866SpinLockRelease_NoHoldoff (& xlogctl -> info_lck );
856867}
857868
869+ ProcLastRecEnd = RecPtr ;
870+
858871END_CRIT_SECTION ();
859872
860873return (RecPtr );
@@ -2538,7 +2551,7 @@ StartupXLOG(void)
25382551
25392552ThisStartUpID = checkPoint .ThisStartUpID ;
25402553RedoRecPtr = XLogCtl -> Insert .RedoRecPtr =
2541- XLogCtl -> RedoRecPtr = checkPoint .redo ;
2554+ XLogCtl -> SavedRedoRecPtr = checkPoint .redo ;
25422555
25432556if (XLByteLT (RecPtr ,checkPoint .redo ))
25442557elog (PANIC ,"invalid redo in checkpoint record" );
@@ -2824,32 +2837,47 @@ void
28242837SetThisStartUpID (void )
28252838{
28262839ThisStartUpID = XLogCtl -> ThisStartUpID ;
2827- RedoRecPtr = XLogCtl -> RedoRecPtr ;
2840+ RedoRecPtr = XLogCtl -> SavedRedoRecPtr ;
28282841}
28292842
28302843/*
28312844 * CheckPoint process called by postmaster saves copy of new RedoRecPtr
2832- * in shmem (using SetRedoRecPtr).When checkpointer completes, postmaster
2833- * calls GetRedoRecPtr to update its own copy of RedoRecPtr, so that
2834- * subsequently-spawned backends will start out with a reasonably up-to-date
2835- * local RedoRecPtr. Since these operations are not protected by any lock
2836- * and copying an XLogRecPtr isn't atomic, it's unsafe to use either of these
2837- * routines at other times!
2838- *
2839- * Note: once spawned, a backend must update its local RedoRecPtr from
2840- * XLogCtl->Insert.RedoRecPtr while holding the insert lock. This is
2841- * done in XLogInsert().
2845+ * in shmem (using SetSavedRedoRecPtr). When checkpointer completes,
2846+ * postmaster calls GetSavedRedoRecPtr to update its own copy of RedoRecPtr,
2847+ * so that subsequently-spawned backends will start out with a reasonably
2848+ * up-to-date local RedoRecPtr. Since these operations are not protected by
2849+ * any lock and copying an XLogRecPtr isn't atomic, it's unsafe to use either
2850+ * of these routines at other times!
28422851 */
28432852void
2844- SetRedoRecPtr (void )
2853+ SetSavedRedoRecPtr (void )
28452854{
2846- XLogCtl -> RedoRecPtr = RedoRecPtr ;
2855+ XLogCtl -> SavedRedoRecPtr = RedoRecPtr ;
28472856}
28482857
28492858void
2859+ GetSavedRedoRecPtr (void )
2860+ {
2861+ RedoRecPtr = XLogCtl -> SavedRedoRecPtr ;
2862+ }
2863+
2864+ /*
2865+ * Once spawned, a backend may update its local RedoRecPtr from
2866+ * XLogCtl->Insert.RedoRecPtr; it must hold the insert lock or info_lck
2867+ * to do so. This is done in XLogInsert() or GetRedoRecPtr().
2868+ */
2869+ XLogRecPtr
28502870GetRedoRecPtr (void )
28512871{
2852- RedoRecPtr = XLogCtl -> RedoRecPtr ;
2872+ /* use volatile pointer to prevent code rearrangement */
2873+ volatile XLogCtlData * xlogctl = XLogCtl ;
2874+
2875+ SpinLockAcquire_NoHoldoff (& xlogctl -> info_lck );
2876+ Assert (XLByteLE (RedoRecPtr ,xlogctl -> Insert .RedoRecPtr ));
2877+ RedoRecPtr = xlogctl -> Insert .RedoRecPtr ;
2878+ SpinLockRelease_NoHoldoff (& xlogctl -> info_lck );
2879+
2880+ return RedoRecPtr ;
28532881}
28542882
28552883/*
@@ -2862,6 +2890,7 @@ ShutdownXLOG(void)
28622890
28632891/* suppress in-transaction check in CreateCheckPoint */
28642892MyLastRecPtr .xrecoff = 0 ;
2893+ MyXactMadeXLogEntry = false;
28652894
28662895CritSectionCount ++ ;
28672896CreateDummyCaches ();
@@ -2886,7 +2915,7 @@ CreateCheckPoint(bool shutdown)
28862915uint32 _logId ;
28872916uint32 _logSeg ;
28882917
2889- if (MyLastRecPtr . xrecoff != 0 )
2918+ if (MyXactMadeXLogEntry )
28902919elog (ERROR ,"CreateCheckPoint: cannot be called inside transaction block" );
28912920
28922921/*
@@ -2972,9 +3001,16 @@ CreateCheckPoint(bool shutdown)
29723001
29733002/*
29743003 * Here we update the shared RedoRecPtr for future XLogInsert calls;
2975- * this must be done while holding the insert lock.
3004+ * this must be done while holding the insert lock AND the info_lck .
29763005 */
2977- RedoRecPtr = XLogCtl -> Insert .RedoRecPtr = checkPoint .redo ;
3006+ {
3007+ /* use volatile pointer to prevent code rearrangement */
3008+ volatile XLogCtlData * xlogctl = XLogCtl ;
3009+
3010+ SpinLockAcquire_NoHoldoff (& xlogctl -> info_lck );
3011+ RedoRecPtr = xlogctl -> Insert .RedoRecPtr = checkPoint .redo ;
3012+ SpinLockRelease_NoHoldoff (& xlogctl -> info_lck );
3013+ }
29783014
29793015/*
29803016 * Get UNDO record ptr - this is oldest of PROC->logRec values. We do