From 4774346c087a114c4e7d840fd4c52608333ab0aa Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 2 Aug 2019 17:29:34 +1200
Subject: [PATCH 1/2] Add SQL type xid8 to expose FullTransactionId to users.

Similar to xid, but 64 bits wide.  This new type is suitable for use
in various system views and administration functions.

Author: Thomas Munro
Discussion: https://postgr.es/m/https://www.postgresql.org/message-id/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
 src/backend/access/hash/hashvalidate.c   |  3 +
 src/backend/utils/adt/xid.c              | 74 ++++++++++++++++++++++++
 src/fe_utils/print.c                     |  1 +
 src/include/access/transam.h             | 11 ++++
 src/include/catalog/pg_amop.dat          |  4 ++
 src/include/catalog/pg_amproc.dat        |  4 ++
 src/include/catalog/pg_cast.dat          |  4 ++
 src/include/catalog/pg_opclass.dat       |  2 +
 src/include/catalog/pg_operator.dat      |  8 +++
 src/include/catalog/pg_opfamily.dat      |  2 +
 src/include/catalog/pg_proc.dat          | 21 +++++++
 src/include/catalog/pg_type.dat          |  4 ++
 src/include/utils/xid8.h                 | 22 +++++++
 src/test/regress/expected/opr_sanity.out |  2 +
 14 files changed, 162 insertions(+)
 create mode 100644 src/include/utils/xid8.h

diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 9315872751..8ecbd074b2 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -313,6 +313,9 @@ check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
 			(argtype == DATEOID ||
 			 argtype == XIDOID || argtype == CIDOID))
 			 /* okay, allowed use of hashint4() */ ;
+		else if ((funcid == F_HASHINT8 || funcid == F_HASHINT8EXTENDED) &&
+			(argtype == XID8OID))
+			 /* okay, allowed use of hashint8() */ ;
 		else if ((funcid == F_TIMESTAMP_HASH ||
 				  funcid == F_TIMESTAMP_HASH_EXTENDED) &&
 				 argtype == TIMESTAMPTZOID)
diff --git a/src/backend/utils/adt/xid.c b/src/backend/utils/adt/xid.c
index 7853e41865..3d7c249b1c 100644
--- a/src/backend/utils/adt/xid.c
+++ b/src/backend/utils/adt/xid.c
@@ -21,6 +21,7 @@
 #include "access/xact.h"
 #include "libpq/pqformat.h"
 #include "utils/builtins.h"
+#include "utils/xid8.h"
 
 #define PG_GETARG_TRANSACTIONID(n)	DatumGetTransactionId(PG_GETARG_DATUM(n))
 #define PG_RETURN_TRANSACTIONID(x)	return TransactionIdGetDatum(x)
@@ -147,6 +148,79 @@ xidComparator(const void *arg1, const void *arg2)
 	return 0;
 }
 
+Datum
+xid8toxid(PG_FUNCTION_ARGS)
+{
+	FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
+
+	PG_RETURN_TRANSACTIONID(XidFromFullTransactionId(fxid));
+}
+
+Datum
+xid8in(PG_FUNCTION_ARGS)
+{
+	char	   *str = PG_GETARG_CSTRING(0);
+	char	   *end;
+	uint64		value;
+
+	value = pg_strtouint64(str, &end, 10);
+	if (*str == '\0' || *end != '\0')
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("value \"%s\" is invalid for type xid8", str)));
+
+	PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(value));
+}
+
+Datum
+xid8out(PG_FUNCTION_ARGS)
+{
+	FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
+	char	   *result = (char *) palloc(21);
+
+	snprintf(result, 21, UINT64_FORMAT, U64FromFullTransactionId(fxid));
+	PG_RETURN_CSTRING(result);
+}
+
+Datum
+xid8recv(PG_FUNCTION_ARGS)
+{
+	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
+	uint64		value;
+
+	value = (uint64) pq_getmsgint64(buf);
+	PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(value));
+}
+
+Datum
+xid8send(PG_FUNCTION_ARGS)
+{
+	FullTransactionId arg1 = PG_GETARG_FULLTRANSACTIONID(0);
+	StringInfoData buf;
+
+	pq_begintypsend(&buf);
+	pq_sendint64(&buf, (uint64) U64FromFullTransactionId(arg1));
+	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
+}
+
+Datum
+xid8eq(PG_FUNCTION_ARGS)
+{
+	FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+	FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+	PG_RETURN_BOOL(FullTransactionIdEquals(fxid1, fxid2));
+}
+
+Datum
+xid8neq(PG_FUNCTION_ARGS)
+{
+	FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+	FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+	PG_RETURN_BOOL(!FullTransactionIdEquals(fxid1, fxid2));
+}
+
 /*****************************************************************************
  *	 COMMAND IDENTIFIER ROUTINES											 *
  *****************************************************************************/
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index e41f42ea98..9fe7e0a27e 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3512,6 +3512,7 @@ column_type_alignment(Oid ftype)
 		case NUMERICOID:
 		case OIDOID:
 		case XIDOID:
