On ons, 2012-01-18 at 14:55 -0500, Tom Lane wrote:
> BTW, it strikes me that maybe the coding should work about like this:
> 
>       if (!TransactionIdIsValid(age_reference_xid))
>       {
>               age_reference_xid = GetTopTransactionIdIfAny();
>               if (!TransactionIdIsValid(age_reference_xid))
>                       age_reference_xid = ReadNewTransactionId();
>       }
>       ... use age_reference_xid to compute result ...
> 
> and of course code somewhere to reset age_reference_xid at end of xact.

How about this patch?

diff --git i/src/backend/access/transam/xact.c w/src/backend/access/transam/xact.c
index 7f412b1..25fbd05 100644
--- i/src/backend/access/transam/xact.c
+++ w/src/backend/access/transam/xact.c
@@ -43,6 +43,7 @@
 #include "storage/procarray.h"
 #include "storage/sinvaladt.h"
 #include "storage/smgr.h"
+#include "utils/builtins.h"
 #include "utils/combocid.h"
 #include "utils/guc.h"
 #include "utils/inval.h"
@@ -1938,6 +1939,7 @@ CommitTransaction(void)
 
 	AtCommit_Notify();
 	AtEOXact_GUC(true, 1);
+	AtEOXact_xid();
 	AtEOXact_SPI(true);
 	AtEOXact_on_commit_actions(true);
 	AtEOXact_Namespace(true);
@@ -2191,6 +2193,7 @@ PrepareTransaction(void)
 
 	/* PREPARE acts the same as COMMIT as far as GUC is concerned */
 	AtEOXact_GUC(true, 1);
+	AtEOXact_xid();
 	AtEOXact_SPI(true);
 	AtEOXact_on_commit_actions(true);
 	AtEOXact_Namespace(true);
@@ -2337,6 +2340,7 @@ AbortTransaction(void)
 		AtEOXact_CatCache(false);
 
 		AtEOXact_GUC(false, 1);
+		AtEOXact_xid();
 		AtEOXact_SPI(false);
 		AtEOXact_on_commit_actions(false);
 		AtEOXact_Namespace(false);
diff --git i/src/backend/utils/adt/xid.c w/src/backend/utils/adt/xid.c
index 1829b82..9cf6256 100644
--- i/src/backend/utils/adt/xid.c
+++ w/src/backend/utils/adt/xid.c
@@ -86,22 +86,45 @@ xideq(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2));
 }
 
+
 /*
  *		xid_age			- compute age of an XID (relative to current xact)
+ *
+ * We don't want to allocate a new transaction ID just for calling age(),
+ * because it should be avoided in an otherwise read-only transaction, and to
+ * make this work under hot standby.  But calling ReadNewTransactionId() would
+ * make this function volatile (instead of stable), so we cache the reference
+ * point for the duration of the transaction.
  */
+
+static TransactionId age_reference_xid = InvalidTransactionId;
+
 Datum
 xid_age(PG_FUNCTION_ARGS)
 {
 	TransactionId xid = PG_GETARG_TRANSACTIONID(0);
-	TransactionId now = GetTopTransactionId();
+
+	if (!TransactionIdIsValid(age_reference_xid))
+	{
+		age_reference_xid = GetTopTransactionIdIfAny();
+		if (!TransactionIdIsValid(age_reference_xid))
+			age_reference_xid = ReadNewTransactionId();
+	}
 
 	/* Permanent XIDs are always infinitely old */
 	if (!TransactionIdIsNormal(xid))
 		PG_RETURN_INT32(INT_MAX);
 
-	PG_RETURN_INT32((int32) (now - xid));
+	PG_RETURN_INT32((int32) (age_reference_xid - xid));
 }
 
+void
+AtEOXact_xid(void)
+{
+	age_reference_xid = InvalidTransactionId;
+}
+
+
 /*
  * xidComparator
  *		qsort comparison function for XIDs
diff --git i/src/include/utils/builtins.h w/src/include/utils/builtins.h
index f246f11..b7906b8 100644
--- i/src/include/utils/builtins.h
+++ w/src/include/utils/builtins.h
@@ -795,6 +795,7 @@ extern Datum xidrecv(PG_FUNCTION_ARGS);
 extern Datum xidsend(PG_FUNCTION_ARGS);
 extern Datum xideq(PG_FUNCTION_ARGS);
 extern Datum xid_age(PG_FUNCTION_ARGS);
+extern void AtEOXact_xid(void);
 extern int	xidComparator(const void *arg1, const void *arg2);
 extern Datum cidin(PG_FUNCTION_ARGS);
 extern Datum cidout(PG_FUNCTION_ARGS);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to