From bde12919ff153ff702204afe34af1a7a0eaf841a 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
Reviewed-by: Takao Fujii
Discussion: http://postgr.es/m/http://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 1f8bde8443..0e6bf6563f 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3510,6 +3510,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 2d575102ef..7b01e5ca4c 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -168,6 +168,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 fa7dc96ece..047c07d366 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 => '9551', descr => 'equal',
+  oprname => '=', oprcanhash => 't', oprleft => 'xid8', oprright => 'xid8',
+  oprresult => 'bool', oprcom => '=(xid8,xid8)', oprnegate => '<>(xid8,xid8)',
+  oprcode => 'xid8eq', oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '9552', 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 58ea5b982b..3eb35a4eb6 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 => '9553', descr => 'I/O',
+  proname => 'xid8in', prorettype => 'xid8', proargtypes => 'cstring',
+  prosrc => 'xid8in' },
+{ oid => '9554', descr => 'I/O',
+  proname => 'xid8out', prorettype => 'cstring', proargtypes => 'xid8',
+  prosrc => 'xid8out' },
+{ oid => '9555', descr => 'I/O',
+  proname => 'xid8recv', prorettype => 'xid8', proargtypes => 'internal',
+  prosrc => 'xid8recv' },
+{ oid => '9556', 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 => '9557',
+  proname => 'xid8eq', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'xid8 xid8', prosrc => 'xid8eq' },
+{ oid => '9558',
+  proname => 'xid8neq', proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'xid8 xid8', prosrc => 'xid8neq' },
+{ oid => '9559', 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..c6d20b9946 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 => '9560', 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 c19740e5db..dcaf31eb7b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -790,6 +790,8 @@ macaddr8_gt(macaddr8,macaddr8)
 macaddr8_ge(macaddr8,macaddr8)
 macaddr8_ne(macaddr8,macaddr8)
 macaddr8_cmp(macaddr8,macaddr8)
+xid8eq(xid8,xid8)
+xid8neq(xid8,xid8)
 -- restore normal output mode
 \a\t
 -- List of functions used by libpq's fe-lobj.c
-- 
2.23.0