+		case XID8OID:
 		case CIDOID:
 		case CASHOID:
 			align = 'r';
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 33fd052156..787451b3b7 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,6 +47,7 @@
 #define EpochFromFullTransactionId(x)	((uint32) ((x).value >> 32))
 #define XidFromFullTransactionId(x)		((uint32) (x).value)
 #define U64FromFullTransactionId(x)		((x).value)
+#define FullTransactionIdEquals(a, b)	((a).value == (b).value)
 #define FullTransactionIdPrecedes(a, b)	((a).value < (b).value)
 #define FullTransactionIdIsValid(x)		TransactionIdIsValid(XidFromFullTransactionId(x))
 #define InvalidFullTransactionId		FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
@@ -71,6 +72,16 @@ FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
 	return result;
 }
 
+static inline FullTransactionId
+FullTransactionIdFromU64(uint64 value)
+{
+	FullTransactionId result;
+
+	result.value = value;
+
+	return result;
+}
+
 /* advance a transaction ID variable, handling wraparound correctly */
 #define TransactionIdAdvance(dest)	\
 	do { \
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 232557ee81..2a232f1608 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1009,6 +1009,10 @@
 { amopfamily => 'hash/xid_ops', amoplefttype => 'xid', amoprighttype => 'xid',
   amopstrategy => '1', amopopr => '=(xid,xid)', amopmethod => 'hash' },
 
+# xid_ops
+{ amopfamily => 'hash/xid8_ops', amoplefttype => 'xid8', amoprighttype => 'xid8',
+  amopstrategy => '1', amopopr => '=(xid8,xid8)', amopmethod => 'hash' },
+
 # cid_ops
 { amopfamily => 'hash/cid_ops', amoplefttype => 'cid', amoprighttype => 'cid',
   amopstrategy => '1', amopopr => '=(cid,cid)', amopmethod => 'hash' },
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 5e705019b4..fbe1667292 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -339,6 +339,10 @@
   amprocrighttype => 'xid', amprocnum => '1', amproc => 'hashint4' },
 { amprocfamily => 'hash/xid_ops', amproclefttype => 'xid',
   amprocrighttype => 'xid', amprocnum => '2', amproc => 'hashint4extended' },
+{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
+  amprocrighttype => 'xid8', amprocnum => '1', amproc => 'hashint8' },
+{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
+  amprocrighttype => 'xid8', amprocnum => '2', amproc => 'hashint8extended' },
 { amprocfamily => 'hash/cid_ops', amproclefttype => 'cid',
   amprocrighttype => 'cid', amprocnum => '1', amproc => 'hashint4' },
 { amprocfamily => 'hash/cid_ops', amproclefttype => 'cid',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index aabfa7af03..6dc856230f 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -93,6 +93,10 @@
 { castsource => 'bool', casttarget => 'int4', castfunc => 'int4(bool)',
   castcontext => 'e', castmethod => 'f' },
 
+# Allow explicit coercions between xid8 and xid
+{ castsource => 'xid8', casttarget => 'xid', castfunc => 'xid(xid8)',
+  castcontext => 'e', castmethod => 'f' },
+
 # OID category: allow implicit conversion from any integral type (including
 # int8, to support OID literals > 2G) to OID, as well as assignment coercion
 # from OID to int4 or int8.  Similarly for each OID-alias type.  Also allow
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index fdfea85efe..9f951ceddb 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -165,6 +165,8 @@
   opcintype => 'tid' },
 { opcmethod => 'hash', opcname => 'xid_ops', opcfamily => 'hash/xid_ops',
   opcintype => 'xid' },
+{ opcmethod => 'hash', opcname => 'xid8_ops', opcfamily => 'hash/xid8_ops',
+  opcintype => 'xid8' },
 { opcmethod => 'hash', opcname => 'cid_ops', opcfamily => 'hash/cid_ops',
   opcintype => 'cid' },
 { opcmethod => 'hash', opcname => 'tid_ops', opcfamily => 'hash/tid_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 96823cd59b..d98407212a 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -192,6 +192,14 @@
   oprname => '<>', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
   oprnegate => '=(xid,int4)', oprcode => 'xidneqint4', oprrest => 'neqsel',
   oprjoin => 'neqjoinsel' },
