aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/src/sgml/datatype.sgml7
-rw-r--r--src/backend/access/hash/hashvalidate.c3
-rw-r--r--src/backend/utils/adt/xid.c116
-rw-r--r--src/fe_utils/print.c1
-rw-r--r--src/include/access/transam.h14
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_amop.dat22
-rw-r--r--src/include/catalog/pg_amproc.dat8
-rw-r--r--src/include/catalog/pg_cast.dat4
-rw-r--r--src/include/catalog/pg_opclass.dat4
-rw-r--r--src/include/catalog/pg_operator.dat25
-rw-r--r--src/include/catalog/pg_opfamily.dat4
-rw-r--r--src/include/catalog/pg_proc.dat36
-rw-r--r--src/include/catalog/pg_type.dat4
-rw-r--r--src/include/utils/xid8.h22
-rw-r--r--src/test/regress/expected/opr_sanity.out7
-rw-r--r--src/test/regress/expected/xid.out136
-rw-r--r--src/test/regress/parallel_schedule2
-rw-r--r--src/test/regress/serial_schedule1
-rw-r--r--src/test/regress/sql/xid.sql48
20 files changed, 464 insertions, 2 deletions
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 03971822c29..89f3a7c1196 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4517,6 +4517,10 @@ INSERT INTO mytable VALUES(-1); -- fails
</indexterm>
<indexterm zone="datatype-oid">
+ <primary>xid8</primary>
+ </indexterm>
+
+ <indexterm zone="datatype-oid">
<primary>cid</primary>
</indexterm>
@@ -4719,6 +4723,9 @@ SELECT * FROM pg_attribute
Another identifier type used by the system is <type>xid</type>, or transaction
(abbreviated <abbrev>xact</abbrev>) identifier. This is the data type of the system columns
<structfield>xmin</structfield> and <structfield>xmax</structfield>. Transaction identifiers are 32-bit quantities.
+ In some contexts, a 64-bit variant <type>xid8</type> is used. Unlike
+ <type>xid</type> values, <type>xid8</type> values increase strictly
+ monotonically and cannot be reused in the lifetime of a database cluster.
</para>
<para>
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 7b08ed53543..b3d1367fecb 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -317,6 +317,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 db6fc9dd6b8..20389aff1d1 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,121 @@ 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);
+
+ PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(pg_strtouint64(str, NULL, 0)));
+}
+
+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
+xid8ne(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ PG_RETURN_BOOL(!FullTransactionIdEquals(fxid1, fxid2));
+}
+
+Datum
+xid8lt(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ PG_RETURN_BOOL(FullTransactionIdPrecedes(fxid1, fxid2));
+}
+
+Datum
+xid8gt(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ PG_RETURN_BOOL(FullTransactionIdFollows(fxid1, fxid2));
+}
+
+Datum
+xid8le(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ PG_RETURN_BOOL(FullTransactionIdPrecedesOrEquals(fxid1, fxid2));
+}
+
+Datum
+xid8ge(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ PG_RETURN_BOOL(FullTransactionIdFollowsOrEquals(fxid1, fxid2));
+}
+
+Datum
+xid8cmp(PG_FUNCTION_ARGS)
+{
+ FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0);
+ FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1);
+
+ if (FullTransactionIdFollows(fxid1, fxid2))
+ PG_RETURN_INT32(1);
+ else if (FullTransactionIdEquals(fxid1, fxid2))
+ PG_RETURN_INT32(0);
+ else
+ PG_RETURN_INT32(-1);
+}
+
/*****************************************************************************
* COMMAND IDENTIFIER ROUTINES *
*****************************************************************************/
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 06096cc86cb..66a50f183f5 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3509,6 +3509,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 6a947b958b0..9a808f64ebe 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -47,7 +47,11 @@
#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 FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value)
+#define FullTransactionIdFollows(a, b) ((a).value > (b).value)
+#define FullTransactionIdFollowsOrEquals(a, b) ((a).value >= (b).value)
#define FullTransactionIdIsValid(x) TransactionIdIsValid(XidFromFullTransactionId(x))
#define InvalidFullTransactionId FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
@@ -71,6 +75,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/catversion.h b/src/include/catalog/catversion.h
index 6d91fa2bde3..4201790ec48 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202004022
+#define CATALOG_VERSION_NO 202004061
#endif
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8b..b5dfaad9ec0 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -180,6 +180,24 @@
{ amopfamily => 'btree/oid_ops', amoplefttype => 'oid', amoprighttype => 'oid',
amopstrategy => '5', amopopr => '>(oid,oid)', amopmethod => 'btree' },
+# btree xid8_ops
+
+{ amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
+ amoprighttype => 'xid8', amopstrategy => '1', amopopr => '<(xid8,xid8)',
+ amopmethod => 'btree' },
+{ amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
+ amoprighttype => 'xid8', amopstrategy => '2', amopopr => '<=(xid8,xid8)',
+ amopmethod => 'btree' },
+{ amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
+ amoprighttype => 'xid8', amopstrategy => '3', amopopr => '=(xid8,xid8)',
+ amopmethod => 'btree' },
+{ amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
+ amoprighttype => 'xid8', amopstrategy => '4', amopopr => '>=(xid8,xid8)',
+ amopmethod => 'btree' },
+{ amopfamily => 'btree/xid8_ops', amoplefttype => 'xid8',
+ amoprighttype => 'xid8', amopstrategy => '5', amopopr => '>(xid8,xid8)',
+ amopmethod => 'btree' },
+
# btree tid_ops
{ amopfamily => 'btree/tid_ops', amoplefttype => 'tid', amoprighttype => 'tid',
@@ -1009,6 +1027,10 @@
{ amopfamily => 'hash/xid_ops', amoplefttype => 'xid', amoprighttype => 'xid',
amopstrategy => '1', amopopr => '=(xid,xid)', amopmethod => 'hash' },
+# xid8_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 cef63b2a716..37b580883fc 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -287,6 +287,10 @@
amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
{ amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
+{ amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
+ amprocrighttype => 'xid8', amprocnum => '1', amproc => 'xid8cmp' },
+{ amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
+ amprocrighttype => 'xid8', amprocnum => '4', amproc => 'btequalimage' },
# hash
{ amprocfamily => 'hash/bpchar_ops', amproclefttype => 'bpchar',
@@ -399,6 +403,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 01c5328dddb..5a58f50fbb2 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 ab2f50c9eb3..f2342bb328c 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -168,6 +168,10 @@
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 => 'btree', opcname => 'xid8_ops', opcfamily => 'btree/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 65c7fedf237..00ada7e48fc 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -193,6 +193,31 @@
oprname => '<>', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
oprnegate => '=(xid,int4)', oprcode => 'xidneqint4', oprrest => 'neqsel',
oprjoin => 'neqjoinsel' },
+{ oid => '9418', descr => 'equal',
+ oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'xid8',
+ oprright => 'xid8', oprresult => 'bool', oprcom => '=(xid8,xid8)',
+ oprnegate => '<>(xid8,xid8)', oprcode => 'xid8eq', oprrest => 'eqsel',
+ oprjoin => 'eqjoinsel' },
+{ oid => '9422', descr => 'not equal',
+ oprname => '<>', oprleft => 'xid8', oprright => 'xid8',
+ oprresult => 'bool', oprcom => '<>(xid8,xid8)', oprnegate => '=(xid8,xid8)',
+ oprcode => 'xid8ne', oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '9432', descr => 'less than',
+ oprname => '<', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool',
+ oprcom => '>(xid8,xid8)', oprnegate => '>=(xid8,xid8)', oprcode => 'xid8lt',
+ oprrest => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '9433', descr => 'greater than',
+ oprname => '>', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool',
+ oprcom => '<(xid8,xid8)', oprnegate => '<=(xid8,xid8)', oprcode => 'xid8gt',
+ oprrest => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '9434', descr => 'less than or equal',
+ oprname => '<=', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool',
+ oprcom => '>=(xid8,xid8)', oprnegate => '>(xid8,xid8)', oprcode => 'xid8le',
+ oprrest => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '9435', descr => 'greater than or equal',
+ oprname => '>=', oprleft => 'xid8', oprright => 'xid8', oprresult => 'bool',
+ oprcom => '<=(xid8,xid8)', oprnegate => '<(xid8,xid8)', oprcode => 'xid8ge',
+ oprrest => 'scalargesel', oprjoin => 'scalargejoinsel' },
{ 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 26227df2165..4004138d778 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -110,6 +110,10 @@
opfmethod => 'btree', opfname => 'tid_ops' },
{ oid => '2225',
opfmethod => 'hash', opfname => 'xid_ops' },
+{ oid => '8164',
+ opfmethod => 'hash', opfname => 'xid8_ops' },
+{ oid => '9322',
+ opfmethod => 'btree', 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 a649e44d089..6be7e4f1571 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 => '9420', 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,30 @@
{ 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 => 'xid8ne', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8ne' },
+{ oid => '8295',
+ proname => 'xid8lt', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8lt' },
+{ oid => '8296',
+ proname => 'xid8gt', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8gt' },
+{ oid => '8297',
+ proname => 'xid8le', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8le' },
+{ oid => '8298',
+ proname => 'xid8ge', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8ge' },
+{ oid => '9912', descr => 'less-equal-greater',
+ proname => 'xid8cmp', proleakproof => 't', prorettype => 'int4',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8cmp' },
+{ oid => '9421', 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 2e6110e3f2c..a1f441b8daa 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 => '9419', 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 00000000000..288e62de9c3
--- /dev/null
+++ b/src/include/utils/xid8.h
@@ -0,0 +1,22 @@
+/*-------------------------------------------------------------------------
+ *
+ * xid8.h
+ * Header file for the "xid8" ADT.
+ *
+ * Copyright (c) 2020, 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 2efd7d7ec74..0c03afe53d2 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -832,6 +832,13 @@ macaddr8_gt(macaddr8,macaddr8)
macaddr8_ge(macaddr8,macaddr8)
macaddr8_ne(macaddr8,macaddr8)
macaddr8_cmp(macaddr8,macaddr8)
+xid8lt(xid8,xid8)
+xid8gt(xid8,xid8)
+xid8le(xid8,xid8)
+xid8ge(xid8,xid8)
+xid8eq(xid8,xid8)
+xid8ne(xid8,xid8)
+xid8cmp(xid8,xid8)
-- restore normal output mode
\a\t
-- List of functions used by libpq's fe-lobj.c
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
new file mode 100644
index 00000000000..e9e1fa82591
--- /dev/null
+++ b/src/test/regress/expected/xid.out
@@ -0,0 +1,136 @@
+-- xid and xid8
+-- values in range, in octal, decimal, hex
+select '010'::xid,
+ '42'::xid,
+ '0xffffffff'::xid,
+ '-1'::xid,
+ '010'::xid8,
+ '42'::xid8,
+ '0xffffffffffffffff'::xid8,
+ '-1'::xid8;
+ xid | xid | xid | xid | xid8 | xid8 | xid8 | xid8
+-----+-----+------------+------------+------+------+----------------------+----------------------
+ 8 | 42 | 4294967295 | 4294967295 | 8 | 42 | 18446744073709551615 | 18446744073709551615
+(1 row)
+
+-- garbage values are not yet rejected (perhaps they should be)
+select ''::xid;
+ xid
+-----
+ 0
+(1 row)
+
+select 'asdf'::xid;
+ xid
+-----
+ 0
+(1 row)
+
+select ''::xid8;
+ xid8
+------
+ 0
+(1 row)
+
+select 'asdf'::xid8;
+ xid8
+------
+ 0
+(1 row)
+
+-- equality
+select '1'::xid = '1'::xid;
+ ?column?
+----------
+ t
+(1 row)
+
+select '1'::xid != '1'::xid;
+ ?column?
+----------
+ f
+(1 row)
+
+select '1'::xid8 = '1'::xid8;
+ ?column?
+----------
+ t
+(1 row)
+
+select '1'::xid8 != '1'::xid8;
+ ?column?
+----------
+ f
+(1 row)
+
+-- conversion
+select '1'::xid = '1'::xid8::xid;
+ ?column?
+----------
+ t
+(1 row)
+
+select '1'::xid != '1'::xid8::xid;
+ ?column?
+----------
+ f
+(1 row)
+
+-- we don't want relational operators for xid, due to use of modular arithmetic
+select '1'::xid < '2'::xid;
+ERROR: operator does not exist: xid < xid
+LINE 1: select '1'::xid < '2'::xid;
+ ^
+HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
+select '1'::xid <= '2'::xid;
+ERROR: operator does not exist: xid <= xid
+LINE 1: select '1'::xid <= '2'::xid;
+ ^
+HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
+select '1'::xid > '2'::xid;
+ERROR: operator does not exist: xid > xid
+LINE 1: select '1'::xid > '2'::xid;
+ ^
+HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
+select '1'::xid >= '2'::xid;
+ERROR: operator does not exist: xid >= xid
+LINE 1: select '1'::xid >= '2'::xid;
+ ^
+HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
+-- we want them for xid8 though
+select '1'::xid8 < '2'::xid8, '2'::xid8 < '2'::xid8, '2'::xid8 < '1'::xid8;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ t | f | f
+(1 row)
+
+select '1'::xid8 <= '2'::xid8, '2'::xid8 <= '2'::xid8, '2'::xid8 <= '1'::xid8;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ t | t | f
+(1 row)
+
+select '1'::xid8 > '2'::xid8, '2'::xid8 > '2'::xid8, '2'::xid8 > '1'::xid8;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ f | f | t
+(1 row)
+
+select '1'::xid8 >= '2'::xid8, '2'::xid8 >= '2'::xid8, '2'::xid8 >= '1'::xid8;
+ ?column? | ?column? | ?column?
+----------+----------+----------
+ f | t | t
+(1 row)
+
+-- we also have a 3way compare for btrees
+select xid8cmp('1', '2'), xid8cmp('2', '2'), xid8cmp('2', '1');
+ xid8cmp | xid8cmp | xid8cmp
+---------+---------+---------
+ -1 | 0 | 1
+(1 row)
+
+-- xid8 has btree and hash opclasses
+create table xid8_t1 (x xid8);
+create index on xid8_t1 using btree(x);
+create index on xid8_t1 using hash(x);
+drop table xid8_t1;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index a741e89616a..95f1925072b 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -20,7 +20,7 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid
# ----------
# Another group of parallel tests
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 1a6821ca46b..8ba41362202 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -10,6 +10,7 @@ test: int2
test: int4
test: int8
test: oid
+test: xid
test: float4
test: float8
test: bit
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
new file mode 100644
index 00000000000..a4fbca51766
--- /dev/null
+++ b/src/test/regress/sql/xid.sql
@@ -0,0 +1,48 @@
+-- xid and xid8
+
+-- values in range, in octal, decimal, hex
+select '010'::xid,
+ '42'::xid,
+ '0xffffffff'::xid,
+ '-1'::xid,
+ '010'::xid8,
+ '42'::xid8,
+ '0xffffffffffffffff'::xid8,
+ '-1'::xid8;
+
+-- garbage values are not yet rejected (perhaps they should be)
+select ''::xid;
+select 'asdf'::xid;
+select ''::xid8;
+select 'asdf'::xid8;
+
+-- equality
+select '1'::xid = '1'::xid;
+select '1'::xid != '1'::xid;
+select '1'::xid8 = '1'::xid8;
+select '1'::xid8 != '1'::xid8;
+
+-- conversion
+select '1'::xid = '1'::xid8::xid;
+select '1'::xid != '1'::xid8::xid;
+
+-- we don't want relational operators for xid, due to use of modular arithmetic
+select '1'::xid < '2'::xid;
+select '1'::xid <= '2'::xid;
+select '1'::xid > '2'::xid;
+select '1'::xid >= '2'::xid;
+
+-- we want them for xid8 though
+select '1'::xid8 < '2'::xid8, '2'::xid8 < '2'::xid8, '2'::xid8 < '1'::xid8;
+select '1'::xid8 <= '2'::xid8, '2'::xid8 <= '2'::xid8, '2'::xid8 <= '1'::xid8;
+select '1'::xid8 > '2'::xid8, '2'::xid8 > '2'::xid8, '2'::xid8 > '1'::xid8;
+select '1'::xid8 >= '2'::xid8, '2'::xid8 >= '2'::xid8, '2'::xid8 >= '1'::xid8;
+
+-- we also have a 3way compare for btrees
+select xid8cmp('1', '2'), xid8cmp('2', '2'), xid8cmp('2', '1');
+
+-- xid8 has btree and hash opclasses
+create table xid8_t1 (x xid8);
+create index on xid8_t1 using btree(x);
+create index on xid8_t1 using hash(x);
+drop table xid8_t1;