+{ oid => '562', descr => 'equal',
+  oprname => '=', oprcanhash => 't', oprleft => 'xid8', oprright => 'xid8',
+  oprresult => 'bool', oprcom => '=(xid8,xid8)', oprnegate => '<>(xid8,xid8)',
+  oprcode => 'xid8eq', oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '563', descr => 'not equal',
+  oprname => '<>', oprleft => 'xid8', oprright => 'xid8',
+  oprresult => 'bool', oprcom => '<>(xid8,xid8)', oprnegate => '=(xid8,xid8)',
+  oprcode => 'xid8neq', oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
 { oid => '388', descr => 'factorial',
   oprname => '!', oprkind => 'r', oprleft => 'int8', oprright => '0',
   oprresult => 'numeric', oprcode => 'numeric_fac' },
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 41e40d657a..45dd8a5701 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -110,6 +110,8 @@
   opfmethod => 'btree', opfname => 'tid_ops' },
 { oid => '2225',
   opfmethod => 'hash', opfname => 'xid_ops' },
+{ oid => '8164',
+  opfmethod => 'hash', opfname => 'xid8_ops' },
 { oid => '2226',
   opfmethod => 'hash', opfname => 'cid_ops' },
 { oid => '2227',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index e6645f139c..22ce0d5d90 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -112,6 +112,18 @@
 { oid => '51', descr => 'I/O',
   proname => 'xidout', prorettype => 'cstring', proargtypes => 'xid',
   prosrc => 'xidout' },
+{ oid => '272', descr => 'I/O',
+  proname => 'xid8in', prorettype => 'xid8', proargtypes => 'cstring',
+  prosrc => 'xid8in' },
+{ oid => '273', descr => 'I/O',
+  proname => 'xid8out', prorettype => 'cstring', proargtypes => 'xid8',
+  prosrc => 'xid8out' },
+{ oid => '560', descr => 'I/O',
+  proname => 'xid8recv', prorettype => 'xid8', proargtypes => 'internal',
+  prosrc => 'xid8recv' },
+{ oid => '561', descr => 'I/O',
+  proname => 'xid8send', prorettype => 'bytea', proargtypes => 'xid8',
+  prosrc => 'xid8send' },
 { oid => '52', descr => 'I/O',
   proname => 'cidin', prorettype => 'cid', proargtypes => 'cstring',
   prosrc => 'cidin' },
@@ -163,6 +175,15 @@
 { oid => '3308',
   proname => 'xidneq', proleakproof => 't', prorettype => 'bool',
   proargtypes => 'xid xid', prosrc => 'xidneq' },
+{ oid => '1179',
+  proname => 'xid8eq', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'xid8 xid8', prosrc => 'xid8eq' },
+{ oid => '1180',
+  proname => 'xid8neq', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'xid8 xid8', prosrc => 'xid8neq' },
+{ oid => '1177', descr => 'convert xid8 to xid',
+  proname => 'xid', prorettype => 'xid', proargtypes => 'xid8',
+  prosrc => 'xid8toxid' },
 { oid => '69',
   proname => 'cideq', proleakproof => 't', prorettype => 'bool',
   proargtypes => 'cid cid', prosrc => 'cideq' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index be49e00114..aaeb4d9d61 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -177,6 +177,10 @@
   typtype => 'p', typcategory => 'P', typinput => 'pg_ddl_command_in',
   typoutput => 'pg_ddl_command_out', typreceive => 'pg_ddl_command_recv',
   typsend => 'pg_ddl_command_send', typalign => 'ALIGNOF_POINTER' },
+{ oid => '270', array_type_oid => '271', descr => 'full transaction id',
+  typname => 'xid8', typlen => '8', typbyval => 'FLOAT8PASSBYVAL',
+  typcategory => 'U', typinput => 'xid8in', typoutput => 'xid8out',
+  typreceive => 'xid8recv', typsend => 'xid8send', typalign => 'd' },
 
 # OIDS 600 - 699
 
diff --git a/src/include/utils/xid8.h b/src/include/utils/xid8.h
new file mode 100644
index 0000000000..3919b2f195
--- /dev/null
+++ b/src/include/utils/xid8.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * xid8.h
+ *	  Header file for the "xid8" ADT.
+ *
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ *
+ * src/include/utils/xid8.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef XID8_H
+#define XID8_H
+
+#include "access/transam.h"
+
+#define DatumGetFullTransactionId(X) (FullTransactionIdFromU64(DatumGetUInt64(X)))
+#define FullTransactionIdGetDatum(X) (UInt64GetDatum(U64FromFullTransactionId(X)))
+#define PG_GETARG_FULLTRANSACTIONID(X) DatumGetFullTransactionId(PG_GETARG_DATUM(X))
+#define PG_RETURN_FULLTRANSACTIONID(X) return FullTransactionIdGetDatum(X)
+
+#endif							/* XID8_H */
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 33c058ff51..157763be59 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -638,6 +638,8 @@ interval_lt(interval,interval)
 interval_le(interval,interval)
 interval_ge(interval,interval)
 interval_gt(interval,interval)
+xid8eq(xid8,xid8)
+xid8neq(xid8,xid8)
 charlt("char","char")
 tidne(tid,tid)
 tideq(tid,tid)
-- 
2.22.0

