Should we add xid_current() or a int8->xid cast?
Hi,
we have txid_current(), which returns an int8. But there's no convenient
way to convert that to type 'xid'. Which is fairly inconvenient, given
that we expose xids in various places.
My current need for this was just a regression test to make sure that
system columns (xmin/xmax in particular) don't get broken again for ON
CONFLICT. But I've needed this before in other scenarios - e.g. age(xid)
can be useful to figure out how old a transaction is, but age() doesn't
work with txid_current()'s return value.
Seems easiest to just add xid_current(), or add a cast from int8 to xid
(probably explicit?) that handles the wraparound logic correctly?
Greetings,
Andres Freund
On Thu, Jul 25, 2019 at 12:06 PM Andres Freund <andres@anarazel.de> wrote:
we have txid_current(), which returns an int8. But there's no convenient
way to convert that to type 'xid'. Which is fairly inconvenient, given
that we expose xids in various places.My current need for this was just a regression test to make sure that
system columns (xmin/xmax in particular) don't get broken again for ON
CONFLICT. But I've needed this before in other scenarios - e.g. age(xid)
can be useful to figure out how old a transaction is, but age() doesn't
work with txid_current()'s return value.Seems easiest to just add xid_current(), or add a cast from int8 to xid
(probably explicit?) that handles the wraparound logic correctly?
Yeah, I was wondering about that. int8 isn't really the right type,
since FullTransactionId is unsigned. If we had a SQL type for 64 bit
xids, it should be convertible to xid, and the reverse conversion
should require a more complicated dance. Of course we can't casually
change txid_current() without annoying people who are using it, so
perhaps if we invent a new SQL type we should also make a new function
that returns it.
--
Thomas Munro
https://enterprisedb.com
Hi,
On 2019-07-25 12:20:58 +1200, Thomas Munro wrote:
On Thu, Jul 25, 2019 at 12:06 PM Andres Freund <andres@anarazel.de> wrote:
we have txid_current(), which returns an int8. But there's no convenient
way to convert that to type 'xid'. Which is fairly inconvenient, given
that we expose xids in various places.My current need for this was just a regression test to make sure that
system columns (xmin/xmax in particular) don't get broken again for ON
CONFLICT. But I've needed this before in other scenarios - e.g. age(xid)
can be useful to figure out how old a transaction is, but age() doesn't
work with txid_current()'s return value.Seems easiest to just add xid_current(), or add a cast from int8 to xid
(probably explicit?) that handles the wraparound logic correctly?Yeah, I was wondering about that. int8 isn't really the right type,
since FullTransactionId is unsigned.
For now that doesn't seem that big an impediment...
If we had a SQL type for 64 bit xids, it should be convertible to xid,
and the reverse conversion should require a more complicated dance.
Of course we can't casually change txid_current() without annoying
people who are using it, so perhaps if we invent a new SQL type we
should also make a new function that returns it.
Possibly we could add a fullxid or xid8, xid64, pg_xid64, ... type, and
have an implicit cast to int8?
Greetings,
Andres Freund
Andres Freund <andres@anarazel.de> writes:
On 2019-07-25 12:20:58 +1200, Thomas Munro wrote:
On Thu, Jul 25, 2019 at 12:06 PM Andres Freund <andres@anarazel.de> wrote:
Seems easiest to just add xid_current(), or add a cast from int8 to xid
(probably explicit?) that handles the wraparound logic correctly?
Yeah, I was wondering about that. int8 isn't really the right type,
since FullTransactionId is unsigned.
For now that doesn't seem that big an impediment...
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.
I think an explicit cast is a reasonable thing to add, though.
regards, tom lane
Hi,
On 2019-07-24 20:34:39 -0400, Tom Lane wrote:
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.
I assume Thomas was thinking more of another bespoke type like xid, just
wider. There's some notational advantage in not being able to
immediately do math etc on xids.
- Andres
Andres Freund <andres@anarazel.de> writes:
On 2019-07-24 20:34:39 -0400, Tom Lane wrote:
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.
I assume Thomas was thinking more of another bespoke type like xid, just
wider. There's some notational advantage in not being able to
immediately do math etc on xids.
Well, we could invent an xid8 type if we want, just don't try to make
it part of the numeric hierarchy (as indeed xid isn't).
regards, tom lane
On Thu, Jul 25, 2019 at 12:42 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andres Freund <andres@anarazel.de> writes:
On 2019-07-24 20:34:39 -0400, Tom Lane wrote:
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.I assume Thomas was thinking more of another bespoke type like xid, just
wider. There's some notational advantage in not being able to
immediately do math etc on xids.Well, we could invent an xid8 type if we want, just don't try to make
it part of the numeric hierarchy (as indeed xid isn't).
Yeah, I meant an xid64/xid8/fxid/pg_something/... type that isn't a
kind of number.
--
Thomas Munro
https://enterprisedb.com
On Thu, Jul 25, 2019 at 1:11 PM Thomas Munro <thomas.munro@gmail.com> wrote:
On Thu, Jul 25, 2019 at 12:42 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andres Freund <andres@anarazel.de> writes:
On 2019-07-24 20:34:39 -0400, Tom Lane wrote:
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.I assume Thomas was thinking more of another bespoke type like xid, just
wider. There's some notational advantage in not being able to
immediately do math etc on xids.Well, we could invent an xid8 type if we want, just don't try to make
it part of the numeric hierarchy (as indeed xid isn't).Yeah, I meant an xid64/xid8/fxid/pg_something/... type that isn't a
kind of number.
I played around with an xid8 type over here (not tested much yet, in
particular not tested on 32 bit box):
/messages/by-id/CA+hUKGKbQtX8E5TEdcZaYhTxqLqrvcpN1Vjb7eCu2bz5EACZbw@mail.gmail.com
--
Thomas Munro
https://enterprisedb.com
On Fri, Aug 2, 2019 at 10:42 PM Thomas Munro <thomas.munro@gmail.com> wrote:
On Thu, Jul 25, 2019 at 1:11 PM Thomas Munro <thomas.munro@gmail.com> wrote:
On Thu, Jul 25, 2019 at 12:42 PM Tom Lane <tgl@sss.pgh.pa.us> wrote:
Andres Freund <andres@anarazel.de> writes:
On 2019-07-24 20:34:39 -0400, Tom Lane wrote:
Yeah, I would absolutely NOT recommend that you open that can of worms
right now. We have looked at adding unsigned integer types in the past
and it looked like a mess.I assume Thomas was thinking more of another bespoke type like xid, just
wider. There's some notational advantage in not being able to
immediately do math etc on xids.Well, we could invent an xid8 type if we want, just don't try to make
it part of the numeric hierarchy (as indeed xid isn't).Yeah, I meant an xid64/xid8/fxid/pg_something/... type that isn't a
kind of number.
I thought about how to deal with the transition to xid8 for the
txid_XXX() family of functions. The best idea I've come up with so
far is to create a parallel xid8_XXX() family of functions, and
declare the bigint-based functions to be deprecated, and threaten to
drop them from a future release. The C code for the two families can
be the same (it's a bit of a dirty trick, but only until the
txid_XXX() variants go away). Here's a PoC patch demonstrating that.
Not tested much, yet, probably needs some more work, but I wanted to
see if others thought the idea was terrible first.
I wonder if there is a better way to share hash functions than the
hack in check_hash_func_signature(), which I had to extend to cover
xid8.
Adding to CF.
--
Thomas Munro
https://enterprisedb.com
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-use.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-use.patchDownload
From 842b1923ff4c31b4cd7cc6060453e019c83b74fa 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..e1e72cb6f1 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 => '1986',
+ 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 cf1f409351..900fe7d2a9 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
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-functio.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-functio.patchDownload
From f18d3a5020677f6657d1c8d271b37b0b9ab437bc Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 1 Sep 2019 16:47:02 +1200
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/func.sgml | 112 ++++++++-
src/backend/utils/adt/txid.c | 286 +++++++++--------------
src/include/catalog/pg_proc.dat | 41 ++++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
5 files changed, 266 insertions(+), 189 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index c878a0ba4d..c40cb42e69 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -19124,6 +19124,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19157,12 +19189,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof bigint</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19174,42 +19268,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 90b2c9b694..b77b497c17 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2019, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -33,17 +39,9 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
-
/*
* If defined, use bsearch() function for searching for txids in snapshots
* that have more than the specified number of values.
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -213,21 +186,21 @@ cmp_txid(const void *aa, const void *bb)
static void
sort_snapshot(TxidSnapshot *snap)
{
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip,
idx1,
idx2;
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
/* remove duplicates */
nxip = snap->nxip;
idx1 = idx2 = 0;
while (idx1 < nxip)
{
- if (snap->xip[idx1] != last)
+ if (!FullTransactionIdEquals(snap->xip[idx1], last))
last = snap->xip[idx2++] = snap->xip[idx1];
else
snap->nxip--;
@@ -237,21 +210,22 @@ sort_snapshot(TxidSnapshot *snap)
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -262,7 +236,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -274,7 +248,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -289,14 +263,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -313,68 +287,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -384,15 +324,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdPrecedes(xmax, val) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -421,33 +363,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -457,18 +389,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -484,15 +410,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -505,11 +429,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -556,14 +480,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -581,20 +508,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -603,13 +532,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -635,7 +567,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -646,29 +578,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -677,11 +609,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -690,11 +622,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -703,7 +635,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -725,7 +657,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -747,7 +679,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -755,7 +687,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 900fe7d2a9..5ea00c3782 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9395,6 +9395,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress txids in snapshot',
+ proname => 'xid_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index aaeb4d9d61..53e187feeb 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 157763be59..6afb4f322f 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 270
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 270
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
--
2.22.0
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com> wrote:
Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
--
Thomas Munro
https://enterprisedb.com
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v2.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v2.patchDownload
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
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v2.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v2.patchDownload
From f0e5d94fb73a88698ee66a180359f89e6fa37ae7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 1 Sep 2019 16:47:02 +1200
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/func.sgml | 112 ++++++++-
src/backend/utils/adt/txid.c | 286 +++++++++--------------
src/include/catalog/pg_proc.dat | 41 ++++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
5 files changed, 266 insertions(+), 189 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4e3e213fbc..575bb55843 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -19152,6 +19152,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19185,12 +19217,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof bigint</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19202,42 +19296,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 90b2c9b694..b77b497c17 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2019, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -33,17 +39,9 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
-
/*
* If defined, use bsearch() function for searching for txids in snapshots
* that have more than the specified number of values.
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -213,21 +186,21 @@ cmp_txid(const void *aa, const void *bb)
static void
sort_snapshot(TxidSnapshot *snap)
{
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip,
idx1,
idx2;
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
/* remove duplicates */
nxip = snap->nxip;
idx1 = idx2 = 0;
while (idx1 < nxip)
{
- if (snap->xip[idx1] != last)
+ if (!FullTransactionIdEquals(snap->xip[idx1], last))
last = snap->xip[idx2++] = snap->xip[idx1];
else
snap->nxip--;
@@ -237,21 +210,22 @@ sort_snapshot(TxidSnapshot *snap)
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -262,7 +236,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -274,7 +248,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -289,14 +263,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -313,68 +287,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -384,15 +324,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdPrecedes(xmax, val) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -421,33 +363,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -457,18 +389,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -484,15 +410,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -505,11 +429,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -556,14 +480,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -581,20 +508,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -603,13 +532,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -635,7 +567,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -646,29 +578,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -677,11 +609,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -690,11 +622,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -703,7 +635,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -725,7 +657,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -747,7 +679,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -755,7 +687,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 22ce0d5d90..f9efd03c7a 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9401,6 +9401,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress txids in snapshot',
+ proname => 'xid_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index aaeb4d9d61..53e187feeb 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 157763be59..6afb4f322f 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 270
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 270
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
--
2.22.0
Thomas Munro <thomas.munro@gmail.com> writes:
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com> wrote:
Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
FWIW, I'd move *all* the OIDs added by this patch up to >= 8000.
I don't feel a strong need to fill in the gaps in the low-numbered
OIDs, and people who do try that are likely to hit problems of the
sort you just did.
regards, tom lane
Thomas Munro <thomas.munro@gmail.com> writes:
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com>
wrote:Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
I have some questions in this code.
First,
"FullTransactionIdPrecedes(xmax, val)" is not equal to "val >= xmax" of
the previous code. "FullTransactionIdPrecedes(xmax, val)" expresses
"val > xmax". Is it all right?
@@ -384,15 +324,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdPrecedes(xmax, val) ||
+ FullTransactionIdPrecedes(val, last_val))
In addition to it, as to current TransactionId(not FullTransactionId)
comparison, when we express ">=" of TransactionId, we use
"TransactionIdFollowsOrEquals". this method is referred by some methods.
On the other hand, FullTransactionIdFollowsOrEquals has not implemented
yet. So, how about implementing this method?
Second,
About naming rule, "8" of xid8 means 8 bytes, but "8" has different
meaning in each situation. For example, int8 of PostgreSQL means 8
bytes, int8 of C language means 8 bits. If 64 is used, it just means 64
bits. how about xid64()?
regards,
Takao Fujii
Import Notes
Reply to msg id not found: CAHGQGwEL_SDhF1AK3+Rt3Z5+Ez5049V_v8yZZg8hZ5ihE_04Q@mail.gmail.com
On Tue, Oct 29, 2019 at 5:23 PM btfujiitkp <btfujiitkp@oss.nttdata.com> wrote:
Thomas Munro <thomas.munro@gmail.com> writes:
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com>
wrote:Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
I have some questions in this code.
Thanks for looking at the patch.
First,
"FullTransactionIdPrecedes(xmax, val)" is not equal to "val >= xmax" of
the previous code. "FullTransactionIdPrecedes(xmax, val)" expresses
"val > xmax". Is it all right?@@ -384,15 +324,17 @@ parse_snapshot(const char *str) while (*str != '\0') { /* read next value */ - val = str2txid(str, &endp); + val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); str = endp;/* require the input to be in order */ - if (val < xmin || val >= xmax || val < last_val) + if (FullTransactionIdPrecedes(val, xmin) || + FullTransactionIdPrecedes(xmax, val) || + FullTransactionIdPrecedes(val, last_val))In addition to it, as to current TransactionId(not FullTransactionId)
comparison, when we express ">=" of TransactionId, we use
"TransactionIdFollowsOrEquals". this method is referred by some methods.
On the other hand, FullTransactionIdFollowsOrEquals has not implemented
yet. So, how about implementing this method?
Good idea. I added the missing variants:
+#define FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value)
+#define FullTransactionIdFollows(a, b) ((a).value > (b).value)
+#define FullTransactionIdFollowsOrEquals(a, b) ((a).value >= (b).value)
Second,
About naming rule, "8" of xid8 means 8 bytes, but "8" has different
meaning in each situation. For example, int8 of PostgreSQL means 8
bytes, int8 of C language means 8 bits. If 64 is used, it just means 64
bits. how about xid64()?
In C, the typenames use bits, by happy coincidence similar to the C99
stdint.h typenames (int32_t etc) that we should perhaps eventually
switch to.
In SQL, the types have names based on the number of bytes: int2, int4,
int8, float4, float8, not conforming to any standard but established
over 3 decades ago and also understood by a few other SQL systems.
That's unfortunate, but I can't see that ever changing. I thought
that it would make most sense for the SQL type to be called xid8,
though admittedly it doesn't quite fit the pattern because xid is not
called xid4. There is another example a bit like that: macaddr (6
bytes) and macaccdr8 (8 bytes). As for the C type, we use
TransactionId and FullTransactionId (rather than, say, xid32 and
xid64).
In the attached I also took Tom's advice and used unused_oids script
to pick random OIDs >= 8000 for all new objects (ignoring nearby
comments about the range of OIDs used in different sections of the
file).
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v3.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v3.patchDownload
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: 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 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
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v3.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v3.patchDownload
From 2035acec2af2c755090f70b87e32a02519ba37d1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 1 Sep 2019 16:47:02 +1200
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Reviewed-by: Takao Fujii
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/func.sgml | 112 ++++++++-
src/backend/utils/adt/txid.c | 286 +++++++++--------------
src/include/access/transam.h | 3 +
src/include/catalog/pg_proc.dat | 41 ++++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
6 files changed, 269 insertions(+), 189 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 28eb322f3f..7450c390de 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -19471,6 +19471,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19504,12 +19536,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof bigint</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19521,42 +19615,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 90b2c9b694..56ad2b364e 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2019, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -33,17 +39,9 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
-
/*
* If defined, use bsearch() function for searching for txids in snapshots
* that have more than the specified number of values.
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -213,21 +186,21 @@ cmp_txid(const void *aa, const void *bb)
static void
sort_snapshot(TxidSnapshot *snap)
{
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip,
idx1,
idx2;
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
/* remove duplicates */
nxip = snap->nxip;
idx1 = idx2 = 0;
while (idx1 < nxip)
{
- if (snap->xip[idx1] != last)
+ if (!FullTransactionIdEquals(snap->xip[idx1], last))
last = snap->xip[idx2++] = snap->xip[idx1];
else
snap->nxip--;
@@ -237,21 +210,22 @@ sort_snapshot(TxidSnapshot *snap)
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -262,7 +236,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -274,7 +248,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -289,14 +263,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -313,68 +287,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -384,15 +324,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -421,33 +363,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -457,18 +389,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -484,15 +410,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -505,11 +429,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -556,14 +480,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -581,20 +508,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -603,13 +532,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -635,7 +567,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -646,29 +578,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -677,11 +609,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -690,11 +622,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -703,7 +635,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -725,7 +657,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -747,7 +679,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -755,7 +687,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 787451b3b7..884059cbd4 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,9 @@
#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)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3eb35a4eb6..a7e1230965 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9423,6 +9423,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress txids in snapshot',
+ proname => 'xid_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index c6d20b9946..4aa0d5560d 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index dcaf31eb7b..246419ca1a 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9560
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9560
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
--
2.23.0
On Tue, Oct 29, 2019 at 5:23 PM btfujiitkp <btfujiitkp@oss.nttdata.com>
wrote:Thomas Munro <thomas.munro@gmail.com> writes:
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com>
wrote:Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
I have some questions in this code.
Thanks for looking at the patch.
First,
"FullTransactionIdPrecedes(xmax, val)" is not equal to "val >= xmax"
of
the previous code. "FullTransactionIdPrecedes(xmax, val)" expresses
"val > xmax". Is it all right?@@ -384,15 +324,17 @@ parse_snapshot(const char *str) while (*str != '\0') { /* read next value */ - val = str2txid(str, &endp); + val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); str = endp;/* require the input to be in order */ - if (val < xmin || val >= xmax || val < last_val) + if (FullTransactionIdPrecedes(val, xmin) || + FullTransactionIdPrecedes(xmax, val) || + FullTransactionIdPrecedes(val, last_val))In addition to it, as to current TransactionId(not FullTransactionId)
comparison, when we express ">=" of TransactionId, we use
"TransactionIdFollowsOrEquals". this method is referred by some
methods.
On the other hand, FullTransactionIdFollowsOrEquals has not
implemented
yet. So, how about implementing this method?Good idea. I added the missing variants:
+#define FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value) +#define FullTransactionIdFollows(a, b) ((a).value > (b).value) +#define FullTransactionIdFollowsOrEquals(a, b) ((a).value >= (b).value)
Thank you for your patch.
It looks good.
Second,
About naming rule, "8" of xid8 means 8 bytes, but "8" has different
meaning in each situation. For example, int8 of PostgreSQL means 8
bytes, int8 of C language means 8 bits. If 64 is used, it just means
64
bits. how about xid64()?In C, the typenames use bits, by happy coincidence similar to the C99
stdint.h typenames (int32_t etc) that we should perhaps eventually
switch to.In SQL, the types have names based on the number of bytes: int2, int4,
int8, float4, float8, not conforming to any standard but established
over 3 decades ago and also understood by a few other SQL systems.That's unfortunate, but I can't see that ever changing. I thought
that it would make most sense for the SQL type to be called xid8,
though admittedly it doesn't quite fit the pattern because xid is not
called xid4. There is another example a bit like that: macaddr (6
bytes) and macaccdr8 (8 bytes). As for the C type, we use
TransactionId and FullTransactionId (rather than, say, xid32 and
xid64).
That makes sense.
Anyway,
In the pg_proc.dat, "xid_snapshot_xip" should be "xid8_snapshot_xip".
And some parts of 0002 patch are rejected when I patch 0002 after
patching 0001.
regards
Hi Thomas,
Please let me ask something about wraparound problem.
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
...
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
ISTM codes for wraparound are deleted. Is that correct?
I couldn't have read all related threads about using FullTransactionId but
does using FullTransactionId avoid wraparound problem?
If we consider below conditions, we can say it's difficult to see wraparound
with current disk like SSD (2GB/s) or memory DDR4 (34GB/s), but if we can use
more high-spec hardware like HBM3 (2048GB/s), we can see wraparound. Or do
I say silly things?
* 10 year system ( < 2^4 )
* 1 year = 31536000 ( = 60 * 60 * 24 * 365) secs ( < 2^25 )
* 2^35 ( = 2^64 / 2^4 / 2^25) transactions we can use in each seconds
* we can write at (2^5 * 2^30 * n) bytes/sec = (32 * n) GB/sec if we use 'n'
bytes for each transactions.
Is there any agreement we can throw the wraparound problem away if we adopt
FullTransactionId?
Thanks
--
Yoshikazu Imai
On Wed, Nov 20, 2019 at 5:43 PM imai.yoshikazu@fujitsu.com
<imai.yoshikazu@fujitsu.com> wrote:
Is there any agreement we can throw the wraparound problem away if we adopt
FullTransactionId?
Here is one argument for why 64 bits ought to be enough: we use 64 bit
LSNs for the WAL, and it usually takes more than one byte of WAL to
consume a transaction. If you write about 500MB of WAL per second,
your system will break in about a thousand years due to LSN
wraparound, that is, assuming the earth hasn't been destroyed to make
way for a hyperspace bypass, but either way you will probably still
have some spare full transaction IDs.
That's fun to think about, but unfortunately it's not easy to figure
out how to retrofit FullTransactionId into enough places to make
wraparounds go away in the traditional heap. It's a goal of at least
a couple of ongoing new AM projects to not have that problem, and I
figured it was a good idea to lay down very basic facilities for that,
trivial as they might be, and see where else they can be useful...
On 11/3/19 2:43 PM, Thomas Munro wrote:
On Tue, Oct 29, 2019 at 5:23 PM btfujiitkp <btfujiitkp@oss.nttdata.com> wrote:
Thomas Munro <thomas.munro@gmail.com> writes:
On Sun, Sep 1, 2019 at 5:04 PM Thomas Munro <thomas.munro@gmail.com>
wrote:Adding to CF.
Rebased. An OID clashed so re-roll the dice. Also spotted a typo.
I have some questions in this code.
Thanks for looking at the patch.
First,
"FullTransactionIdPrecedes(xmax, val)" is not equal to "val >= xmax" of
the previous code. "FullTransactionIdPrecedes(xmax, val)" expresses
"val > xmax". Is it all right?@@ -384,15 +324,17 @@ parse_snapshot(const char *str) while (*str != '\0') { /* read next value */ - val = str2txid(str, &endp); + val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); str = endp;/* require the input to be in order */ - if (val < xmin || val >= xmax || val < last_val) + if (FullTransactionIdPrecedes(val, xmin) || + FullTransactionIdPrecedes(xmax, val) || + FullTransactionIdPrecedes(val, last_val))In addition to it, as to current TransactionId(not FullTransactionId)
comparison, when we express ">=" of TransactionId, we use
"TransactionIdFollowsOrEquals". this method is referred by some methods.
On the other hand, FullTransactionIdFollowsOrEquals has not implemented
yet. So, how about implementing this method?Good idea. I added the missing variants:
+#define FullTransactionIdPrecedesOrEquals(a, b) ((a).value <= (b).value) +#define FullTransactionIdFollows(a, b) ((a).value > (b).value) +#define FullTransactionIdFollowsOrEquals(a, b) ((a).value >= (b).value)Second,
About naming rule, "8" of xid8 means 8 bytes, but "8" has different
meaning in each situation. For example, int8 of PostgreSQL means 8
bytes, int8 of C language means 8 bits. If 64 is used, it just means 64
bits. how about xid64()?In C, the typenames use bits, by happy coincidence similar to the C99
stdint.h typenames (int32_t etc) that we should perhaps eventually
switch to.In SQL, the types have names based on the number of bytes: int2, int4,
int8, float4, float8, not conforming to any standard but established
over 3 decades ago and also understood by a few other SQL systems.That's unfortunate, but I can't see that ever changing. I thought
that it would make most sense for the SQL type to be called xid8,
though admittedly it doesn't quite fit the pattern because xid is not
called xid4. There is another example a bit like that: macaddr (6
bytes) and macaccdr8 (8 bytes). As for the C type, we use
TransactionId and FullTransactionId (rather than, say, xid32 and
xid64).In the attached I also took Tom's advice and used unused_oids script
to pick random OIDs >= 8000 for all new objects (ignoring nearby
comments about the range of OIDs used in different sections of the
file).
These two patches (v3) no longer apply cleanly. Could you please
rebase?
--
Mark Dilger
On Sun, Dec 1, 2019 at 12:22 PM Mark Dilger <hornschnorter@gmail.com> wrote:
These two patches (v3) no longer apply cleanly. Could you please
rebase?
Hi Mark,
Thanks. Here's v4.
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v4.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v4.patchDownload
From 9ec66311c64289774da1aa6fc12b97347791a2a7 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: 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 b9cd6a1752..9ec046af2d 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 126bfac3ff..bfbc1f5066 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -193,6 +193,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 ac8f64b219..348b528dad 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 d9b35af914..fadef93aca 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
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v4.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v4.patchDownload
From fda9f7d05d9da52aea93ffb54fc4eaa35fa67eed Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sun, 1 Dec 2019 14:09:59 +1300
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Reviewed-by: Takao Fujii
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/func.sgml | 112 ++++++++-
src/backend/utils/adt/txid.c | 284 +++++++++--------------
src/include/access/transam.h | 3 +
src/include/catalog/pg_proc.dat | 41 ++++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
6 files changed, 269 insertions(+), 187 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 57a1539506..765f9cbd51 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -19471,6 +19471,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19504,12 +19536,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof bigint</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19521,42 +19615,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 06bf5a93ab..3fabdbf6e1 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2019, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,15 +40,8 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
* If defined, use bsearch() function for searching for txids in snapshots
@@ -63,39 +62,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +88,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +111,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +142,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -215,27 +189,29 @@ sort_snapshot(TxidSnapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +222,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -258,7 +234,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -273,14 +249,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -297,68 +273,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +310,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -405,33 +349,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -441,18 +375,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -468,15 +396,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -489,11 +415,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -540,14 +466,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -565,20 +494,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -587,13 +518,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -619,7 +553,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -630,29 +564,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -661,11 +595,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -674,11 +608,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -687,7 +621,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -731,7 +665,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 787451b3b7..884059cbd4 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,9 @@
#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)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 348b528dad..97d87bbcae 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9421,6 +9421,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress txids in snapshot',
+ proname => 'xid_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index fadef93aca..3d76f568f6 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index dcaf31eb7b..246419ca1a 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9560
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9560
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
--
2.23.0
On 11/30/19 5:14 PM, Thomas Munro wrote:
On Sun, Dec 1, 2019 at 12:22 PM Mark Dilger <hornschnorter@gmail.com> wrote:
These two patches (v3) no longer apply cleanly. Could you please
rebase?Hi Mark,
Thanks. Here's v4.
Thanks, Thomas.
The new patches apply cleanly and pass 'installcheck'.
--
Mark Dilger
On Tue, Dec 3, 2019 at 6:56 AM Mark Dilger <hornschnorter@gmail.com> wrote:
On 11/30/19 5:14 PM, Thomas Munro wrote:
On Sun, Dec 1, 2019 at 12:22 PM Mark Dilger <hornschnorter@gmail.com> wrote:
These two patches (v3) no longer apply cleanly. Could you please
rebase?Hi Mark,
Thanks. Here's v4.Thanks, Thomas.
The new patches apply cleanly and pass 'installcheck'.
I rebased, fixed the "xid_snapshot_xip" problem spotted by Takao Fujii
that I had missed earlier, updated a couple of error messages to refer
to the new names (even when using the old functions) and ran
check-world and some simple manual tests on an -m32 build just to be
paranoid. Here are the versions of these patches I'd like to commit.
Does anyone want to object to the txid/xid8 type punning scheme or
long term txid-sunsetting plan?
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v5.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v5.patchDownload
From c26d9fd2ec11c94720f236f7f3d89c773a13719d Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 16:36:36 +1300
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, Yoshikazu Imai, Mark Dilger
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/catversion.h | 2 +-
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 +
15 files changed, 163 insertions(+), 1 deletion(-)
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 6346e65865..2f2858021c 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 db6fc9dd6b..f0c317e7b1 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 06096cc86c..66a50f183f 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 6a947b958b..4dc9a0c67b 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/catversion.h b/src/include/catalog/catversion.h
index 249b1f5a34..efd5df5517 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202001251
+#define CATALOG_VERSION_NO 202001271
#endif
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..a2ac3b8b5b 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 c67768fcab..dd3d29822b 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 6ef8b8a4e7..13389d4695 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 ab2f50c9eb..50837463fb 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 7c135da3b1..38bac36361 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -193,6 +193,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 26227df216..ba5c770c65 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 bef50c76d9..95a888e060 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 fe2c4eabb4..89d9c40e42 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..288e62de9c
--- /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 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
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v5.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v5.patchDownload
From 623d385f35f29d9b30a9edc2d49388b708b18382 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 17:23:49 +1300
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Reviewed-by: Takao Fujii, Yoshikazu Imai, Mark Dilger
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/func.sgml | 112 ++++++++-
src/backend/utils/adt/txid.c | 288 +++++++++--------------
src/include/access/transam.h | 3 +
src/include/catalog/catversion.h | 2 +-
src/include/catalog/pg_proc.dat | 41 ++++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 10 +-
8 files changed, 277 insertions(+), 195 deletions(-)
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 895b4b7b1b..5f7bd28e6d 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18916,6 +18916,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -18949,12 +18981,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof bigint</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -18966,42 +19060,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 33272f8030..e0b29d4456 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,15 +40,8 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
* If defined, use bsearch() function for searching for txids in snapshots
@@ -63,39 +62,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +88,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +111,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +142,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -215,27 +189,29 @@ sort_snapshot(TxidSnapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +222,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -258,7 +234,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -273,14 +249,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -297,68 +273,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +310,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,7 +336,7 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "xid8_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
@@ -405,33 +349,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -441,18 +375,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -468,15 +396,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -489,11 +415,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -540,14 +466,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -565,20 +494,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -587,13 +518,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -610,7 +544,7 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external xid8_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
@@ -619,7 +553,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -630,29 +564,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -661,11 +595,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -674,11 +608,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -687,7 +621,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -731,7 +665,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 4dc9a0c67b..9a808f64eb 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,9 @@
#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)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index efd5df5517..ea3fdbe393 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202001271
+#define CATALOG_VERSION_NO 202001272
#endif
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 95a888e060..f941739ad5 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9460,6 +9460,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'xid8_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 89d9c40e42..7ae5eabd21 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index dcaf31eb7b..246419ca1a 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9560
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9560
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..69867dbf16 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -20,19 +20,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +235,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
--
2.23.0
On 2020/01/28 14:05, Thomas Munro wrote:
On Tue, Dec 3, 2019 at 6:56 AM Mark Dilger <hornschnorter@gmail.com> wrote:
On 11/30/19 5:14 PM, Thomas Munro wrote:
On Sun, Dec 1, 2019 at 12:22 PM Mark Dilger <hornschnorter@gmail.com> wrote:
These two patches (v3) no longer apply cleanly. Could you please
rebase?Hi Mark,
Thanks. Here's v4.Thanks, Thomas.
The new patches apply cleanly and pass 'installcheck'.
I rebased, fixed the "xid_snapshot_xip" problem spotted by Takao Fujii
that I had missed earlier, updated a couple of error messages to refer
to the new names (even when using the old functions) and ran
check-world and some simple manual tests on an -m32 build just to be
paranoid. Here are the versions of these patches I'd like to commit.
Thanks for the patches! Here are my minor comments.
Isn't it better to add also xid8lt, xid8gt, xid8le, and xid8ge?
xid8 and xid8_snapshot should be documented in datatype.sgml like
txid_snapshot is?
logicaldecoding.sgml and monitoring.sgml still referred to txid_xxx.
They should be updated so that new xid8_xxx is used?
In func.sgml, the table "Snapshot Components" is described still based
on txid. It should be updated so that it uses xid8, instead?
+# xid_ops
+{ amopfamily => 'hash/xid8_ops', amoplefttype => 'xid8', amoprighttype => 'xid8',
+ amopstrategy => '1', amopopr => '=(xid8,xid8)', amopmethod => 'hash' },
"xid_ops" in the comment should be "xid8_ops".
+{ oid => '9558',
+ proname => 'xid8neq', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid8 xid8', prosrc => 'xid8neq' },
Basically the names of not-equal functions for most data types are
something like "xxxne" not "xxxneq". So IMO it's better to use the name
"xid8ne" instead of "xid8neq" here.
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
As the comment suggests, this function assumes that "xid" must
precede "next_fxid". But there is no check for the assumption.
Isn't it better to add, e.g., an assertion checking that?
Or convert_xid() should handle the case where "xid" follows
"next_fxid" like the orignal convert_xid() does. That is, don't
apply the following change.
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
Does anyone want to object to the txid/xid8 type punning scheme or
long term txid-sunsetting plan?
No. +1 to retire txid someday.
Regards,
--
Fujii Masao
NTT DATA CORPORATION
Advanced Platform Technology Group
Research and Development Headquarters
Hello Fujii-san,
Thanks for your review!
On Wed, Jan 29, 2020 at 12:43 AM Fujii Masao
<masao.fujii@oss.nttdata.com> wrote:
Isn't it better to add also xid8lt, xid8gt, xid8le, and xid8ge?
xid doesn't have these operators, probably to avoid confusion about
wraparound. But you're right, we should add them for xid8, especially
since the xid_snapshot documentation mentions such comparisons (the
type used by the old functions was int8, so that worked). Done. I
also added the extra catalogue nuts and bolts required to use xid8 in
btree indexes and merge joins.
To test the operators, I added a new regression test for xid and xid8
types. While doing that, I tried to add some range checks to validate
input, but I discovered that it's a bit tricky to do so portably with
strtoul(). I suspect '0x100000000'::xid already gives different
results on Windows and Unix today, and if better input validation is
desired, I think it should be tackled outside this project.
While working on those tests, I realised that we probably wanted two
sets of tests:
1. txid.sql: The existing tests that show that the old txid_XXX()
functions continue to work correctly (with the only user-visible
difference being that in their error messages they sometimes mention
names including xid8). This test will be dropped when the txid_XXX()
functions are dropped.
2. xid.sql: A new set of tests that show that the new xid8_XXX()
functions work correctly.
To verify that the old tests and the new tests are exactly the same
except for typenames and some casts, use:
diff -u src/test/regress/expected/txid.out src/test/regress/expected/xid.out
xid8 and xid8_snapshot should be documented in datatype.sgml like
txid_snapshot is?
Done.
logicaldecoding.sgml and monitoring.sgml still referred to txid_xxx.
They should be updated so that new xid8_xxx is used?
Done.
In func.sgml, the table "Snapshot Components" is described still based
on txid. It should be updated so that it uses xid8, instead?
Done.
+# xid_ops +{ amopfamily => 'hash/xid8_ops', amoplefttype => 'xid8', amoprighttype => 'xid8', + amopstrategy => '1', amopopr => '=(xid8,xid8)', amopmethod => 'hash' },"xid_ops" in the comment should be "xid8_ops".
Fixed.
+{ oid => '9558', + proname => 'xid8neq', proleakproof => 't', prorettype => 'bool', + proargtypes => 'xid8 xid8', prosrc => 'xid8neq' },Basically the names of not-equal functions for most data types are
something like "xxxne" not "xxxneq". So IMO it's better to use the name
"xid8ne" instead of "xid8neq" here.
Huh. You are right, but the existing function xidneq is an exception.
It's not clear which one to follow. I will take your advice and use
xid8ne. We could potentially change xidneq to xidne too, but it's
user-visible.
/* - * do a TransactionId -> txid conversion for an XID near the given epoch + * Do a TransactionId -> fxid conversion for an XID that is known to precede + * the given 'next_fxid'. */ -static txid -convert_xid(TransactionId xid, const TxidEpoch *state) +static FullTransactionId +convert_xid(TransactionId xid, FullTransactionId next_fxid)As the comment suggests, this function assumes that "xid" must
precede "next_fxid". But there is no check for the assumption.
Isn't it better to add, e.g., an assertion checking that?
Or convert_xid() should handle the case where "xid" follows
"next_fxid" like the orignal convert_xid() does. That is, don't
apply the following change.- if (xid > state->last_xid && - TransactionIdPrecedes(xid, state->last_xid)) + if (xid > next_xid) epoch--; - else if (xid < state->last_xid && - TransactionIdFollows(xid, state->last_xid)) - epoch++;
I need to think about this part some more, but I wanted to share
responses to the rest of your review now. I'll return to this point
next week.
Does anyone want to object to the txid/xid8 type punning scheme or
long term txid-sunsetting plan?No. +1 to retire txid someday.
Cool. Let's do that in a couple of years.
Attachments:
0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v6.patchapplication/octet-stream; name=0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to--v6.patchDownload
From e1880ad6a819b1079087a4f24fe0c59f79b68adf Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 16:36:36 +1300
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: Fujii Masao, Takao Fujii, Yoshikazu Imai, Mark Dilger
Discussion: https://postgr.es/m/https://www.postgresql.org/message-id/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 7 ++
src/backend/access/hash/hashvalidate.c | 3 +
src/backend/utils/adt/xid.c | 116 +++++++++++++++++++
src/fe_utils/print.c | 1 +
src/include/access/transam.h | 11 ++
src/include/catalog/pg_amop.dat | 22 ++++
src/include/catalog/pg_amproc.dat | 6 +
src/include/catalog/pg_cast.dat | 4 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 25 +++++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 36 ++++++
src/include/catalog/pg_type.dat | 4 +
src/include/utils/xid8.h | 22 ++++
src/test/regress/expected/opr_sanity.out | 7 ++
src/test/regress/expected/xid.out | 136 +++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/xid.sql | 48 ++++++++
19 files changed, 458 insertions(+), 1 deletion(-)
create mode 100644 src/include/utils/xid8.h
create mode 100644 src/test/regress/expected/xid.out
create mode 100644 src/test/regress/sql/xid.sql
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index d1d033178f..8a97855180 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4508,6 +4508,10 @@ INSERT INTO mytable VALUES(-1); -- fails
<primary>xid</primary>
</indexterm>
+ <indexterm zone="datatype-oid">
+ <primary>xid8</primary>
+ </indexterm>
+
<indexterm zone="datatype-oid">
<primary>cid</primary>
</indexterm>
@@ -4704,6 +4708,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 6346e65865..2f2858021c 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 db6fc9dd6b..20389aff1d 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 06096cc86c..66a50f183f 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 6a947b958b..4dc9a0c67b 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 11aaa519c8..b5dfaad9ec 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 c67768fcab..6d838792ac 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -227,6 +227,8 @@
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' },
# hash
{ amprocfamily => 'hash/bpchar_ops', amproclefttype => 'bpchar',
@@ -339,6 +341,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 6ef8b8a4e7..13389d4695 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 ab2f50c9eb..f2342bb328 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 7c135da3b1..13d9a9fce5 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 => '9551', 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 => '9552', 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 26227df216..4004138d77 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 226c904c04..50aa20e034 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,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 => '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 fe2c4eabb4..89d9c40e42 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..288e62de9c
--- /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 c19740e5db..306ab62720 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -790,6 +790,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 0000000000..e9e1fa8259
--- /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 d2b17dd3ea..b4c100b1c0 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 acba391332..017aa3888f 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 0000000000..a4fbca5176
--- /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;
--
2.23.0
0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v6.patchapplication/octet-stream; name=0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func-v6.patchDownload
From ba77a1cd4e17745af6fdcf85e7c5c5e4b00dd79e Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 17:23:49 +1300
Subject: [PATCH 2/2] Introduce xid8 variants of the txid_XXX() fmgr functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions were already using the wrong signedness,
and since we'll presumably drop the txid_XXX() variants after a period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Author: Thomas Munro
Reviewed-by: Fujii Masao, Takao Fujii, Yoshikazu Imai, Mark Dilger
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 8 +-
doc/src/sgml/func.sgml | 140 ++++++++--
doc/src/sgml/logicaldecoding.sgml | 2 +-
doc/src/sgml/monitoring.sgml | 2 +-
src/backend/utils/adt/txid.c | 288 ++++++++------------
src/include/access/transam.h | 3 +
src/include/catalog/pg_proc.dat | 41 +++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 13 +-
src/test/regress/expected/xid.out | 326 +++++++++++++++++++++++
src/test/regress/sql/txid.sql | 3 +
src/test/regress/sql/xid.sql | 104 ++++++++
13 files changed, 736 insertions(+), 210 deletions(-)
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 8a97855180..9592aacdd1 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -279,7 +279,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
- <entry>user-level transaction ID snapshot</entry>
+ <entry>user-level transaction ID snapshot (see also <type>xid8_snapshot</type>)</entry>
</row>
<row>
@@ -288,6 +288,12 @@
<entry>universally unique identifier</entry>
</row>
+ <row>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry></entry>
+ <entry>user-level transaction ID snapshot</entry>
+ </row>
+
<row>
<entry><type>xml</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ceda48e0fc..4b171b5dd4 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18916,6 +18916,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -18949,12 +18981,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof xid8</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -18966,42 +19060,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
@@ -19012,13 +19106,15 @@ SELECT collation for ('foo' COLLATE "de_DE");
wraps around every 4 billion transactions. However, these functions
export a 64-bit format that is extended with an <quote>epoch</quote> counter
so it will not wrap around during the life of an installation.
- The data type used by these functions, <type>txid_snapshot</type>,
+ The data type used by these functions, <type>xid8_snapshot</type>,
stores information about transaction ID
visibility at a particular moment in time. Its components are
- described in <xref linkend="functions-txid-snapshot-parts"/>.
+ described in <xref linkend="functions-xid8-snapshot-parts"/>. The
+ <type>xid8</type> values it contains can be converted to <type>xid</type>
+ by casting, if required.
</para>
- <table id="functions-txid-snapshot-parts">
+ <table id="functions-xid8-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@@ -19033,7 +19129,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
- Earliest transaction ID (txid) that is still active. All earlier
+ Earliest transaction ID (xid8) that is still active. All earlier
transactions will either be committed and visible, or rolled
back and dead.
</entry>
@@ -19042,7 +19138,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmax</type></entry>
<entry>
- First as-yet-unassigned txid. All txids greater than or equal to this
+ First as-yet-unassigned xid8. All xid8s greater than or equal to this
are not yet started as of the time of the snapshot, and thus invisible.
</entry>
</row>
@@ -19050,14 +19146,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xip_list</type></entry>
<entry>
- Active txids at the time of the snapshot. The list
- includes only those active txids between <literal>xmin</literal>
- and <literal>xmax</literal>; there might be active txids higher
- than <literal>xmax</literal>. A txid that is <literal>xmin <= txid <
+ Active xid8s at the time of the snapshot. The list
+ includes only those active xid8s between <literal>xmin</literal>
+ and <literal>xmax</literal>; there might be active xid8s higher
+ than <literal>xmax</literal>. An xid8 that is <literal>xmin <= xid8 <
xmax</literal> and not in this list was already completed
at the time of the snapshot, and thus either visible or
dead according to its commit status. The list does not
- include txids of subtransactions.
+ include xid8s of subtransactions.
</entry>
</row>
@@ -19066,14 +19162,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
- <type>txid_snapshot</type>'s textual representation is
+ <type>xid8_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
- <function>txid_status(bigint)</function> reports the commit status of a recent
+ <function>xid8_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@@ -19087,7 +19183,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
- need to determine whether the txid is a prepared transaction.
+ need to determine whether the xid8 is a prepared transaction.
</para>
<para>
diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index bce6d379bf..e8e6e36a27 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
- calling <literal>txid_current()</literal>.
+ calling <literal>xid8_current()</literal>.
</para>
</sect2>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 9129f79bbf..511db194a6 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1097,7 +1097,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
- <entry>Waiting to execute <function>txid_status</function> or update
+ <entry>Waiting to execute <function>xid8_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 33272f8030..e0b29d4456 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,12 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. This works because the types
+ * are binary compatible for the first 2^63 transactions. The txid_XXX
+ * variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,15 +40,8 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
* If defined, use bsearch() function for searching for txids in snapshots
@@ -63,39 +62,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +88,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +111,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +142,37 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Do a TransactionId -> fxid conversion for an XID that is known to precede
+ * the given 'next_fxid'.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+convert_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -215,27 +189,29 @@ sort_snapshot(TxidSnapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +222,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -258,7 +234,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -273,14 +249,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -297,68 +273,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +310,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,7 +336,7 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "xid8_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
@@ -405,33 +349,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
-
- val = convert_xid(GetTopTransactionId(), &state);
+ PreventCommandDuringRecovery("xid8_current()");
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -441,18 +375,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -468,15 +396,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -489,11 +415,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = convert_xid(cur->xmin, next_fxid);
+ snap->xmax = convert_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = convert_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -540,14 +466,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -565,20 +494,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -587,13 +518,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -610,7 +544,7 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external xid8_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
@@ -619,7 +553,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -630,29 +564,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -661,11 +595,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -674,11 +608,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -687,7 +621,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -731,7 +665,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 4dc9a0c67b..9a808f64eb 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -49,6 +49,9 @@
#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)
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 50aa20e034..9be7b24e6f 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9475,6 +9475,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'xid8_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 89d9c40e42..7ae5eabd21 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -455,6 +455,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 306ab62720..16e24bc6ac 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9560
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9560
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..efbd21af7e 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index e9e1fa8259..e0f7cf6fa8 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -134,3 +134,329 @@ 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;
+-- xid8_snapshot data type and related functions
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions shared C code)
+-- i/o
+select '12:13:'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:13:
+(1 row)
+
+select '12:18:14,16'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:18:14,16
+(1 row)
+
+select '12:16:14,14'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:16:14
+(1 row)
+
+-- errors
+select '31:12:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
+LINE 1: select '31:12:'::xid8_snapshot;
+ ^
+select '0:1:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
+LINE 1: select '0:1:'::xid8_snapshot;
+ ^
+select '12:13:0'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
+LINE 1: select '12:13:0'::xid8_snapshot;
+ ^
+select '12:16:14,13'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
+LINE 1: select '12:16:14,13'::xid8_snapshot;
+ ^
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+ snap
+-------------------------------------------------------------------------------------------------------------------------------------
+ 12:13:
+ 12:20:13,15,18
+ 100001:100009:100005,100007,100008
+ 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
+(4 rows)
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+ xid8_snapshot_xmin | xid8_snapshot_xmax | xid8_snapshot_xip
+--------------------+--------------------+-------------------
+ 12 | 20 | 13
+ 12 | 20 | 15
+ 12 | 20 | 18
+ 100001 | 100009 | 100005
+ 100001 | 100009 | 100007
+ 100001 | 100009 | 100008
+ 100 | 150 | 101
+ 100 | 150 | 102
+ 100 | 150 | 103
+ 100 | 150 | 104
+ 100 | 150 | 105
+ 100 | 150 | 106
+ 100 | 150 | 107
+ 100 | 150 | 108
+ 100 | 150 | 109
+ 100 | 150 | 110
+ 100 | 150 | 111
+ 100 | 150 | 112
+ 100 | 150 | 113
+ 100 | 150 | 114
+ 100 | 150 | 115
+ 100 | 150 | 116
+ 100 | 150 | 117
+ 100 | 150 | 118
+ 100 | 150 | 119
+ 100 | 150 | 120
+ 100 | 150 | 121
+ 100 | 150 | 122
+ 100 | 150 | 123
+ 100 | 150 | 124
+ 100 | 150 | 125
+ 100 | 150 | 126
+ 100 | 150 | 127
+ 100 | 150 | 128
+ 100 | 150 | 129
+ 100 | 150 | 130
+ 100 | 150 | 131
+(37 rows)
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+ id | xid8_visible_in_snapshot
+----+--------------------------
+ 11 | t
+ 12 | t
+ 13 | f
+ 14 | t
+ 15 | f
+ 16 | t
+ 17 | t
+ 18 | f
+ 19 | t
+ 20 | f
+ 21 | f
+(11 rows)
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+ id | xid8_visible_in_snapshot
+-----+--------------------------
+ 90 | t
+ 91 | t
+ 92 | t
+ 93 | t
+ 94 | t
+ 95 | t
+ 96 | t
+ 97 | t
+ 98 | t
+ 99 | t
+ 100 | t
+ 101 | f
+ 102 | f
+ 103 | f
+ 104 | f
+ 105 | f
+ 106 | f
+ 107 | f
+ 108 | f
+ 109 | f
+ 110 | f
+ 111 | f
+ 112 | f
+ 113 | f
+ 114 | f
+ 115 | f
+ 116 | f
+ 117 | f
+ 118 | f
+ 119 | f
+ 120 | f
+ 121 | f
+ 122 | f
+ 123 | f
+ 124 | f
+ 125 | f
+ 126 | f
+ 127 | f
+ 128 | f
+ 129 | f
+ 130 | f
+ 131 | f
+ 132 | t
+ 133 | t
+ 134 | t
+ 135 | t
+ 136 | t
+ 137 | t
+ 138 | t
+ 139 | t
+ 140 | t
+ 141 | t
+ 142 | t
+ 143 | t
+ 144 | t
+ 145 | t
+ 146 | t
+ 147 | t
+ 148 | t
+ 149 | t
+ 150 | f
+ 151 | f
+ 152 | f
+ 153 | f
+ 154 | f
+ 155 | f
+ 156 | f
+ 157 | f
+ 158 | f
+ 159 | f
+ 160 | f
+(71 rows)
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+ ?column?
+----------
+ t
+(1 row)
+
+-- we can't assume current is always less than xmax, however
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+-- test 64bitness
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+ xid8_snapshot
+---------------------------------------------------------------------
+ 1000100010001000:1000100010001100:1000100010001012,1000100010001013
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ t
+(1 row)
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+ xid8_snapshot
+-------------------------
+ 1:9223372036854775807:3
+(1 row)
+
+SELECT xid8_snapshot '1:9223372036854775808:3';
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
+LINE 1: SELECT xid8_snapshot '1:9223372036854775808:3';
+ ^
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+SELECT xid8_status(:committed::text::xid8) AS committed;
+ committed
+-----------
+ committed
+(1 row)
+
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+ rolledback
+------------
+ aborted
+(1 row)
+
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+ inprogress
+-------------
+ in progress
+(1 row)
+
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+ xid8_status
+-------------
+
+(1 row)
+
+COMMIT;
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+NOTICE: Got expected error for xid in the future
+ test_future_xid_status
+------------------------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql
index bd6decf0ef..8d5ac98a89 100644
--- a/src/test/regress/sql/txid.sql
+++ b/src/test/regress/sql/txid.sql
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index a4fbca5176..55cd21fe92 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -46,3 +46,107 @@ 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;
+
+
+-- xid8_snapshot data type and related functions
+
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions shared C code)
+
+-- i/o
+select '12:13:'::xid8_snapshot;
+select '12:18:14,16'::xid8_snapshot;
+select '12:16:14,14'::xid8_snapshot;
+
+-- errors
+select '31:12:'::xid8_snapshot;
+select '0:1:'::xid8_snapshot;
+select '12:13:0'::xid8_snapshot;
+select '12:16:14,13'::xid8_snapshot;
+
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+
+-- we can't assume current is always less than xmax, however
+
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+
+-- test 64bitness
+
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+SELECT xid8_snapshot '1:9223372036854775808:3';
+
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+COMMIT;
+
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+
+SELECT xid8_status(:committed::text::xid8) AS committed;
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+
+COMMIT;
+
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+ROLLBACK;
--
2.23.0
On Fri, Feb 14, 2020 at 6:31 PM Thomas Munro <thomas.munro@gmail.com> wrote:
On Wed, Jan 29, 2020 at 12:43 AM Fujii Masao <masao.fujii@oss.nttdata.com> wrote:
/* - * do a TransactionId -> txid conversion for an XID near the given epoch + * Do a TransactionId -> fxid conversion for an XID that is known to precede + * the given 'next_fxid'. */ -static txid -convert_xid(TransactionId xid, const TxidEpoch *state) +static FullTransactionId +convert_xid(TransactionId xid, FullTransactionId next_fxid)As the comment suggests, this function assumes that "xid" must
precede "next_fxid". But there is no check for the assumption.
Isn't it better to add, e.g., an assertion checking that?
Or convert_xid() should handle the case where "xid" follows
"next_fxid" like the orignal convert_xid() does. That is, don't
apply the following change.- if (xid > state->last_xid && - TransactionIdPrecedes(xid, state->last_xid)) + if (xid > next_xid) epoch--; - else if (xid < state->last_xid && - TransactionIdFollows(xid, state->last_xid)) - epoch++;
I don't think it is reachable. I have renamed the function to
widen_snapshot_xid() and rewritten the comments to explain the logic.
The other changes in this version:
* updated OIDs to avoid collisions
* added btequalimage to btree/xid8_ops
Attachments:
v7-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchtext/x-patch; charset=US-ASCII; name=v7-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchDownload
From 7fc4387dae4395df111ac64aa28c66aebbb04dbb Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 16:36:36 +1300
Subject: [PATCH v7 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.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Discussion: https://postgr.es/m/https://www.postgresql.org/message-id/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 7 ++
src/backend/access/hash/hashvalidate.c | 3 +
src/backend/utils/adt/xid.c | 116 +++++++++++++++++++
src/fe_utils/print.c | 1 +
src/include/access/transam.h | 14 +++
src/include/catalog/pg_amop.dat | 22 ++++
src/include/catalog/pg_amproc.dat | 8 ++
src/include/catalog/pg_cast.dat | 4 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 25 +++++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 36 ++++++
src/include/catalog/pg_type.dat | 4 +
src/include/utils/xid8.h | 22 ++++
src/test/regress/expected/opr_sanity.out | 7 ++
src/test/regress/expected/xid.out | 136 +++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/xid.sql | 48 ++++++++
19 files changed, 463 insertions(+), 1 deletion(-)
create mode 100644 src/include/utils/xid8.h
create mode 100644 src/test/regress/expected/xid.out
create mode 100644 src/test/regress/sql/xid.sql
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 03971822c2..89f3a7c119 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4516,6 +4516,10 @@ INSERT INTO mytable VALUES(-1); -- fails
<primary>regtype</primary>
</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 6346e65865..2f2858021c 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 db6fc9dd6b..20389aff1d 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 06096cc86c..66a50f183f 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 6a947b958b..9a808f64eb 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/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..b5dfaad9ec 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 75c0152b66..6a258f7fe2 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 01c5328ddd..5a58f50fbb 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 ab2f50c9eb..f2342bb328 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 7c135da3b1..0e6680661c 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 26227df216..4004138d77 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 87d25d4a4b..ba3ab7c979 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 2e6110e3f2..a1f441b8da 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 0000000000..288e62de9c
--- /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 3c0b21d633..39c50afde6 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 0000000000..e9e1fa8259
--- /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 d2b17dd3ea..b4c100b1c0 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 acba391332..017aa3888f 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 0000000000..a4fbca5176
--- /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;
--
2.20.1
v7-0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func.patchtext/x-patch; charset=US-ASCII; name=v7-0002-Introduce-xid8-variants-of-the-txid_XXX-fmgr-func.patchDownload
From e1674ee61e0a6d5893cc73ef0404c75fd6515bad Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Tue, 28 Jan 2020 17:23:49 +1300
Subject: [PATCH v7 2/2] Introduce xid8 variants of the txid_XXX() fmgr
functions.
The txid_XXX() family of functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a set of functions xid8_XXX() corresponding to the txid_XXX()
functions.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions are the ones using the wrong signedness,
and since we'll presumably drop the older ones after a reasonable period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 8 +-
doc/src/sgml/func.sgml | 140 ++++++++--
doc/src/sgml/logicaldecoding.sgml | 2 +-
doc/src/sgml/monitoring.sgml | 2 +-
src/backend/utils/adt/txid.c | 298 +++++++++------------
src/include/catalog/pg_proc.dat | 41 +++
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 13 +-
src/test/regress/expected/xid.out | 326 +++++++++++++++++++++++
src/test/regress/sql/txid.sql | 3 +
src/test/regress/sql/xid.sql | 104 ++++++++
12 files changed, 742 insertions(+), 211 deletions(-)
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 89f3a7c119..91c9202458 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -279,7 +279,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
- <entry>user-level transaction ID snapshot</entry>
+ <entry>user-level transaction ID snapshot (see also <type>xid8_snapshot</type>)</entry>
</row>
<row>
@@ -288,6 +288,12 @@
<entry>universally unique identifier</entry>
</row>
+ <row>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry></entry>
+ <entry>user-level transaction ID snapshot</entry>
+ </row>
+
<row>
<entry><type>xml</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 464a48ed6a..e63ca1aa25 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18935,6 +18935,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -18968,12 +19000,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof xid8</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backwards compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -18985,42 +19079,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
@@ -19031,13 +19125,15 @@ SELECT collation for ('foo' COLLATE "de_DE");
wraps around every 4 billion transactions. However, these functions
export a 64-bit format that is extended with an <quote>epoch</quote> counter
so it will not wrap around during the life of an installation.
- The data type used by these functions, <type>txid_snapshot</type>,
+ The data type used by these functions, <type>xid8_snapshot</type>,
stores information about transaction ID
visibility at a particular moment in time. Its components are
- described in <xref linkend="functions-txid-snapshot-parts"/>.
+ described in <xref linkend="functions-xid8-snapshot-parts"/>. The
+ <type>xid8</type> values it contains can be converted to <type>xid</type>
+ by casting, if required.
</para>
- <table id="functions-txid-snapshot-parts">
+ <table id="functions-xid8-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@@ -19052,7 +19148,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
- Earliest transaction ID (txid) that is still active. All earlier
+ Earliest transaction ID (xid8) that is still active. All earlier
transactions will either be committed and visible, or rolled
back and dead.
</entry>
@@ -19061,7 +19157,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmax</type></entry>
<entry>
- First as-yet-unassigned txid. All txids greater than or equal to this
+ First as-yet-unassigned xid8. All xid8s greater than or equal to this
are not yet started as of the time of the snapshot, and thus invisible.
</entry>
</row>
@@ -19069,14 +19165,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xip_list</type></entry>
<entry>
- Active txids at the time of the snapshot. The list
- includes only those active txids between <literal>xmin</literal>
- and <literal>xmax</literal>; there might be active txids higher
- than <literal>xmax</literal>. A txid that is <literal>xmin <= txid <
+ Active xid8s at the time of the snapshot. The list
+ includes only those active xid8s between <literal>xmin</literal>
+ and <literal>xmax</literal>; there might be active xid8s higher
+ than <literal>xmax</literal>. An xid8 that is <literal>xmin <= xid8 <
xmax</literal> and not in this list was already completed
at the time of the snapshot, and thus either visible or
dead according to its commit status. The list does not
- include txids of subtransactions.
+ include xid8s of subtransactions.
</entry>
</row>
@@ -19085,14 +19181,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
- <type>txid_snapshot</type>'s textual representation is
+ <type>xid8_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
- <function>txid_status(bigint)</function> reports the commit status of a recent
+ <function>xid8_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@@ -19106,7 +19202,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
- need to determine whether the txid is a prepared transaction.
+ need to determine whether the xid8 is a prepared transaction.
</para>
<para>
diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index bce6d379bf..e8e6e36a27 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
- calling <literal>txid_current()</literal>.
+ calling <literal>xid8_current()</literal>.
</para>
</sect2>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 5bffdcce10..48d51fa6da 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1105,7 +1105,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
- <entry>Waiting to execute <function>txid_status</function> or update
+ <entry>Waiting to execute <function>xid8_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c
index 33272f8030..fbda2ba02c 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/txid.c
@@ -9,6 +9,11 @@
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. The txid_XXX variants should
+ * eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,15 +39,8 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
* If defined, use bsearch() function for searching for txids in snapshots
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
+ (offsetof(TxidSnapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+ ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Convert a TransactionId obtained from a snapshot held by the caller to a
+ * FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
+ * we can compute the high order bits. It must have been obtained by the
+ * caller with ReadNextFullTransactionId() after the snapshot was created.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
- /* return special xid's as-is */
+ /* Special transaction ID. */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ /*
+ * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
+ * issued yet when the snapshot was created. Every TransactionId in the
+ * snapshot must therefore be from the same epoch as next_fxid, or the
+ * epoch before. We know this because next_fxid is never allow to get more
+ * than one epoch ahead of the TransactionIds in any snapshot.
+ */
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -215,27 +197,29 @@ sort_snapshot(TxidSnapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const TxidSnapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -258,7 +242,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
StringInfo buf;
@@ -273,14 +257,14 @@ buf_init(txid xmin, txid xmax)
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
@@ -297,68 +281,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +318,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,7 +344,7 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "xid8_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
@@ -405,33 +357,23 @@ bad_format:
*/
/*
- * txid_current() returns int8
+ * txid_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
+ PreventCommandDuringRecovery("xid8_current()");
- val = convert_xid(GetTopTransactionId(), &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
@@ -441,18 +383,12 @@ txid_current(PG_FUNCTION_ARGS)
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
@@ -468,15 +404,13 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
TxidSnapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
@@ -489,11 +423,11 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
+ snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -540,14 +474,17 @@ txid_snapshot_out(PG_FUNCTION_ARGS)
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
@@ -565,20 +502,22 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
- txid last = 0;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
@@ -587,13 +526,16 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -610,7 +552,7 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external xid8_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
@@ -619,7 +561,7 @@ bad_format:
*
* binary output function for type txid_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
@@ -630,29 +572,29 @@ txid_snapshot_send(PG_FUNCTION_ARGS)
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * txid_visible_in_snapshot(xid8, txid_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * txid_snapshot_xmin(txid_snapshot) returns xid8
*
* return snapshot's xmin
*/
@@ -661,11 +603,11 @@ txid_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * txid_snapshot_xmax(txid_snapshot) returns xid8
*
* return snapshot's xmax
*/
@@ -674,11 +616,11 @@ txid_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * txid_snapshot_xip(txid_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
*/
@@ -687,7 +629,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
- txid value;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
@@ -709,7 +651,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -731,7 +673,7 @@ Datum
txid_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +681,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index ba3ab7c979..a212d2fae8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9523,6 +9523,47 @@
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
+# xid8-based variants of txid functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'txid_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'txid_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'txid_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'txid_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'xid8_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'txid_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'txid_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'txid_status' },
+
# record comparison using normal comparison rules
{ oid => '2981',
proname => 'record_eq', prorettype => 'bool', proargtypes => 'record record',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1f441b8da..2f420889e2 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -460,6 +460,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 39c50afde6..899c38f5b1 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9419
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9419
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..efbd21af7e 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index e9e1fa8259..e0f7cf6fa8 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -134,3 +134,329 @@ 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;
+-- xid8_snapshot data type and related functions
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions shared C code)
+-- i/o
+select '12:13:'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:13:
+(1 row)
+
+select '12:18:14,16'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:18:14,16
+(1 row)
+
+select '12:16:14,14'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:16:14
+(1 row)
+
+-- errors
+select '31:12:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
+LINE 1: select '31:12:'::xid8_snapshot;
+ ^
+select '0:1:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
+LINE 1: select '0:1:'::xid8_snapshot;
+ ^
+select '12:13:0'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
+LINE 1: select '12:13:0'::xid8_snapshot;
+ ^
+select '12:16:14,13'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
+LINE 1: select '12:16:14,13'::xid8_snapshot;
+ ^
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+ snap
+-------------------------------------------------------------------------------------------------------------------------------------
+ 12:13:
+ 12:20:13,15,18
+ 100001:100009:100005,100007,100008
+ 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
+(4 rows)
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+ xid8_snapshot_xmin | xid8_snapshot_xmax | xid8_snapshot_xip
+--------------------+--------------------+-------------------
+ 12 | 20 | 13
+ 12 | 20 | 15
+ 12 | 20 | 18
+ 100001 | 100009 | 100005
+ 100001 | 100009 | 100007
+ 100001 | 100009 | 100008
+ 100 | 150 | 101
+ 100 | 150 | 102
+ 100 | 150 | 103
+ 100 | 150 | 104
+ 100 | 150 | 105
+ 100 | 150 | 106
+ 100 | 150 | 107
+ 100 | 150 | 108
+ 100 | 150 | 109
+ 100 | 150 | 110
+ 100 | 150 | 111
+ 100 | 150 | 112
+ 100 | 150 | 113
+ 100 | 150 | 114
+ 100 | 150 | 115
+ 100 | 150 | 116
+ 100 | 150 | 117
+ 100 | 150 | 118
+ 100 | 150 | 119
+ 100 | 150 | 120
+ 100 | 150 | 121
+ 100 | 150 | 122
+ 100 | 150 | 123
+ 100 | 150 | 124
+ 100 | 150 | 125
+ 100 | 150 | 126
+ 100 | 150 | 127
+ 100 | 150 | 128
+ 100 | 150 | 129
+ 100 | 150 | 130
+ 100 | 150 | 131
+(37 rows)
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+ id | xid8_visible_in_snapshot
+----+--------------------------
+ 11 | t
+ 12 | t
+ 13 | f
+ 14 | t
+ 15 | f
+ 16 | t
+ 17 | t
+ 18 | f
+ 19 | t
+ 20 | f
+ 21 | f
+(11 rows)
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+ id | xid8_visible_in_snapshot
+-----+--------------------------
+ 90 | t
+ 91 | t
+ 92 | t
+ 93 | t
+ 94 | t
+ 95 | t
+ 96 | t
+ 97 | t
+ 98 | t
+ 99 | t
+ 100 | t
+ 101 | f
+ 102 | f
+ 103 | f
+ 104 | f
+ 105 | f
+ 106 | f
+ 107 | f
+ 108 | f
+ 109 | f
+ 110 | f
+ 111 | f
+ 112 | f
+ 113 | f
+ 114 | f
+ 115 | f
+ 116 | f
+ 117 | f
+ 118 | f
+ 119 | f
+ 120 | f
+ 121 | f
+ 122 | f
+ 123 | f
+ 124 | f
+ 125 | f
+ 126 | f
+ 127 | f
+ 128 | f
+ 129 | f
+ 130 | f
+ 131 | f
+ 132 | t
+ 133 | t
+ 134 | t
+ 135 | t
+ 136 | t
+ 137 | t
+ 138 | t
+ 139 | t
+ 140 | t
+ 141 | t
+ 142 | t
+ 143 | t
+ 144 | t
+ 145 | t
+ 146 | t
+ 147 | t
+ 148 | t
+ 149 | t
+ 150 | f
+ 151 | f
+ 152 | f
+ 153 | f
+ 154 | f
+ 155 | f
+ 156 | f
+ 157 | f
+ 158 | f
+ 159 | f
+ 160 | f
+(71 rows)
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+ ?column?
+----------
+ t
+(1 row)
+
+-- we can't assume current is always less than xmax, however
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+-- test 64bitness
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+ xid8_snapshot
+---------------------------------------------------------------------
+ 1000100010001000:1000100010001100:1000100010001012,1000100010001013
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ t
+(1 row)
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+ xid8_snapshot
+-------------------------
+ 1:9223372036854775807:3
+(1 row)
+
+SELECT xid8_snapshot '1:9223372036854775808:3';
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
+LINE 1: SELECT xid8_snapshot '1:9223372036854775808:3';
+ ^
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+SELECT xid8_status(:committed::text::xid8) AS committed;
+ committed
+-----------
+ committed
+(1 row)
+
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+ rolledback
+------------
+ aborted
+(1 row)
+
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+ inprogress
+-------------
+ in progress
+(1 row)
+
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+ xid8_status
+-------------
+
+(1 row)
+
+COMMIT;
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+NOTICE: Got expected error for xid in the future
+ test_future_xid_status
+------------------------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql
index bd6decf0ef..8d5ac98a89 100644
--- a/src/test/regress/sql/txid.sql
+++ b/src/test/regress/sql/txid.sql
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index a4fbca5176..55cd21fe92 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -46,3 +46,107 @@ 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;
+
+
+-- xid8_snapshot data type and related functions
+
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions shared C code)
+
+-- i/o
+select '12:13:'::xid8_snapshot;
+select '12:18:14,16'::xid8_snapshot;
+select '12:16:14,14'::xid8_snapshot;
+
+-- errors
+select '31:12:'::xid8_snapshot;
+select '0:1:'::xid8_snapshot;
+select '12:13:0'::xid8_snapshot;
+select '12:16:14,13'::xid8_snapshot;
+
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+
+-- we can't assume current is always less than xmax, however
+
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+
+-- test 64bitness
+
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+SELECT xid8_snapshot '1:9223372036854775808:3';
+
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+COMMIT;
+
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+
+SELECT xid8_status(:committed::text::xid8) AS committed;
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+
+COMMIT;
+
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+ROLLBACK;
--
2.20.1
On Sat, Mar 21, 2020 at 11:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
* updated OIDs to avoid collisions
* added btequalimage to btree/xid8_ops
Here's the version I'm planning to commit tomorrow, if no one objects. Changes:
* txid.c renamed to xid8funcs.c
* remaining traces of "txid" replaced various internal identifiers
* s/backwards compatible/backward compatible/ in funcs.sgml (en_GB -> en_US)
Attachments:
v8-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchtext/x-patch; charset=US-ASCII; name=v8-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchDownload
From 308b094306f587a6736963983162290ac2808ac7 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Thu, 2 Apr 2020 15:00:23 +1300
Subject: [PATCH v8 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.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 7 ++
src/backend/access/hash/hashvalidate.c | 3 +
src/backend/utils/adt/xid.c | 116 +++++++++++++++++++
src/fe_utils/print.c | 1 +
src/include/access/transam.h | 14 +++
src/include/catalog/pg_amop.dat | 22 ++++
src/include/catalog/pg_amproc.dat | 8 ++
src/include/catalog/pg_cast.dat | 4 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 25 +++++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 36 ++++++
src/include/catalog/pg_type.dat | 4 +
src/include/utils/xid8.h | 22 ++++
src/test/regress/expected/opr_sanity.out | 7 ++
src/test/regress/expected/xid.out | 136 +++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/xid.sql | 48 ++++++++
19 files changed, 463 insertions(+), 1 deletion(-)
create mode 100644 src/include/utils/xid8.h
create mode 100644 src/test/regress/expected/xid.out
create mode 100644 src/test/regress/sql/xid.sql
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 03971822c2..89f3a7c119 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4516,6 +4516,10 @@ INSERT INTO mytable VALUES(-1); -- fails
<primary>regtype</primary>
</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 7b08ed5354..b3d1367fec 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 db6fc9dd6b..20389aff1d 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 06096cc86c..66a50f183f 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 6a947b958b..9a808f64eb 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/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..b5dfaad9ec 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 cef63b2a71..37b580883f 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 01c5328ddd..5a58f50fbb 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 ab2f50c9eb..f2342bb328 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 65c7fedf23..00ada7e48f 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 26227df216..4004138d77 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 55ef716858..3474c04bdf 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 2e6110e3f2..a1f441b8da 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 0000000000..288e62de9c
--- /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 2efd7d7ec7..0c03afe53d 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 0000000000..e9e1fa8259
--- /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 d2b17dd3ea..b4c100b1c0 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 acba391332..017aa3888f 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 0000000000..a4fbca5176
--- /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;
--
2.20.1
v8-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patchtext/x-patch; charset=US-ASCII; name=v8-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patchDownload
From 05210fce0563a2424b1b70f2ed94107eb60406f1 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Thu, 2 Apr 2020 15:18:18 +1300
Subject: [PATCH v8 2/2] Introduce xid8_XXX functions to replace txid_XXX.
The txid_XXX family of fmgr functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a new set of functions xid8_XXX. Keep the old functions around
too, for now.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions are the ones using the wrong signedness,
and since we'll presumably drop the older ones after a reasonable period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <hornschnorter@gmail.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 8 +-
doc/src/sgml/func.sgml | 140 +++++-
doc/src/sgml/logicaldecoding.sgml | 2 +-
doc/src/sgml/monitoring.sgml | 2 +-
src/backend/utils/adt/Makefile | 2 +-
src/backend/utils/adt/{txid.c => xid8funcs.c} | 414 ++++++++----------
src/include/catalog/pg_proc.dat | 67 ++-
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 13 +-
src/test/regress/expected/xid.out | 326 ++++++++++++++
src/test/regress/sql/txid.sql | 3 +
src/test/regress/sql/xid.sql | 104 +++++
13 files changed, 814 insertions(+), 283 deletions(-)
rename src/backend/utils/adt/{txid.c => xid8funcs.c} (55%)
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 89f3a7c119..91c9202458 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -279,7 +279,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
- <entry>user-level transaction ID snapshot</entry>
+ <entry>user-level transaction ID snapshot (see also <type>xid8_snapshot</type>)</entry>
</row>
<row>
@@ -288,6 +288,12 @@
<entry>universally unique identifier</entry>
</row>
+ <row>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry></entry>
+ <entry>user-level transaction ID snapshot</entry>
+ </row>
+
<row>
<entry><type>xml</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index cbfd2a762e..a16aba20eb 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18950,6 +18950,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -18983,12 +19015,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof xid8</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backward compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19000,42 +19094,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
@@ -19046,13 +19140,15 @@ SELECT collation for ('foo' COLLATE "de_DE");
wraps around every 4 billion transactions. However, these functions
export a 64-bit format that is extended with an <quote>epoch</quote> counter
so it will not wrap around during the life of an installation.
- The data type used by these functions, <type>txid_snapshot</type>,
+ The data type used by these functions, <type>xid8_snapshot</type>,
stores information about transaction ID
visibility at a particular moment in time. Its components are
- described in <xref linkend="functions-txid-snapshot-parts"/>.
+ described in <xref linkend="functions-xid8-snapshot-parts"/>. The
+ <type>xid8</type> values it contains can be converted to <type>xid</type>
+ by casting, if required.
</para>
- <table id="functions-txid-snapshot-parts">
+ <table id="functions-xid8-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@@ -19067,7 +19163,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
- Earliest transaction ID (txid) that is still active. All earlier
+ Earliest transaction ID (xid8) that is still active. All earlier
transactions will either be committed and visible, or rolled
back and dead.
</entry>
@@ -19076,7 +19172,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmax</type></entry>
<entry>
- First as-yet-unassigned txid. All txids greater than or equal to this
+ First as-yet-unassigned xid8. All xid8s greater than or equal to this
are not yet started as of the time of the snapshot, and thus invisible.
</entry>
</row>
@@ -19084,14 +19180,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xip_list</type></entry>
<entry>
- Active txids at the time of the snapshot. The list
- includes only those active txids between <literal>xmin</literal>
- and <literal>xmax</literal>; there might be active txids higher
- than <literal>xmax</literal>. A txid that is <literal>xmin <= txid <
+ Active xid8s at the time of the snapshot. The list
+ includes only those active xid8s between <literal>xmin</literal>
+ and <literal>xmax</literal>; there might be active xid8s higher
+ than <literal>xmax</literal>. An xid8 that is <literal>xmin <= xid8 <
xmax</literal> and not in this list was already completed
at the time of the snapshot, and thus either visible or
dead according to its commit status. The list does not
- include txids of subtransactions.
+ include xid8s of subtransactions.
</entry>
</row>
@@ -19100,14 +19196,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
- <type>txid_snapshot</type>'s textual representation is
+ <type>xid8_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
- <function>txid_status(bigint)</function> reports the commit status of a recent
+ <function>xid8_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@@ -19121,7 +19217,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
- need to determine whether the txid is a prepared transaction.
+ need to determine whether the xid8 is a prepared transaction.
</para>
<para>
diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index bce6d379bf..e8e6e36a27 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
- calling <literal>txid_current()</literal>.
+ calling <literal>xid8_current()</literal>.
</para>
</sect2>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 28ceb04d33..25d905d111 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
- <entry>Waiting to execute <function>txid_status</function> or update
+ <entry>Waiting to execute <function>xid8_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..5d2aca8cfe 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -101,7 +101,6 @@ OBJS = \
tsvector.o \
tsvector_op.o \
tsvector_parser.o \
- txid.o \
uuid.o \
varbit.o \
varchar.o \
@@ -109,6 +108,7 @@ OBJS = \
version.o \
windowfuncs.o \
xid.o \
+ xid8funcs.o \
xml.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/xid8funcs.c
similarity index 55%
rename from src/backend/utils/adt/txid.c
rename to src/backend/utils/adt/xid8funcs.c
index 33272f8030..873604eeb8 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -1,14 +1,19 @@
/*-------------------------------------------------------------------------
- * txid.c
+ * xid8funcs.c
*
* Export internal transaction IDs to user level.
*
- * Note that only top-level transaction IDs are ever converted to TXID.
- * This is important because TXIDs frequently persist beyond the global
+ * Note that only top-level transaction IDs are exposed to user sessions.
+ * This is important because xid8s frequently persist beyond the global
* xmin horizon, or may even be shipped to other machines, so we cannot
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. The txid_XXX variants should
+ * eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,25 +39,18 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
- * If defined, use bsearch() function for searching for txids in snapshots
+ * If defined, use bsearch() function for searching for xid8s in snapshots
* that have more than the specified number of values.
*/
#define USE_BSEARCH_IF_NXIP_GREATER 30
/*
- * Snapshot containing 8byte txids.
+ * Snapshot containing FullTransactionIds.
*/
typedef struct
{
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
-} TxidSnapshot;
-
-#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
-#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
+} Xid8Snapshot;
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+#define XID8_SNAPSHOT_SIZE(nxip) \
+ (offsetof(Xid8Snapshot, xip) + sizeof(FullTransactionId) * (nxip))
+#define XID8_SNAPSHOT_MAX_NXIP \
+ ((MaxAllocSize - offsetof(Xid8Snapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Convert a TransactionId obtained from a snapshot held by the caller to a
+ * FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
+ * we can compute the high order bits. It must have been obtained by the
+ * caller with ReadNextFullTransactionId() after the snapshot was created.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
- /* return special xid's as-is */
+ /* Special transaction ID. */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ /*
+ * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
+ * issued yet when the snapshot was created. Every TransactionId in the
+ * snapshot must therefore be from the same epoch as next_fxid, or the
+ * epoch before. We know this because next_fxid is never allow to get more
+ * than one epoch ahead of the TransactionIds in any snapshot.
+ */
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -211,31 +193,33 @@ cmp_txid(const void *aa, const void *bb)
* will not be used.
*/
static void
-sort_snapshot(TxidSnapshot *snap)
+sort_snapshot(Xid8Snapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const Xid8Snapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -254,13 +238,13 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
}
/*
- * helper functions to use StringInfo for TxidSnapshot creation.
+ * helper functions to use StringInfo for Xid8Snapshot creation.
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
- TxidSnapshot snap;
+ Xid8Snapshot snap;
StringInfo buf;
snap.xmin = xmin;
@@ -268,25 +252,25 @@ buf_init(txid xmin, txid xmax)
snap.nxip = 0;
buf = makeStringInfo();
- appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
+ appendBinaryStringInfo(buf, (char *) &snap, XID8_SNAPSHOT_SIZE(0));
return buf;
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ Xid8Snapshot *snap = (Xid8Snapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
-static TxidSnapshot *
+static Xid8Snapshot *
buf_finalize(StringInfo buf)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ Xid8Snapshot *snap = (Xid8Snapshot *) buf->data;
SET_VARSIZE(snap, buf->len);
@@ -297,68 +281,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
-static TxidSnapshot *
+static Xid8Snapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +318,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,108 +344,90 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "xid8_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
/*
* Public functions.
*
- * txid_current() and txid_current_snapshot() are the only ones that
+ * xid8_current() and xid8_current_snapshot() are the only ones that
* communicate with core xid machinery. All the others work on data
* returned by them.
*/
/*
- * txid_current() returns int8
+ * xid8_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
-txid_current(PG_FUNCTION_ARGS)
+xid8_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
+ PreventCommandDuringRecovery("xid8_current()");
- val = convert_xid(GetTopTransactionId(), &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
- * Same as txid_current() but doesn't assign a new xid if there isn't one
+ * Same as xid8_current() but doesn't assign a new xid if there isn't one
* yet.
*/
Datum
-txid_current_if_assigned(PG_FUNCTION_ARGS)
+xid8_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
- * txid_current_snapshot() returns txid_snapshot
+ * xid8_current_snapshot() returns xid8_snapshot
*
- * Return current snapshot in TXID format
+ * Return current snapshot
*
* Note that only top-transaction XIDs are included in the snapshot.
*/
Datum
-txid_current_snapshot(PG_FUNCTION_ARGS)
+xid8_current_snapshot(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap;
+ Xid8Snapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
*/
- StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
- "possible overflow in txid_current_snapshot()");
+ StaticAssertStmt(MAX_BACKENDS * 2 <= XID8_SNAPSHOT_MAX_NXIP,
+ "possible overflow in xid8_current_snapshot()");
/* allocate */
nxip = cur->xcnt;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(XID8_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
+ snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -505,21 +439,21 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
sort_snapshot(snap);
/* set size after sorting, because it may have removed duplicate xips */
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
+ SET_VARSIZE(snap, XID8_SNAPSHOT_SIZE(snap->nxip));
PG_RETURN_POINTER(snap);
}
/*
- * txid_snapshot_in(cstring) returns txid_snapshot
+ * xid8_snapshot_in(cstring) returns xid8_snapshot
*
- * input function for type txid_snapshot
+ * input function for type xid8_snapshot
*/
Datum
-txid_snapshot_in(PG_FUNCTION_ARGS)
+xid8_snapshot_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- TxidSnapshot *snap;
+ Xid8Snapshot *snap;
snap = parse_snapshot(str);
@@ -527,73 +461,81 @@ txid_snapshot_in(PG_FUNCTION_ARGS)
}
/*
- * txid_snapshot_out(txid_snapshot) returns cstring
+ * xid8_snapshot_out(xid8_snapshot) returns cstring
*
- * output function for type txid_snapshot
+ * output function for type xid8_snapshot
*/
Datum
-txid_snapshot_out(PG_FUNCTION_ARGS)
+xid8_snapshot_out(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData str;
uint32 i;
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
}
/*
- * txid_snapshot_recv(internal) returns txid_snapshot
+ * xid8_snapshot_recv(internal) returns xid8_snapshot
*
- * binary input function for type txid_snapshot
+ * binary input function for type xid8_snapshot
*
* format: int4 nxip, int8 xmin, int8 xmax, int8 xip
*/
Datum
-txid_snapshot_recv(PG_FUNCTION_ARGS)
+xid8_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- TxidSnapshot *snap;
- txid last = 0;
+ Xid8Snapshot *snap;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
- if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
+ if (nxip < 0 || nxip > XID8_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(XID8_SNAPSHOT_SIZE(nxip));
snap->xmin = xmin;
snap->xmax = xmax;
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -604,95 +546,95 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
last = cur;
}
snap->nxip = nxip;
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
+ SET_VARSIZE(snap, XID8_SNAPSHOT_SIZE(nxip));
PG_RETURN_POINTER(snap);
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external xid8_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
/*
- * txid_snapshot_send(txid_snapshot) returns bytea
+ * xid8_snapshot_send(xid8_snapshot) returns bytea
*
- * binary output function for type txid_snapshot
+ * binary output function for type xid8_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
-txid_snapshot_send(PG_FUNCTION_ARGS)
+xid8_snapshot_send(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData buf;
uint32 i;
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * xid8_visible_in_snapshot(xid8, xid8_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
-txid_visible_in_snapshot(PG_FUNCTION_ARGS)
+xid8_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * xid8_snapshot_xmin(xid8_snapshot) returns xid8
*
* return snapshot's xmin
*/
Datum
-txid_snapshot_xmin(PG_FUNCTION_ARGS)
+xid8_snapshot_xmin(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * xid8_snapshot_xmax(xid8_snapshot) returns xid8
*
* return snapshot's xmax
*/
Datum
-txid_snapshot_xmax(PG_FUNCTION_ARGS)
+xid8_snapshot_xmax(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * xid8_snapshot_xip(xid8_snapshot) returns setof xid8
*
- * return in-progress TXIDs in snapshot.
+ * return in-progress xid8s in snapshot.
*/
Datum
-txid_snapshot_xip(PG_FUNCTION_ARGS)
+xid8_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
- TxidSnapshot *snap;
- txid value;
+ Xid8Snapshot *snap;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
{
- TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *arg = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
fctx = SRF_FIRSTCALL_INIT();
@@ -709,7 +651,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -728,10 +670,10 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
* though the parent xact may still be in progress or may have aborted.
*/
Datum
-txid_status(PG_FUNCTION_ARGS)
+xid8_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +681,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 3474c04bdf..1dbacbe064 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9505,46 +9505,87 @@
proname => 'jsonb_path_match_opr', prorettype => 'bool',
proargtypes => 'jsonb jsonpath', prosrc => 'jsonb_path_match_opr' },
-# txid
+# historical int8/txid_snapshot variants of xid8 functions
{ oid => '2939', descr => 'I/O',
proname => 'txid_snapshot_in', prorettype => 'txid_snapshot',
- proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+ proargtypes => 'cstring', prosrc => 'xid8_snapshot_in' },
{ oid => '2940', descr => 'I/O',
proname => 'txid_snapshot_out', prorettype => 'cstring',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_out' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_out' },
{ oid => '2941', descr => 'I/O',
proname => 'txid_snapshot_recv', prorettype => 'txid_snapshot',
- proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+ proargtypes => 'internal', prosrc => 'xid8_snapshot_recv' },
{ oid => '2942', descr => 'I/O',
proname => 'txid_snapshot_send', prorettype => 'bytea',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_send' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_send' },
{ oid => '2943', descr => 'get current transaction ID',
proname => 'txid_current', provolatile => 's', proparallel => 'u',
- prorettype => 'int8', proargtypes => '', prosrc => 'txid_current' },
+ prorettype => 'int8', proargtypes => '', prosrc => 'xid8_current' },
{ oid => '3348', descr => 'get current transaction ID',
proname => 'txid_current_if_assigned', provolatile => 's', proparallel => 'u',
prorettype => 'int8', proargtypes => '',
- prosrc => 'txid_current_if_assigned' },
+ prosrc => 'xid8_current_if_assigned' },
{ oid => '2944', descr => 'get current snapshot',
proname => 'txid_current_snapshot', provolatile => 's',
prorettype => 'txid_snapshot', proargtypes => '',
- prosrc => 'txid_current_snapshot' },
+ prosrc => 'xid8_current_snapshot' },
{ oid => '2945', descr => 'get xmin of snapshot',
proname => 'txid_snapshot_xmin', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmin' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_xmin' },
{ oid => '2946', descr => 'get xmax of snapshot',
proname => 'txid_snapshot_xmax', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmax' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_xmax' },
{ oid => '2947', descr => 'get set of in-progress txids in snapshot',
proname => 'txid_snapshot_xip', prorows => '50', proretset => 't',
prorettype => 'int8', proargtypes => 'txid_snapshot',
- prosrc => 'txid_snapshot_xip' },
+ prosrc => 'xid8_snapshot_xip' },
{ oid => '2948', descr => 'is txid visible in snapshot?',
proname => 'txid_visible_in_snapshot', prorettype => 'bool',
- proargtypes => 'int8 txid_snapshot', prosrc => 'txid_visible_in_snapshot' },
+ proargtypes => 'int8 txid_snapshot', prosrc => 'xid8_visible_in_snapshot' },
{ oid => '3360', descr => 'commit status of transaction',
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
- proargtypes => 'int8', prosrc => 'txid_status' },
+ proargtypes => 'int8', prosrc => 'xid8_status' },
+
+# xid8 functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'xid8_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'xid8_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'xid8_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'xid8_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'xid8_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'xid8_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'xid8_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'xid8_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'xid8_status' },
# record comparison using normal comparison rules
{ oid => '2981',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1f441b8da..2f420889e2 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -460,6 +460,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0c03afe53d..8d350ab61b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9419
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9419
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..efbd21af7e 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index e9e1fa8259..2d24388b59 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -134,3 +134,329 @@ 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;
+-- xid8_snapshot data type and related functions
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+-- i/o
+select '12:13:'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:13:
+(1 row)
+
+select '12:18:14,16'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:18:14,16
+(1 row)
+
+select '12:16:14,14'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:16:14
+(1 row)
+
+-- errors
+select '31:12:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
+LINE 1: select '31:12:'::xid8_snapshot;
+ ^
+select '0:1:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
+LINE 1: select '0:1:'::xid8_snapshot;
+ ^
+select '12:13:0'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
+LINE 1: select '12:13:0'::xid8_snapshot;
+ ^
+select '12:16:14,13'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
+LINE 1: select '12:16:14,13'::xid8_snapshot;
+ ^
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+ snap
+-------------------------------------------------------------------------------------------------------------------------------------
+ 12:13:
+ 12:20:13,15,18
+ 100001:100009:100005,100007,100008
+ 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
+(4 rows)
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+ xid8_snapshot_xmin | xid8_snapshot_xmax | xid8_snapshot_xip
+--------------------+--------------------+-------------------
+ 12 | 20 | 13
+ 12 | 20 | 15
+ 12 | 20 | 18
+ 100001 | 100009 | 100005
+ 100001 | 100009 | 100007
+ 100001 | 100009 | 100008
+ 100 | 150 | 101
+ 100 | 150 | 102
+ 100 | 150 | 103
+ 100 | 150 | 104
+ 100 | 150 | 105
+ 100 | 150 | 106
+ 100 | 150 | 107
+ 100 | 150 | 108
+ 100 | 150 | 109
+ 100 | 150 | 110
+ 100 | 150 | 111
+ 100 | 150 | 112
+ 100 | 150 | 113
+ 100 | 150 | 114
+ 100 | 150 | 115
+ 100 | 150 | 116
+ 100 | 150 | 117
+ 100 | 150 | 118
+ 100 | 150 | 119
+ 100 | 150 | 120
+ 100 | 150 | 121
+ 100 | 150 | 122
+ 100 | 150 | 123
+ 100 | 150 | 124
+ 100 | 150 | 125
+ 100 | 150 | 126
+ 100 | 150 | 127
+ 100 | 150 | 128
+ 100 | 150 | 129
+ 100 | 150 | 130
+ 100 | 150 | 131
+(37 rows)
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+ id | xid8_visible_in_snapshot
+----+--------------------------
+ 11 | t
+ 12 | t
+ 13 | f
+ 14 | t
+ 15 | f
+ 16 | t
+ 17 | t
+ 18 | f
+ 19 | t
+ 20 | f
+ 21 | f
+(11 rows)
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+ id | xid8_visible_in_snapshot
+-----+--------------------------
+ 90 | t
+ 91 | t
+ 92 | t
+ 93 | t
+ 94 | t
+ 95 | t
+ 96 | t
+ 97 | t
+ 98 | t
+ 99 | t
+ 100 | t
+ 101 | f
+ 102 | f
+ 103 | f
+ 104 | f
+ 105 | f
+ 106 | f
+ 107 | f
+ 108 | f
+ 109 | f
+ 110 | f
+ 111 | f
+ 112 | f
+ 113 | f
+ 114 | f
+ 115 | f
+ 116 | f
+ 117 | f
+ 118 | f
+ 119 | f
+ 120 | f
+ 121 | f
+ 122 | f
+ 123 | f
+ 124 | f
+ 125 | f
+ 126 | f
+ 127 | f
+ 128 | f
+ 129 | f
+ 130 | f
+ 131 | f
+ 132 | t
+ 133 | t
+ 134 | t
+ 135 | t
+ 136 | t
+ 137 | t
+ 138 | t
+ 139 | t
+ 140 | t
+ 141 | t
+ 142 | t
+ 143 | t
+ 144 | t
+ 145 | t
+ 146 | t
+ 147 | t
+ 148 | t
+ 149 | t
+ 150 | f
+ 151 | f
+ 152 | f
+ 153 | f
+ 154 | f
+ 155 | f
+ 156 | f
+ 157 | f
+ 158 | f
+ 159 | f
+ 160 | f
+(71 rows)
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+ ?column?
+----------
+ t
+(1 row)
+
+-- we can't assume current is always less than xmax, however
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+-- test 64bitness
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+ xid8_snapshot
+---------------------------------------------------------------------
+ 1000100010001000:1000100010001100:1000100010001012,1000100010001013
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ t
+(1 row)
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+ xid8_snapshot
+-------------------------
+ 1:9223372036854775807:3
+(1 row)
+
+SELECT xid8_snapshot '1:9223372036854775808:3';
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
+LINE 1: SELECT xid8_snapshot '1:9223372036854775808:3';
+ ^
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+SELECT xid8_status(:committed::text::xid8) AS committed;
+ committed
+-----------
+ committed
+(1 row)
+
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+ rolledback
+------------
+ aborted
+(1 row)
+
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+ inprogress
+-------------
+ in progress
+(1 row)
+
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+ xid8_status
+-------------
+
+(1 row)
+
+COMMIT;
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+NOTICE: Got expected error for xid in the future
+ test_future_xid_status
+------------------------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql
index bd6decf0ef..8d5ac98a89 100644
--- a/src/test/regress/sql/txid.sql
+++ b/src/test/regress/sql/txid.sql
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index a4fbca5176..8c098906e9 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -46,3 +46,107 @@ 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;
+
+
+-- xid8_snapshot data type and related functions
+
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+
+-- i/o
+select '12:13:'::xid8_snapshot;
+select '12:18:14,16'::xid8_snapshot;
+select '12:16:14,14'::xid8_snapshot;
+
+-- errors
+select '31:12:'::xid8_snapshot;
+select '0:1:'::xid8_snapshot;
+select '12:13:0'::xid8_snapshot;
+select '12:16:14,13'::xid8_snapshot;
+
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+
+-- we can't assume current is always less than xmax, however
+
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+
+-- test 64bitness
+
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+SELECT xid8_snapshot '1:9223372036854775808:3';
+
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+COMMIT;
+
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+
+SELECT xid8_status(:committed::text::xid8) AS committed;
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+
+COMMIT;
+
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+ROLLBACK;
--
2.20.1
On Apr 1, 2020, at 8:21 PM, Thomas Munro <thomas.munro@gmail.com> wrote:
On Sat, Mar 21, 2020 at 11:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
* updated OIDs to avoid collisions
* added btequalimage to btree/xid8_opsHere's the version I'm planning to commit tomorrow, if no one objects. Changes:
* txid.c renamed to xid8funcs.c
* remaining traces of "txid" replaced various internal identifiers
* s/backwards compatible/backward compatible/ in funcs.sgml (en_GB -> en_US)
<v8-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patch><v8-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patch>
Hi Thomas, Thanks for working on this.
I'm taking a quick look at your patches. It's not a big deal, and certainly not a show stopper if you want to go ahead with the commit, but you've left some mentions of "txid_current" that might better be modified to use the new name "xid8_current". At least one mention of "txid_current" is needed to check that the old name still works, but leaving this many in the regression test suite may lead other developers to follow that lead and use txid_current() in newly developed code. ("xid8_current" is not exercised by name anywhere in the regression suite, that I can see.)
contrib/test_decoding/expected/ddl.out:SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
contrib/test_decoding/expected/decoding_in_xact.out:SELECT txid_current() = 0;
contrib/test_decoding/expected/decoding_in_xact.out:SELECT txid_current() = 0;
contrib/test_decoding/expected/decoding_in_xact.out:SELECT txid_current() = 0;
contrib/test_decoding/expected/oldest_xmin.out:step s0_getxid: SELECT txid_current() IS NULL;
contrib/test_decoding/expected/ondisk_startup.out:step s2txid: SELECT txid_current() IS NULL;
contrib/test_decoding/expected/ondisk_startup.out:step s3txid: SELECT txid_current() IS NULL;
contrib/test_decoding/expected/ondisk_startup.out:step s2txid: SELECT txid_current() IS NULL;
contrib/test_decoding/expected/snapshot_transfer.out:step s0_log_assignment: SELECT txid_current() IS NULL;
contrib/test_decoding/expected/snapshot_transfer.out:step s0_log_assignment: SELECT txid_current() IS NULL;
contrib/test_decoding/specs/oldest_xmin.spec:step "s0_getxid" { SELECT txid_current() IS NULL; }
contrib/test_decoding/specs/ondisk_startup.spec:step "s2txid" { SELECT txid_current() IS NULL; }
contrib/test_decoding/specs/ondisk_startup.spec:step "s3txid" { SELECT txid_current() IS NULL; }
contrib/test_decoding/specs/snapshot_transfer.spec:step "s0_log_assignment" { SELECT txid_current() IS NULL; }
contrib/test_decoding/sql/ddl.sql:SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
contrib/test_decoding/sql/decoding_in_xact.sql:SELECT txid_current() = 0;
contrib/test_decoding/sql/decoding_in_xact.sql:SELECT txid_current() = 0;
contrib/test_decoding/sql/decoding_in_xact.sql:SELECT txid_current() = 0;src/test/modules/commit_ts/t/004_restart.pl: SELECT txid_current();
src/test/modules/commit_ts/t/004_restart.pl: EXECUTE 'SELECT txid_current()';
src/test/modules/commit_ts/t/004_restart.pl: SELECT txid_current();
src/test/recovery/t/003_recovery_targets.pl: "SELECT pg_current_wal_lsn(), txid_current();");
src/test/recovery/t/011_crash_recovery.pl:SELECT txid_current();
src/test/recovery/t/011_crash_recovery.pl:cmp_ok($node->safe_psql('postgres', 'SELECT txid_current()'),
src/test/regress/expected/alter_table.out: where transactionid = txid_current()::integer)
src/test/regress/expected/alter_table.out: where transactionid = txid_current()::integer)
src/test/regress/expected/hs_standby_functions.out:select txid_current();
src/test/regress/expected/hs_standby_functions.out:ERROR: cannot execute txid_current() during recovery
src/test/regress/expected/hs_standby_functions.out:select length(txid_current_snapshot()::text) >= 4;
src/test/regress/expected/txid.out:select txid_current() >= txid_snapshot_xmin(txid_current_snapshot());
src/test/regress/expected/txid.out:select txid_visible_in_snapshot(txid_current(), txid_current_snapshot());
src/test/regress/expected/txid.out:-- test txid_current_if_assigned
src/test/regress/expected/txid.out:SELECT txid_current_if_assigned() IS NULL;
src/test/regress/expected/txid.out:SELECT txid_current() \gset
src/test/regress/expected/txid.out:SELECT txid_current_if_assigned() IS NOT DISTINCT FROM BIGINT :'txid_current';
src/test/regress/expected/txid.out:SELECT txid_current() AS committed \gset
src/test/regress/expected/txid.out:SELECT txid_current() AS rolledback \gset
src/test/regress/expected/txid.out:SELECT txid_current() AS inprogress \gset
src/test/regress/expected/update.out:CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
src/test/regress/sql/alter_table.sql: where transactionid = txid_current()::integer)
src/test/regress/sql/alter_table.sql: where transactionid = txid_current()::integer)
src/test/regress/sql/hs_standby_functions.sql:select txid_current();
src/test/regress/sql/hs_standby_functions.sql:select length(txid_current_snapshot()::text) >= 4;
src/test/regress/sql/txid.sql:select txid_current() >= txid_snapshot_xmin(txid_current_snapshot());
src/test/regress/sql/txid.sql:select txid_visible_in_snapshot(txid_current(), txid_current_snapshot());
src/test/regress/sql/txid.sql:-- test txid_current_if_assigned
src/test/regress/sql/txid.sql:SELECT txid_current_if_assigned() IS NULL;
src/test/regress/sql/txid.sql:SELECT txid_current() \gset
src/test/regress/sql/txid.sql:SELECT txid_current_if_assigned() IS NOT DISTINCT FROM BIGINT :'txid_current';
src/test/regress/sql/txid.sql:SELECT txid_current() AS committed \gset
src/test/regress/sql/txid.sql:SELECT txid_current() AS rolledback \gset
src/test/regress/sql/txid.sql:SELECT txid_current() AS inprogress \gset
src/test/regress/sql/update.sql:CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
A reasonable argument could be made for treating txid_current as the preferred form, and xid8_current merely as a synonym, but then I can't make sense of the change your patch makes to the docs:
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backward compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
which looks like a txid deprecation warning to me.
Am I reading all this wrong? If I'm reading this right, then FYI there are similar s/txid_(.*)/xid8_$1/g changes to be made that I didn't bother listing here by name.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Apr 2, 2020, at 9:06 AM, Mark Dilger <mark.dilger@enterprisedb.com> wrote:
("xid8_current" is not exercised by name anywhere in the regression suite, that I can see.)
I spoke too soon. That's exercised in the new xid.sql test file. It didn't show up in my 'git diff', because it's new. Sorry about that.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On 2020-Apr-02, Thomas Munro wrote:
On Sat, Mar 21, 2020 at 11:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
* updated OIDs to avoid collisions
* added btequalimage to btree/xid8_opsHere's the version I'm planning to commit tomorrow, if no one objects. Changes:
* txid.c renamed to xid8funcs.c
* remaining traces of "txid" replaced various internal identifiers
* s/backwards compatible/backward compatible/ in funcs.sgml (en_GB -> en_US)
Hmm, for some reason I had it in my head that we would make these use an
"epoch/val" output format rather than raw uint64 values. Are we really
going to do it this way? Myself I can convert values easily enough, but
I'm not sure this is user-friendly. (If somebody were to tell me that
LSNs are going to be straight uint64 values, I would not be happy.)
Or maybe it's the other way around -- this is fine for everyone except
me -- and we should never expose the epoch as a separate quantity.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
Hi,
On 2020-04-02 14:33:18 -0300, Alvaro Herrera wrote:
On 2020-Apr-02, Thomas Munro wrote:
On Sat, Mar 21, 2020 at 11:14 AM Thomas Munro <thomas.munro@gmail.com> wrote:
* updated OIDs to avoid collisions
* added btequalimage to btree/xid8_opsHere's the version I'm planning to commit tomorrow, if no one objects. Changes:
* txid.c renamed to xid8funcs.c
* remaining traces of "txid" replaced various internal identifiers
* s/backwards compatible/backward compatible/ in funcs.sgml (en_GB -> en_US)Hmm, for some reason I had it in my head that we would make these use an
"epoch/val" output format rather than raw uint64 values.
Why would we do that? IMO the goal should be to reduce awareness of the
32bitness of normal xids from as many places as possible, and treat them
as an internal space optimization.
Greetings,
Andres Freund
On Apr 2, 2020, at 11:01 AM, Andres Freund <andres@anarazel.de> wrote:
Hmm, for some reason I had it in my head that we would make these use an
"epoch/val" output format rather than raw uint64 values.Why would we do that? IMO the goal should be to reduce awareness of the
32bitness of normal xids from as many places as possible, and treat them
as an internal space optimization.
I agree with transitioning to 64-bit xids with 32 bit xid/epoch pairs as an internal implementation and storage detail only, but we still have user facing views that don't treat it that way. pg_stat_get_activity still returns backend_xid and backend_xmin as 32-bit, not 64-bit. Should this function change to be consistent? I'm curious what the user experience will be during the transitional period where some user facing xids are 64 bit and others (perhaps the same xids but viewed elsewhere) will be 32 bit. That might make it difficult for users to match them up.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Andres Freund <andres@anarazel.de> writes:
On 2020-04-02 14:33:18 -0300, Alvaro Herrera wrote:
Hmm, for some reason I had it in my head that we would make these use an
"epoch/val" output format rather than raw uint64 values.
Why would we do that? IMO the goal should be to reduce awareness of the
32bitness of normal xids from as many places as possible, and treat them
as an internal space optimization.
If they're just int64s then you don't need special functions to do
things like finding the min or max in a column of them.
regards, tom lane
On Thu, Apr 2, 2020 at 2:47 PM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
On Apr 2, 2020, at 11:01 AM, Andres Freund <andres@anarazel.de> wrote:
Hmm, for some reason I had it in my head that we would make these use an
"epoch/val" output format rather than raw uint64 values.Why would we do that? IMO the goal should be to reduce awareness of the
32bitness of normal xids from as many places as possible, and treat them
as an internal space optimization.I agree with transitioning to 64-bit xids with 32 bit xid/epoch pairs as an internal implementation and storage detail only, but we still have user facing views that don't treat it that way. pg_stat_get_activity still returns backend_xid and backend_xmin as 32-bit, not 64-bit. Should this function change to be consistent? I'm curious what the user experience will be during the transitional period where some user facing xids are 64 bit and others (perhaps the same xids but viewed elsewhere) will be 32 bit. That might make it difficult for users to match them up.
Agreed. The "benefit" (at least in the short term) of using the
epoch/value style is that it makes (visual, at least) comparison with
other (32-bit) xid values easier.
I'm not sure if that's worth it, or if it's worth making a change
depend on changing all of those views too.
James
Hi,
On 2020-04-02 11:47:32 -0700, Mark Dilger wrote:
I agree with transitioning to 64-bit xids with 32 bit xid/epoch pairs
as an internal implementation and storage detail only, but we still
have user facing views that don't treat it that way.
Note that epochs are not really a thing internally anymore. The xid
counter is a FullTransactionId.
pg_stat_get_activity still returns backend_xid and backend_xmin as
32-bit, not 64-bit. Should this function change to be consistent? I'm
curious what the user experience will be during the transitional period
where some user facing xids are 64 bit and others (perhaps the same xids
but viewed elsewhere) will be 32 bit. That might make it difficult for
users to match them up.
I think we probably should switch them over at some point, but I would
strongly advise against coupling that with Thomas' patch. That patch
doesn't make the current situation around 32bit / 64bit any worse, as
far as I can tell.
Given that txid_current() "always" has been a plain 64 bit integer, and
the various txid_* functions always have returned 64 bit integers, I
really don't think arguing for some 32bit/32bit situation now makes
sense.
Greetings,
Andres Freund
On Apr 2, 2020, at 2:13 PM, Andres Freund <andres@anarazel.de> wrote:
Hi,
On 2020-04-02 11:47:32 -0700, Mark Dilger wrote:
I agree with transitioning to 64-bit xids with 32 bit xid/epoch pairs
as an internal implementation and storage detail only, but we still
have user facing views that don't treat it that way.Note that epochs are not really a thing internally anymore. The xid
counter is a FullTransactionId.pg_stat_get_activity still returns backend_xid and backend_xmin as
32-bit, not 64-bit. Should this function change to be consistent? I'm
curious what the user experience will be during the transitional period
where some user facing xids are 64 bit and others (perhaps the same xids
but viewed elsewhere) will be 32 bit. That might make it difficult for
users to match them up.I think we probably should switch them over at some point, but I would
strongly advise against coupling that with Thomas' patch. That patch
doesn't make the current situation around 32bit / 64bit any worse, as
far as I can tell.
I agree with that.
Given that txid_current() "always" has been a plain 64 bit integer, and
the various txid_* functions always have returned 64 bit integers, I
really don't think arguing for some 32bit/32bit situation now makes
sense.
Yeah, I'm not arguing for that, though I can see how my email might have been ambiguous on that point.
Since Thomas's patch is really just focused on transitioning from txid -> xid8, I think this conversation is a bit beyond scope for this patch, except that "xid8" sounds an awful lot like the new type that all user facing xid output will transition to. Maybe I'm wrong about that. Are we going to change the definition of the "xid" type to 8 bytes? That sounds dangerous, from a compatibility standpoint.
On balance, I'd rather have xid8in and xid8out work just as Thomas has it. I'm not asking for any change there. But I'm curious if the whole community is on the same page regarding where this is all heading.
I'm contemplating the statement that "the goal should be to reduce awareness of the 32bitness of normal xids from as many places as possible", which I support, and what that means for the eventual signatures of functions like pg_stat_get_activity, including:
(..., backend_xid XID, backend_xminxid XID, ...) pg_stat_get_activity(pid INTEGER)
(..., transactionid XID, ...) pg_lock_status()
(transaction XID, ...) pg_prepared_xact()
timestamptz pg_xact_commit_timestamp(XID)
(xid XID, ...) pg_last_committed_xact()
(..., xmin XID, catalog_xmin XID, ...) pg_get_replication_slots()
... more that I'm too lazy to copy-and-paste just now ...
I would expect that, eventually, these would be upgraded to xid8. If that happened seemlessly in one release, then there would be no problem with some functions returning 4-byte xids and some returning 8-byte xids, but otherwise there would be a transition period where some have been reworked to return xid8 but others not, and users during that transition period might be happier with Alvaro's suggestion of treating epoch/xid as two fields in xid8in and xid8out. I'm also doubtful that these functions would be "upgraded". It seems far more likely that alternate versions, perhaps named something with /xid8/ in them, would exist side-by-side with the originals.
So I'm really just wondering where others on this list think all this is heading, and if there are any arguments brewing about that which could be avoided by making assumptions clear right up front.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
On 2020-04-02 14:26:41 -0700, Mark Dilger wrote:
Since Thomas's patch is really just focused on transitioning from txid
-> xid8, I think this conversation is a bit beyond scope for this
patch, except that "xid8" sounds an awful lot like the new type that
all user facing xid output will transition to. Maybe I'm wrong about
that.
Several at least. For me it'd e.g. make no sense to change pageinspect
etc.
Are we going to change the definition of the "xid" type to 8 bytes?
That sounds dangerous, from a compatibility standpoint.
No, I can't see that happening.
On balance, I'd rather have xid8in and xid8out work just as Thomas has
it. I'm not asking for any change there. But I'm curious if the
whole community is on the same page regarding where this is all
heading.I'm contemplating the statement that "the goal should be to reduce
awareness of the 32bitness of normal xids from as many places as
possible", which I support, and what that means for the eventual
signatures of functions like pg_stat_get_activity, including:
Maybe. Aiming to do things like this all-at-once just makes it less
likely for anything to ever happen.
but otherwise there would be a transition period where some have been
reworked to return xid8 but others not, and users during that
transition period might be happier with Alvaro's suggestion of
treating epoch/xid as two fields in xid8in and xid8out.
-countless
I can only restate my point that we've had 8 byte xids exposed for many
years. We've had very few epoch/xid values exposed. I think it'd be
insane to now start to expose that more widely.
It's just about impossible for normal users to compare xids. Once one
wrapped around, it's just too hard/mindbending. Yes, an accompanying
epoch makes it easier, but it still can be quite confusing.
Greetings,
Andres Freund
On Fri, Apr 3, 2020 at 10:37 AM Andres Freund <andres@anarazel.de> wrote:
On 2020-04-02 14:26:41 -0700, Mark Dilger wrote:
On balance, I'd rather have xid8in and xid8out work just as Thomas has
it. I'm not asking for any change there. But I'm curious if the
whole community is on the same page regarding where this is all
heading.I'm contemplating the statement that "the goal should be to reduce
awareness of the 32bitness of normal xids from as many places as
possible", which I support, and what that means for the eventual
signatures of functions like pg_stat_get_activity, including:Maybe. Aiming to do things like this all-at-once just makes it less
likely for anything to ever happen.
Agreed. Let's just keep chipping away at this stuff.
but otherwise there would be a transition period where some have been
reworked to return xid8 but others not, and users during that
transition period might be happier with Alvaro's suggestion of
treating epoch/xid as two fields in xid8in and xid8out.-countless
I can only restate my point that we've had 8 byte xids exposed for many
years. We've had very few epoch/xid values exposed. I think it'd be
insane to now start to expose that more widely.It's just about impossible for normal users to compare xids. Once one
wrapped around, it's just too hard/mindbending. Yes, an accompanying
epoch makes it easier, but it still can be quite confusing.
Just by the way, any xid8 values can be sliced with ::xid, so that
should help with comparisons. I'm not keen to allow users to convert
in the other direction though, due to the hard-to-understand
interlocking requirements of modulo xids (as belaboured elsewhere).
As Mark noted, I'd left a few uses of txid_XXX stuff in other tests.
So here's a 0003 patch that upgrades all of those too, so that the
only remaining usage is in the txid.sql tests (= the tests that the
backwards compat functions still work). No change to 0001 and 0002,
other than a commit message tweak (reviewer email address change).
Attachments:
v9-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchapplication/x-patch; name=v9-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patchDownload
From 54f854b92f1364422afe250782b603d16555d7a0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Thu, 2 Apr 2020 15:00:23 +1300
Subject: [PATCH v9 1/3] 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.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 7 ++
src/backend/access/hash/hashvalidate.c | 3 +
src/backend/utils/adt/xid.c | 116 +++++++++++++++++++
src/fe_utils/print.c | 1 +
src/include/access/transam.h | 14 +++
src/include/catalog/pg_amop.dat | 22 ++++
src/include/catalog/pg_amproc.dat | 8 ++
src/include/catalog/pg_cast.dat | 4 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 25 +++++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 36 ++++++
src/include/catalog/pg_type.dat | 4 +
src/include/utils/xid8.h | 22 ++++
src/test/regress/expected/opr_sanity.out | 7 ++
src/test/regress/expected/xid.out | 136 +++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/xid.sql | 48 ++++++++
19 files changed, 463 insertions(+), 1 deletion(-)
create mode 100644 src/include/utils/xid8.h
create mode 100644 src/test/regress/expected/xid.out
create mode 100644 src/test/regress/sql/xid.sql
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 03971822c2..89f3a7c119 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4516,6 +4516,10 @@ INSERT INTO mytable VALUES(-1); -- fails
<primary>regtype</primary>
</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 7b08ed5354..b3d1367fec 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 db6fc9dd6b..20389aff1d 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 06096cc86c..66a50f183f 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 6a947b958b..9a808f64eb 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/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..b5dfaad9ec 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 cef63b2a71..37b580883f 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 01c5328ddd..5a58f50fbb 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 ab2f50c9eb..f2342bb328 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 65c7fedf23..00ada7e48f 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 26227df216..4004138d77 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 a649e44d08..6be7e4f157 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 2e6110e3f2..a1f441b8da 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 0000000000..288e62de9c
--- /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 2efd7d7ec7..0c03afe53d 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 0000000000..e9e1fa8259
--- /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 a98dba7b2f..22a5380c45 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 3f66e0b859..e3da6c5cea 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 0000000000..a4fbca5176
--- /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;
--
2.20.1
v9-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patchapplication/x-patch; name=v9-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patchDownload
From 0c3fec3d6bca052c8fdac481825b722dc2c1ee12 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Thu, 2 Apr 2020 15:18:18 +1300
Subject: [PATCH v9 2/3] Introduce xid8_XXX functions to replace txid_XXX.
The txid_XXX family of fmgr functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type for FullTransactionId,
define a new set of functions xid8_XXX. Keep the old functions around
too, for now.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions are the ones using the wrong signedness,
and since we'll presumably drop the older ones after a reasonable period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 8 +-
doc/src/sgml/func.sgml | 140 +++++-
doc/src/sgml/logicaldecoding.sgml | 2 +-
doc/src/sgml/monitoring.sgml | 2 +-
src/backend/utils/adt/Makefile | 2 +-
src/backend/utils/adt/{txid.c => xid8funcs.c} | 414 ++++++++----------
src/include/catalog/pg_proc.dat | 67 ++-
src/include/catalog/pg_type.dat | 5 +
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 13 +-
src/test/regress/expected/xid.out | 326 ++++++++++++++
src/test/regress/sql/txid.sql | 3 +
src/test/regress/sql/xid.sql | 104 +++++
13 files changed, 814 insertions(+), 283 deletions(-)
rename src/backend/utils/adt/{txid.c => xid8funcs.c} (55%)
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 89f3a7c119..91c9202458 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -279,7 +279,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
- <entry>user-level transaction ID snapshot</entry>
+ <entry>user-level transaction ID snapshot (see also <type>xid8_snapshot</type>)</entry>
</row>
<row>
@@ -288,6 +288,12 @@
<entry>universally unique identifier</entry>
</row>
+ <row>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry></entry>
+ <entry>user-level transaction ID snapshot</entry>
+ </row>
+
<row>
<entry><type>xml</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4d88b45e72..5ab8e3f76c 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18998,6 +18998,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>xid8_current</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>xid8_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19031,12 +19063,74 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-xid8-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
+ <table id="functions-xid8-snapshot">
+ <title>Transaction IDs and Snapshots</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><literal><function>xid8_current()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>xid8_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_current_snapshot()</function></literal></entry>
+ <entry><type>xid8_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xip(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof xid8</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmax(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_snapshot_xmin(<parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>xid8_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ </row>
+ <row>
+ <entry><literal><function>xid8_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. The older functions with
+ <literal>txid</literal>
+ in the name are still supported for backward compatibility, but may be
+ removed from a future release. The <type>bigint</type> variants are shown
+ in <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
@@ -19048,42 +19142,42 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>xid8_current()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>xid8_current_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>xid8_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>xid8_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>xid8_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>xid8_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>xid8_status()</function></entry>
</row>
</tbody>
</tgroup>
@@ -19094,13 +19188,15 @@ SELECT collation for ('foo' COLLATE "de_DE");
wraps around every 4 billion transactions. However, these functions
export a 64-bit format that is extended with an <quote>epoch</quote> counter
so it will not wrap around during the life of an installation.
- The data type used by these functions, <type>txid_snapshot</type>,
+ The data type used by these functions, <type>xid8_snapshot</type>,
stores information about transaction ID
visibility at a particular moment in time. Its components are
- described in <xref linkend="functions-txid-snapshot-parts"/>.
+ described in <xref linkend="functions-xid8-snapshot-parts"/>. The
+ <type>xid8</type> values it contains can be converted to <type>xid</type>
+ by casting, if required.
</para>
- <table id="functions-txid-snapshot-parts">
+ <table id="functions-xid8-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@@ -19115,7 +19211,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
- Earliest transaction ID (txid) that is still active. All earlier
+ Earliest transaction ID (xid8) that is still active. All earlier
transactions will either be committed and visible, or rolled
back and dead.
</entry>
@@ -19124,7 +19220,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmax</type></entry>
<entry>
- First as-yet-unassigned txid. All txids greater than or equal to this
+ First as-yet-unassigned xid8. All xid8s greater than or equal to this
are not yet started as of the time of the snapshot, and thus invisible.
</entry>
</row>
@@ -19132,14 +19228,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xip_list</type></entry>
<entry>
- Active txids at the time of the snapshot. The list
- includes only those active txids between <literal>xmin</literal>
- and <literal>xmax</literal>; there might be active txids higher
- than <literal>xmax</literal>. A txid that is <literal>xmin <= txid <
+ Active xid8s at the time of the snapshot. The list
+ includes only those active xid8s between <literal>xmin</literal>
+ and <literal>xmax</literal>; there might be active xid8s higher
+ than <literal>xmax</literal>. An xid8 that is <literal>xmin <= xid8 <
xmax</literal> and not in this list was already completed
at the time of the snapshot, and thus either visible or
dead according to its commit status. The list does not
- include txids of subtransactions.
+ include xid8s of subtransactions.
</entry>
</row>
@@ -19148,14 +19244,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
- <type>txid_snapshot</type>'s textual representation is
+ <type>xid8_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
- <function>txid_status(bigint)</function> reports the commit status of a recent
+ <function>xid8_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@@ -19169,7 +19265,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
- need to determine whether the txid is a prepared transaction.
+ need to determine whether the xid8 is a prepared transaction.
</para>
<para>
diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index bce6d379bf..e8e6e36a27 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
- calling <literal>txid_current()</literal>.
+ calling <literal>xid8_current()</literal>.
</para>
</sect2>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index 0ebadf0d26..51d71d564b 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
- <entry>Waiting to execute <function>txid_status</function> or update
+ <entry>Waiting to execute <function>xid8_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..5d2aca8cfe 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -101,7 +101,6 @@ OBJS = \
tsvector.o \
tsvector_op.o \
tsvector_parser.o \
- txid.o \
uuid.o \
varbit.o \
varchar.o \
@@ -109,6 +108,7 @@ OBJS = \
version.o \
windowfuncs.o \
xid.o \
+ xid8funcs.o \
xml.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/xid8funcs.c
similarity index 55%
rename from src/backend/utils/adt/txid.c
rename to src/backend/utils/adt/xid8funcs.c
index 33272f8030..873604eeb8 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -1,14 +1,19 @@
/*-------------------------------------------------------------------------
- * txid.c
+ * xid8funcs.c
*
* Export internal transaction IDs to user level.
*
- * Note that only top-level transaction IDs are ever converted to TXID.
- * This is important because TXIDs frequently persist beyond the global
+ * Note that only top-level transaction IDs are exposed to user sessions.
+ * This is important because xid8s frequently persist beyond the global
* xmin horizon, or may even be shipped to other machines, so we cannot
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support both txid_XXX and xid8_XXX fmgr
+ * functions, since the only difference between them is whether they
+ * expose xid8 or int8 values to users. The txid_XXX variants should
+ * eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
@@ -34,25 +39,18 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
- * If defined, use bsearch() function for searching for txids in snapshots
+ * If defined, use bsearch() function for searching for xid8s in snapshots
* that have more than the specified number of values.
*/
#define USE_BSEARCH_IF_NXIP_GREATER 30
/*
- * Snapshot containing 8byte txids.
+ * Snapshot containing FullTransactionIds.
*/
typedef struct
{
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
-} TxidSnapshot;
-
-#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
-#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
+} Xid8Snapshot;
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
-
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+#define XID8_SNAPSHOT_SIZE(nxip) \
+ (offsetof(Xid8Snapshot, xip) + sizeof(FullTransactionId) * (nxip))
+#define XID8_SNAPSHOT_MAX_NXIP \
+ ((MaxAllocSize - offsetof(Xid8Snapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Convert a TransactionId obtained from a snapshot held by the caller to a
+ * FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
+ * we can compute the high order bits. It must have been obtained by the
+ * caller with ReadNextFullTransactionId() after the snapshot was created.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
- /* return special xid's as-is */
+ /* Special transaction ID. */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ /*
+ * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
+ * issued yet when the snapshot was created. Every TransactionId in the
+ * snapshot must therefore be from the same epoch as next_fxid, or the
+ * epoch before. We know this because next_fxid is never allow to get more
+ * than one epoch ahead of the TransactionIds in any snapshot.
+ */
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -211,31 +193,33 @@ cmp_txid(const void *aa, const void *bb)
* will not be used.
*/
static void
-sort_snapshot(TxidSnapshot *snap)
+sort_snapshot(Xid8Snapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const Xid8Snapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -254,13 +238,13 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
}
/*
- * helper functions to use StringInfo for TxidSnapshot creation.
+ * helper functions to use StringInfo for Xid8Snapshot creation.
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
- TxidSnapshot snap;
+ Xid8Snapshot snap;
StringInfo buf;
snap.xmin = xmin;
@@ -268,25 +252,25 @@ buf_init(txid xmin, txid xmax)
snap.nxip = 0;
buf = makeStringInfo();
- appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
+ appendBinaryStringInfo(buf, (char *) &snap, XID8_SNAPSHOT_SIZE(0));
return buf;
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ Xid8Snapshot *snap = (Xid8Snapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
-static TxidSnapshot *
+static Xid8Snapshot *
buf_finalize(StringInfo buf)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ Xid8Snapshot *snap = (Xid8Snapshot *) buf->data;
SET_VARSIZE(snap, buf->len);
@@ -297,68 +281,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
-static TxidSnapshot *
+static Xid8Snapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +318,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,108 +344,90 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "xid8_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
/*
* Public functions.
*
- * txid_current() and txid_current_snapshot() are the only ones that
+ * xid8_current() and xid8_current_snapshot() are the only ones that
* communicate with core xid machinery. All the others work on data
* returned by them.
*/
/*
- * txid_current() returns int8
+ * xid8_current() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
-txid_current(PG_FUNCTION_ARGS)
+xid8_current(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
+ PreventCommandDuringRecovery("xid8_current()");
- val = convert_xid(GetTopTransactionId(), &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
- * Same as txid_current() but doesn't assign a new xid if there isn't one
+ * Same as xid8_current() but doesn't assign a new xid if there isn't one
* yet.
*/
Datum
-txid_current_if_assigned(PG_FUNCTION_ARGS)
+xid8_current_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
- * txid_current_snapshot() returns txid_snapshot
+ * xid8_current_snapshot() returns xid8_snapshot
*
- * Return current snapshot in TXID format
+ * Return current snapshot
*
* Note that only top-transaction XIDs are included in the snapshot.
*/
Datum
-txid_current_snapshot(PG_FUNCTION_ARGS)
+xid8_current_snapshot(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap;
+ Xid8Snapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
*/
- StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
- "possible overflow in txid_current_snapshot()");
+ StaticAssertStmt(MAX_BACKENDS * 2 <= XID8_SNAPSHOT_MAX_NXIP,
+ "possible overflow in xid8_current_snapshot()");
/* allocate */
nxip = cur->xcnt;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(XID8_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
+ snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -505,21 +439,21 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
sort_snapshot(snap);
/* set size after sorting, because it may have removed duplicate xips */
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
+ SET_VARSIZE(snap, XID8_SNAPSHOT_SIZE(snap->nxip));
PG_RETURN_POINTER(snap);
}
/*
- * txid_snapshot_in(cstring) returns txid_snapshot
+ * xid8_snapshot_in(cstring) returns xid8_snapshot
*
- * input function for type txid_snapshot
+ * input function for type xid8_snapshot
*/
Datum
-txid_snapshot_in(PG_FUNCTION_ARGS)
+xid8_snapshot_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- TxidSnapshot *snap;
+ Xid8Snapshot *snap;
snap = parse_snapshot(str);
@@ -527,73 +461,81 @@ txid_snapshot_in(PG_FUNCTION_ARGS)
}
/*
- * txid_snapshot_out(txid_snapshot) returns cstring
+ * xid8_snapshot_out(xid8_snapshot) returns cstring
*
- * output function for type txid_snapshot
+ * output function for type xid8_snapshot
*/
Datum
-txid_snapshot_out(PG_FUNCTION_ARGS)
+xid8_snapshot_out(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData str;
uint32 i;
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
}
/*
- * txid_snapshot_recv(internal) returns txid_snapshot
+ * xid8_snapshot_recv(internal) returns xid8_snapshot
*
- * binary input function for type txid_snapshot
+ * binary input function for type xid8_snapshot
*
* format: int4 nxip, int8 xmin, int8 xmax, int8 xip
*/
Datum
-txid_snapshot_recv(PG_FUNCTION_ARGS)
+xid8_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- TxidSnapshot *snap;
- txid last = 0;
+ Xid8Snapshot *snap;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
- if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
+ if (nxip < 0 || nxip > XID8_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(XID8_SNAPSHOT_SIZE(nxip));
snap->xmin = xmin;
snap->xmax = xmax;
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -604,95 +546,95 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
last = cur;
}
snap->nxip = nxip;
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
+ SET_VARSIZE(snap, XID8_SNAPSHOT_SIZE(nxip));
PG_RETURN_POINTER(snap);
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external xid8_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
/*
- * txid_snapshot_send(txid_snapshot) returns bytea
+ * xid8_snapshot_send(xid8_snapshot) returns bytea
*
- * binary output function for type txid_snapshot
+ * binary output function for type xid8_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
-txid_snapshot_send(PG_FUNCTION_ARGS)
+xid8_snapshot_send(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData buf;
uint32 i;
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * xid8_visible_in_snapshot(xid8, xid8_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
-txid_visible_in_snapshot(PG_FUNCTION_ARGS)
+xid8_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * xid8_snapshot_xmin(xid8_snapshot) returns xid8
*
* return snapshot's xmin
*/
Datum
-txid_snapshot_xmin(PG_FUNCTION_ARGS)
+xid8_snapshot_xmin(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * xid8_snapshot_xmax(xid8_snapshot) returns xid8
*
* return snapshot's xmax
*/
Datum
-txid_snapshot_xmax(PG_FUNCTION_ARGS)
+xid8_snapshot_xmax(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *snap = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * xid8_snapshot_xip(xid8_snapshot) returns setof xid8
*
- * return in-progress TXIDs in snapshot.
+ * return in-progress xid8s in snapshot.
*/
Datum
-txid_snapshot_xip(PG_FUNCTION_ARGS)
+xid8_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
- TxidSnapshot *snap;
- txid value;
+ Xid8Snapshot *snap;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
{
- TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ Xid8Snapshot *arg = (Xid8Snapshot *) PG_GETARG_VARLENA_P(0);
fctx = SRF_FIRSTCALL_INIT();
@@ -709,7 +651,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -728,10 +670,10 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
* though the parent xact may still be in progress or may have aborted.
*/
Datum
-txid_status(PG_FUNCTION_ARGS)
+xid8_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +681,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6be7e4f157..e7364f936c 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9505,46 +9505,87 @@
proname => 'jsonb_path_match_opr', prorettype => 'bool',
proargtypes => 'jsonb jsonpath', prosrc => 'jsonb_path_match_opr' },
-# txid
+# historical int8/txid_snapshot variants of xid8 functions
{ oid => '2939', descr => 'I/O',
proname => 'txid_snapshot_in', prorettype => 'txid_snapshot',
- proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+ proargtypes => 'cstring', prosrc => 'xid8_snapshot_in' },
{ oid => '2940', descr => 'I/O',
proname => 'txid_snapshot_out', prorettype => 'cstring',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_out' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_out' },
{ oid => '2941', descr => 'I/O',
proname => 'txid_snapshot_recv', prorettype => 'txid_snapshot',
- proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+ proargtypes => 'internal', prosrc => 'xid8_snapshot_recv' },
{ oid => '2942', descr => 'I/O',
proname => 'txid_snapshot_send', prorettype => 'bytea',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_send' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_send' },
{ oid => '2943', descr => 'get current transaction ID',
proname => 'txid_current', provolatile => 's', proparallel => 'u',
- prorettype => 'int8', proargtypes => '', prosrc => 'txid_current' },
+ prorettype => 'int8', proargtypes => '', prosrc => 'xid8_current' },
{ oid => '3348', descr => 'get current transaction ID',
proname => 'txid_current_if_assigned', provolatile => 's', proparallel => 'u',
prorettype => 'int8', proargtypes => '',
- prosrc => 'txid_current_if_assigned' },
+ prosrc => 'xid8_current_if_assigned' },
{ oid => '2944', descr => 'get current snapshot',
proname => 'txid_current_snapshot', provolatile => 's',
prorettype => 'txid_snapshot', proargtypes => '',
- prosrc => 'txid_current_snapshot' },
+ prosrc => 'xid8_current_snapshot' },
{ oid => '2945', descr => 'get xmin of snapshot',
proname => 'txid_snapshot_xmin', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmin' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_xmin' },
{ oid => '2946', descr => 'get xmax of snapshot',
proname => 'txid_snapshot_xmax', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmax' },
+ proargtypes => 'txid_snapshot', prosrc => 'xid8_snapshot_xmax' },
{ oid => '2947', descr => 'get set of in-progress txids in snapshot',
proname => 'txid_snapshot_xip', prorows => '50', proretset => 't',
prorettype => 'int8', proargtypes => 'txid_snapshot',
- prosrc => 'txid_snapshot_xip' },
+ prosrc => 'xid8_snapshot_xip' },
{ oid => '2948', descr => 'is txid visible in snapshot?',
proname => 'txid_visible_in_snapshot', prorettype => 'bool',
- proargtypes => 'int8 txid_snapshot', prosrc => 'txid_visible_in_snapshot' },
+ proargtypes => 'int8 txid_snapshot', prosrc => 'xid8_visible_in_snapshot' },
{ oid => '3360', descr => 'commit status of transaction',
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
- proargtypes => 'int8', prosrc => 'txid_status' },
+ proargtypes => 'int8', prosrc => 'xid8_status' },
+
+# xid8 functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'xid8_snapshot_in', prorettype => 'xid8_snapshot',
+ proargtypes => 'cstring', prosrc => 'xid8_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'xid8_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'xid8_snapshot_recv', prorettype => 'xid8_snapshot',
+ proargtypes => 'internal', prosrc => 'xid8_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'xid8_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_send' },
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'xid8_current', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'xid8_current' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'xid8_current_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'xid8_current_if_assigned' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'xid8_current_snapshot', provolatile => 's',
+ prorettype => 'xid8_snapshot', proargtypes => '',
+ prosrc => 'xid8_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'xid8_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'xid8_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'xid8_snapshot', prosrc => 'xid8_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'xid8_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'xid8_snapshot',
+ prosrc => 'xid8_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'xid8_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 xid8_snapshot', prosrc => 'xid8_visible_in_snapshot' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'xid8_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'xid8_status' },
# record comparison using normal comparison rules
{ oid => '2981',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1f441b8da..2f420889e2 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -460,6 +460,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'xid8 snapshot',
+ typname => 'xid8_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'xid8_snapshot_in',
+ typoutput => 'xid8_snapshot_out', typreceive => 'xid8_snapshot_recv',
+ typsend => 'xid8_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0c03afe53d..8d350ab61b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9419
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9419
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..efbd21af7e 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index e9e1fa8259..2d24388b59 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -134,3 +134,329 @@ 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;
+-- xid8_snapshot data type and related functions
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+-- i/o
+select '12:13:'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:13:
+(1 row)
+
+select '12:18:14,16'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:18:14,16
+(1 row)
+
+select '12:16:14,14'::xid8_snapshot;
+ xid8_snapshot
+---------------
+ 12:16:14
+(1 row)
+
+-- errors
+select '31:12:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "31:12:"
+LINE 1: select '31:12:'::xid8_snapshot;
+ ^
+select '0:1:'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "0:1:"
+LINE 1: select '0:1:'::xid8_snapshot;
+ ^
+select '12:13:0'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:13:0"
+LINE 1: select '12:13:0'::xid8_snapshot;
+ ^
+select '12:16:14,13'::xid8_snapshot;
+ERROR: invalid input syntax for type xid8_snapshot: "12:16:14,13"
+LINE 1: select '12:16:14,13'::xid8_snapshot;
+ ^
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+ snap
+-------------------------------------------------------------------------------------------------------------------------------------
+ 12:13:
+ 12:20:13,15,18
+ 100001:100009:100005,100007,100008
+ 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
+(4 rows)
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+ xid8_snapshot_xmin | xid8_snapshot_xmax | xid8_snapshot_xip
+--------------------+--------------------+-------------------
+ 12 | 20 | 13
+ 12 | 20 | 15
+ 12 | 20 | 18
+ 100001 | 100009 | 100005
+ 100001 | 100009 | 100007
+ 100001 | 100009 | 100008
+ 100 | 150 | 101
+ 100 | 150 | 102
+ 100 | 150 | 103
+ 100 | 150 | 104
+ 100 | 150 | 105
+ 100 | 150 | 106
+ 100 | 150 | 107
+ 100 | 150 | 108
+ 100 | 150 | 109
+ 100 | 150 | 110
+ 100 | 150 | 111
+ 100 | 150 | 112
+ 100 | 150 | 113
+ 100 | 150 | 114
+ 100 | 150 | 115
+ 100 | 150 | 116
+ 100 | 150 | 117
+ 100 | 150 | 118
+ 100 | 150 | 119
+ 100 | 150 | 120
+ 100 | 150 | 121
+ 100 | 150 | 122
+ 100 | 150 | 123
+ 100 | 150 | 124
+ 100 | 150 | 125
+ 100 | 150 | 126
+ 100 | 150 | 127
+ 100 | 150 | 128
+ 100 | 150 | 129
+ 100 | 150 | 130
+ 100 | 150 | 131
+(37 rows)
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+ id | xid8_visible_in_snapshot
+----+--------------------------
+ 11 | t
+ 12 | t
+ 13 | f
+ 14 | t
+ 15 | f
+ 16 | t
+ 17 | t
+ 18 | f
+ 19 | t
+ 20 | f
+ 21 | f
+(11 rows)
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+ id | xid8_visible_in_snapshot
+-----+--------------------------
+ 90 | t
+ 91 | t
+ 92 | t
+ 93 | t
+ 94 | t
+ 95 | t
+ 96 | t
+ 97 | t
+ 98 | t
+ 99 | t
+ 100 | t
+ 101 | f
+ 102 | f
+ 103 | f
+ 104 | f
+ 105 | f
+ 106 | f
+ 107 | f
+ 108 | f
+ 109 | f
+ 110 | f
+ 111 | f
+ 112 | f
+ 113 | f
+ 114 | f
+ 115 | f
+ 116 | f
+ 117 | f
+ 118 | f
+ 119 | f
+ 120 | f
+ 121 | f
+ 122 | f
+ 123 | f
+ 124 | f
+ 125 | f
+ 126 | f
+ 127 | f
+ 128 | f
+ 129 | f
+ 130 | f
+ 131 | f
+ 132 | t
+ 133 | t
+ 134 | t
+ 135 | t
+ 136 | t
+ 137 | t
+ 138 | t
+ 139 | t
+ 140 | t
+ 141 | t
+ 142 | t
+ 143 | t
+ 144 | t
+ 145 | t
+ 146 | t
+ 147 | t
+ 148 | t
+ 149 | t
+ 150 | f
+ 151 | f
+ 152 | f
+ 153 | f
+ 154 | f
+ 155 | f
+ 156 | f
+ 157 | f
+ 158 | f
+ 159 | f
+ 160 | f
+(71 rows)
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+ ?column?
+----------
+ t
+(1 row)
+
+-- we can't assume current is always less than xmax, however
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+-- test 64bitness
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+ xid8_snapshot
+---------------------------------------------------------------------
+ 1000100010001000:1000100010001100:1000100010001012,1000100010001013
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ f
+(1 row)
+
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ xid8_visible_in_snapshot
+--------------------------
+ t
+(1 row)
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+ xid8_snapshot
+-------------------------
+ 1:9223372036854775807:3
+(1 row)
+
+SELECT xid8_snapshot '1:9223372036854775808:3';
+ERROR: invalid input syntax for type xid8_snapshot: "1:9223372036854775808:3"
+LINE 1: SELECT xid8_snapshot '1:9223372036854775808:3';
+ ^
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+SELECT xid8_status(:committed::text::xid8) AS committed;
+ committed
+-----------
+ committed
+(1 row)
+
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+ rolledback
+------------
+ aborted
+(1 row)
+
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+ inprogress
+-------------
+ in progress
+(1 row)
+
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+ xid8_status
+-------------
+ committed
+(1 row)
+
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+ xid8_status
+-------------
+
+(1 row)
+
+COMMIT;
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+NOTICE: Got expected error for xid in the future
+ test_future_xid_status
+------------------------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql
index bd6decf0ef..8d5ac98a89 100644
--- a/src/test/regress/sql/txid.sql
+++ b/src/test/regress/sql/txid.sql
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index a4fbca5176..8c098906e9 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -46,3 +46,107 @@ 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;
+
+
+-- xid8_snapshot data type and related functions
+
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+
+-- i/o
+select '12:13:'::xid8_snapshot;
+select '12:18:14,16'::xid8_snapshot;
+select '12:16:14,14'::xid8_snapshot;
+
+-- errors
+select '31:12:'::xid8_snapshot;
+select '0:1:'::xid8_snapshot;
+select '12:13:0'::xid8_snapshot;
+select '12:16:14,13'::xid8_snapshot;
+
+create temp table snapshot_test (
+ nr integer,
+ snap xid8_snapshot
+);
+
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+
+select xid8_snapshot_xmin(snap),
+ xid8_snapshot_xmax(snap),
+ xid8_snapshot_xip(snap)
+from snapshot_test order by nr;
+
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+
+-- test bsearch
+select id, xid8_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+
+-- test current values also
+select xid8_current() >= xid8_snapshot_xmin(xid8_current_snapshot());
+
+-- we can't assume current is always less than xmax, however
+
+select xid8_visible_in_snapshot(xid8_current(), xid8_current_snapshot());
+
+-- test 64bitness
+
+select xid8_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+select xid8_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+select xid8_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+
+-- test 64bit overflow
+SELECT xid8_snapshot '1:9223372036854775807:3';
+SELECT xid8_snapshot '1:9223372036854775808:3';
+
+-- test xid8_current_if_assigned
+BEGIN;
+SELECT xid8_current_if_assigned() IS NULL;
+SELECT xid8_current() \gset
+SELECT xid8_current_if_assigned() IS NOT DISTINCT FROM xid8 :'xid8_current';
+COMMIT;
+
+-- test xid status functions
+BEGIN;
+SELECT xid8_current() AS committed \gset
+COMMIT;
+
+BEGIN;
+SELECT xid8_current() AS rolledback \gset
+ROLLBACK;
+
+BEGIN;
+SELECT xid8_current() AS inprogress \gset
+
+SELECT xid8_status(:committed::text::xid8) AS committed;
+SELECT xid8_status(:rolledback::text::xid8) AS rolledback;
+SELECT xid8_status(:inprogress::text::xid8) AS inprogress;
+SELECT xid8_status('1'::xid8); -- BootstrapTransactionId is always committed
+SELECT xid8_status('2'::xid8); -- FrozenTransactionId is always committed
+SELECT xid8_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+
+COMMIT;
+
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM xid8_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+ROLLBACK;
--
2.20.1
v9-0003-Replace-all-txid_XXX-usage-in-tests-with-xid8_XXX.patchapplication/x-patch; name=v9-0003-Replace-all-txid_XXX-usage-in-tests-with-xid8_XXX.patchDownload
From 2105eb0cd44f2e3e2b04c0033df2e1c19c177ff3 Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Fri, 3 Apr 2020 12:06:32 +1300
Subject: [PATCH v9 3/3] Replace all txid_XXX usage in tests with xid8_XXX.
We're deprecating the txid_XXX functions, so let's stop using them in
tests. The only remaining uses are now isolated in the txid.sql file,
which exists to check that the deprecated functions still work for now.
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
contrib/test_decoding/expected/ddl.out | 2 +-
contrib/test_decoding/expected/decoding_in_xact.out | 6 +++---
contrib/test_decoding/expected/oldest_xmin.out | 2 +-
contrib/test_decoding/expected/ondisk_startup.out | 6 +++---
contrib/test_decoding/expected/snapshot_transfer.out | 4 ++--
contrib/test_decoding/specs/oldest_xmin.spec | 2 +-
contrib/test_decoding/specs/ondisk_startup.spec | 4 ++--
contrib/test_decoding/specs/snapshot_transfer.spec | 2 +-
contrib/test_decoding/sql/ddl.sql | 2 +-
contrib/test_decoding/sql/decoding_in_xact.sql | 6 +++---
src/test/modules/commit_ts/t/004_restart.pl | 6 +++---
.../modules/test_ddl_deparse/expected/create_table.out | 2 +-
src/test/modules/test_ddl_deparse/sql/create_table.sql | 2 +-
src/test/recovery/t/003_recovery_targets.pl | 2 +-
src/test/recovery/t/011_crash_recovery.pl | 10 +++++-----
src/test/regress/expected/alter_table.out | 4 ++--
src/test/regress/expected/hs_standby_functions.out | 6 +++---
src/test/regress/expected/update.out | 6 ++----
src/test/regress/sql/alter_table.sql | 4 ++--
src/test/regress/sql/hs_standby_functions.sql | 4 ++--
src/test/regress/sql/update.sql | 6 ++----
21 files changed, 42 insertions(+), 46 deletions(-)
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index cf0318f697..dd267a049d 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -382,7 +382,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
-SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
+SELECT xid8_current() != '0'; -- so no fixed xid apears in the outfile
?column?
----------
t
diff --git a/contrib/test_decoding/expected/decoding_in_xact.out b/contrib/test_decoding/expected/decoding_in_xact.out
index ab4d3aee72..3e570c010f 100644
--- a/contrib/test_decoding/expected/decoding_in_xact.out
+++ b/contrib/test_decoding/expected/decoding_in_xact.out
@@ -2,7 +2,7 @@
SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
?column?
----------
f
@@ -13,7 +13,7 @@ ERROR: cannot create logical replication slot in transaction that has performed
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
?column?
----------
f
@@ -50,7 +50,7 @@ CREATE TABLE nobarf(id serial primary key, data text);
INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
?column?
----------
f
diff --git a/contrib/test_decoding/expected/oldest_xmin.out b/contrib/test_decoding/expected/oldest_xmin.out
index d1b4f17e3a..f8d44f262c 100644
--- a/contrib/test_decoding/expected/oldest_xmin.out
+++ b/contrib/test_decoding/expected/oldest_xmin.out
@@ -2,7 +2,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_getxid s1_begin s1_insert s0_alter s0_commit s0_checkpoint s0_get_changes s0_get_changes s1_commit s0_vacuum s0_get_changes
step s0_begin: BEGIN;
-step s0_getxid: SELECT txid_current() IS NULL;
+step s0_getxid: SELECT xid8_current() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/expected/ondisk_startup.out b/contrib/test_decoding/expected/ondisk_startup.out
index c7b1f45b46..500673773c 100644
--- a/contrib/test_decoding/expected/ondisk_startup.out
+++ b/contrib/test_decoding/expected/ondisk_startup.out
@@ -2,20 +2,20 @@ Parsed test spec with 3 sessions
starting permutation: s2b s2txid s1init s3b s3txid s2alter s2c s2b s2txid s3c s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start
step s2b: BEGIN;
-step s2txid: SELECT txid_current() IS NULL;
+step s2txid: SELECT xid8_current() IS NULL;
?column?
f
step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); <waiting ...>
step s3b: BEGIN;
-step s3txid: SELECT txid_current() IS NULL;
+step s3txid: SELECT xid8_current() IS NULL;
?column?
f
step s2alter: ALTER TABLE do_write ADD COLUMN addedbys2 int;
step s2c: COMMIT;
step s2b: BEGIN;
-step s2txid: SELECT txid_current() IS NULL;
+step s2txid: SELECT xid8_current() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/expected/snapshot_transfer.out b/contrib/test_decoding/expected/snapshot_transfer.out
index 87bed03f76..0688bea0b1 100644
--- a/contrib/test_decoding/expected/snapshot_transfer.out
+++ b/contrib/test_decoding/expected/snapshot_transfer.out
@@ -3,7 +3,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
-step s0_log_assignment: SELECT txid_current() IS NULL;
+step s0_log_assignment: SELECT xid8_current() IS NULL;
?column?
f
@@ -26,7 +26,7 @@ stop
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_begin_sub1 s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub1 s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
-step s0_log_assignment: SELECT txid_current() IS NULL;
+step s0_log_assignment: SELECT xid8_current() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/specs/oldest_xmin.spec b/contrib/test_decoding/specs/oldest_xmin.spec
index 6cb13e85ce..d38b4f21bc 100644
--- a/contrib/test_decoding/specs/oldest_xmin.spec
+++ b/contrib/test_decoding/specs/oldest_xmin.spec
@@ -19,7 +19,7 @@ teardown
session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
-step "s0_getxid" { SELECT txid_current() IS NULL; }
+step "s0_getxid" { SELECT xid8_current() IS NULL; }
step "s0_alter" { ALTER TYPE basket DROP ATTRIBUTE mangos; }
step "s0_commit" { COMMIT; }
step "s0_checkpoint" { CHECKPOINT; }
diff --git a/contrib/test_decoding/specs/ondisk_startup.spec b/contrib/test_decoding/specs/ondisk_startup.spec
index 12c57a813d..07281e9d28 100644
--- a/contrib/test_decoding/specs/ondisk_startup.spec
+++ b/contrib/test_decoding/specs/ondisk_startup.spec
@@ -25,7 +25,7 @@ session "s2"
setup { SET synchronous_commit=on; }
step "s2b" { BEGIN; }
-step "s2txid" { SELECT txid_current() IS NULL; }
+step "s2txid" { SELECT xid8_current() IS NULL; }
step "s2alter" { ALTER TABLE do_write ADD COLUMN addedbys2 int; }
step "s2c" { COMMIT; }
@@ -34,7 +34,7 @@ session "s3"
setup { SET synchronous_commit=on; }
step "s3b" { BEGIN; }
-step "s3txid" { SELECT txid_current() IS NULL; }
+step "s3txid" { SELECT xid8_current() IS NULL; }
step "s3c" { COMMIT; }
# Force usage of ondisk snapshot by starting and not finishing a
diff --git a/contrib/test_decoding/specs/snapshot_transfer.spec b/contrib/test_decoding/specs/snapshot_transfer.spec
index ae81e8f102..9775c59320 100644
--- a/contrib/test_decoding/specs/snapshot_transfer.spec
+++ b/contrib/test_decoding/specs/snapshot_transfer.spec
@@ -20,7 +20,7 @@ session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
step "s0_begin_sub0" { SAVEPOINT s0; }
-step "s0_log_assignment" { SELECT txid_current() IS NULL; }
+step "s0_log_assignment" { SELECT xid8_current() IS NULL; }
step "s0_begin_sub1" { SAVEPOINT s1; }
step "s0_sub_get_base_snap" { INSERT INTO dummy VALUES (0); }
step "s0_insert" { INSERT INTO harvest VALUES (1, 2, 3); }
diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql
index 0f2b9992f7..22a2b56e04 100644
--- a/contrib/test_decoding/sql/ddl.sql
+++ b/contrib/test_decoding/sql/ddl.sql
@@ -220,7 +220,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
-SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
+SELECT xid8_current() != '0'; -- so no fixed xid apears in the outfile
SAVEPOINT a;
INSERT INTO tr_sub(path) VALUES ('4-top-1-#1');
RELEASE SAVEPOINT a;
diff --git a/contrib/test_decoding/sql/decoding_in_xact.sql b/contrib/test_decoding/sql/decoding_in_xact.sql
index b524eb9a6e..b594a5b723 100644
--- a/contrib/test_decoding/sql/decoding_in_xact.sql
+++ b/contrib/test_decoding/sql/decoding_in_xact.sql
@@ -3,13 +3,13 @@ SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
SAVEPOINT barf;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK TO SAVEPOINT barf;
@@ -29,7 +29,7 @@ INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT xid8_current() = '0';
-- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl
index bd4b943305..f21f986fe7 100644
--- a/src/test/modules/commit_ts/t/004_restart.pl
+++ b/src/test/modules/commit_ts/t/004_restart.pl
@@ -45,7 +45,7 @@ my $xid = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (1, current_timestamp);
- SELECT txid_current();
+ SELECT xid8_current()::xid;
COMMIT;
]);
@@ -93,7 +93,7 @@ DECLARE
i int;
BEGIN
FOR i in 1..cnt LOOP
- EXECUTE 'SELECT txid_current()';
+ EXECUTE 'SELECT xid8_current()';
COMMIT;
END LOOP;
END;
@@ -115,7 +115,7 @@ my $xid_disabled = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (2, current_timestamp);
- SELECT txid_current();
+ SELECT xid8_current();
COMMIT;
]);
diff --git a/src/test/modules/test_ddl_deparse/expected/create_table.out b/src/test/modules/test_ddl_deparse/expected/create_table.out
index 523c996093..3d5704a793 100644
--- a/src/test/modules/test_ddl_deparse/expected/create_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/create_table.out
@@ -43,7 +43,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
- v_txid_snapshot txid_snapshot,
+ v_xid8_snapshot xid8_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
diff --git a/src/test/modules/test_ddl_deparse/sql/create_table.sql b/src/test/modules/test_ddl_deparse/sql/create_table.sql
index dd3a908638..6e16f27c1a 100644
--- a/src/test/modules/test_ddl_deparse/sql/create_table.sql
+++ b/src/test/modules/test_ddl_deparse/sql/create_table.sql
@@ -44,7 +44,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
- v_txid_snapshot txid_snapshot,
+ v_xid8_snapshot xid8_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index fd14bab208..774d10224d 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -68,7 +68,7 @@ $node_master->backup('my_backup');
$node_master->safe_psql('postgres',
"INSERT INTO tab_int VALUES (generate_series(1001,2000))");
my $ret = $node_master->safe_psql('postgres',
- "SELECT pg_current_wal_lsn(), txid_current();");
+ "SELECT pg_current_wal_lsn(), xid8_current();");
my ($lsn2, $recovery_txid) = split /\|/, $ret;
# More data, with recovery target timestamp
diff --git a/src/test/recovery/t/011_crash_recovery.pl b/src/test/recovery/t/011_crash_recovery.pl
index 526a3481fb..cc38c9f6b2 100644
--- a/src/test/recovery/t/011_crash_recovery.pl
+++ b/src/test/recovery/t/011_crash_recovery.pl
@@ -24,7 +24,7 @@ $node->start;
my ($stdin, $stdout, $stderr) = ('', '', '');
-# Ensure that txid_status reports 'aborted' for xacts
+# Ensure that xid8_status reports 'aborted' for xacts
# that were in-progress during crash. To do that, we need
# an xact to be in-progress when we crash and we need to know
# its xid.
@@ -42,7 +42,7 @@ my $tx = IPC::Run::start(
$stdin .= q[
BEGIN;
CREATE TABLE mine(x integer);
-SELECT txid_current();
+SELECT xid8_current();
];
$tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
@@ -50,7 +50,7 @@ $tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
my $xid = $stdout;
chomp($xid);
-is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
+is($node->safe_psql('postgres', qq[SELECT xid8_status('$xid');]),
'in progress', 'own xid is in-progress');
# Crash and restart the postmaster
@@ -58,11 +58,11 @@ $node->stop('immediate');
$node->start;
# Make sure we really got a new xid
-cmp_ok($node->safe_psql('postgres', 'SELECT txid_current()'),
+cmp_ok($node->safe_psql('postgres', 'SELECT xid8_current()'),
'>', $xid, 'new xid after restart is greater');
# and make sure we show the in-progress xact as aborted
-is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
+is($node->safe_psql('postgres', qq[SELECT xid8_status('$xid');]),
'aborted', 'xid is aborted after crash');
$tx->kill_kill;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index fb6d86a269..74fe5f2bf8 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2550,7 +2550,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = xid8_current()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -2713,7 +2713,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = xid8_current()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/expected/hs_standby_functions.out b/src/test/regress/expected/hs_standby_functions.out
index e0af677ea1..1f9459bcd6 100644
--- a/src/test/regress/expected/hs_standby_functions.out
+++ b/src/test/regress/expected/hs_standby_functions.out
@@ -4,9 +4,9 @@
-- hs_standby_functions.sql
--
-- should fail
-select txid_current();
-ERROR: cannot execute txid_current() during recovery
-select length(txid_current_snapshot()::text) >= 4;
+select xid8_current();
+ERROR: cannot execute xid8_current() during recovery
+select length(xid8_current_snapshot()::text) >= 4;
?column?
----------
t
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index b7f90de52b..cea5fd0dc0 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -230,10 +230,9 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
-CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = xid8_current()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
@@ -243,13 +242,12 @@ INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = xid8_current()::xid AS xmin_correct, xmax = xid8_current()::xid AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
(1 row)
-DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;
---------------------------
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 3801f19c58..11f14df144 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1638,7 +1638,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = xid8_current()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -1725,7 +1725,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = xid8_current()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/sql/hs_standby_functions.sql b/src/test/regress/sql/hs_standby_functions.sql
index 251bac0a43..8199f15bd9 100644
--- a/src/test/regress/sql/hs_standby_functions.sql
+++ b/src/test/regress/sql/hs_standby_functions.sql
@@ -5,9 +5,9 @@
--
-- should fail
-select txid_current();
+select xid8_current();
-select length(txid_current_snapshot()::text) >= 4;
+select length(xid8_current_snapshot()::text) >= 4;
select pg_start_backup('should fail');
select pg_switch_wal();
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index bb9c24e40f..cd697cecee 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -117,18 +117,16 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
-CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = xid8_current()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
-- currently xmax is set after a conflict - that's probably not good,
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = xid8_current()::xid AS xmin_correct, xmax = xid8_current()::xid AS xmax_correct;
-DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;
--
2.20.1
On Apr 2, 2020, at 7:39 PM, Thomas Munro <thomas.munro@gmail.com> wrote:
<v9-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to-.patch><v9-0002-Introduce-xid8_XXX-functions-to-replace-txid_XXX.patch><v9-0003-Replace-all-txid_XXX-usage-in-tests-with-xid8_XXX.patch>
These apply cleanly, build and pass check-world on mac, and the documentation and regression test changes surrounding txid look good to me.
FYI, (not the responsibility of this patch), we never quite define what the abbreviation "xip" stands for. If "Active xid8s at the time of the snapshot." were rewritten as "In progress xid8s at the time of the snapshot", it might be slightly easier for the reader to figure out that "xip" = "Xid8s In Progress". As it stands, nothing in the docs seems to explain the abbrevation. See doc/src/sgml/func.sgml
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sat, Apr 4, 2020 at 4:45 AM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
FYI, (not the responsibility of this patch), we never quite define what the abbreviation "xip" stands for. If "Active xid8s at the time of the snapshot." were rewritten as "In progress xid8s at the time of the snapshot", it might be slightly easier for the reader to figure out that "xip" = "Xid8s In Progress". As it stands, nothing in the docs seems to explain the abbrevation. See doc/src/sgml/func.sgml
You're right. Done.
I also noticed that "xid8s" didn't flow very well here, so I changed
it to "transaction IDs" in a couple of places like that (which I think
is fine in these English sentences, to mean xid, xid8 or bigint
depending on the context).
I also removed a sentence about values being "extended with an epoch",
because that's not really how we want people to think about this stuff
anymore. It's rather the other way around: transaction IDs begin life
as 64 bit numbers and then get sliced.
I noticed that the description of xmax was flat out wrong (it didn't
know about ancient commit 6bd4f401), so I rewrote it. And then while
exercising my backspace key, it bothered me that the description of
xip_list said almost the same thing twice so I kept only the more
accurate of two sentences.
However, I am getting cold feet about the new function names. The
existing naming structure made sense when all this stuff originated in
a contrib module with "txid_" as a prefix all over the place, but now
that 64 bit IDs are a core concept, I wonder if we shouldn't aim for
something that looks a little more like core functionality and doesn't
have those "xid8_" warts in the names. Here's what I now propose:
Transaction ID functions, using names that fit with others (cf
pg_xact_commit_timestamp()):
pg_current_xact_id()
pg_current_xact_id_if_assigned()
pg_xact_status(xid8)
Snapshot functions (cf pg_export_snapshot()):
pg_current_snapshot()
pg_snapshot_xmin(pg_snapshot)
pg_snapshot_xmax(pg_snapshot)
pg_snapshot_xip(pg_snapshot)
pg_visible_in_snapshot(xid8, pg_snapshot)
Here's a patch set like that (0003 has been squashed into 0002).
Attachments:
v10-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to.patchtext/x-patch; charset=US-ASCII; name=v10-0001-Add-SQL-type-xid8-to-expose-FullTransactionId-to.patchDownload
From 7e52b15a40616f06d1142bb4e5d20ab20ddaf723 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Sat, 4 Apr 2020 14:46:26 +1300
Subject: [PATCH v10 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.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
doc/src/sgml/datatype.sgml | 7 ++
src/backend/access/hash/hashvalidate.c | 3 +
src/backend/utils/adt/xid.c | 116 +++++++++++++++++++
src/fe_utils/print.c | 1 +
src/include/access/transam.h | 14 +++
src/include/catalog/pg_amop.dat | 22 ++++
src/include/catalog/pg_amproc.dat | 8 ++
src/include/catalog/pg_cast.dat | 4 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 25 +++++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 36 ++++++
src/include/catalog/pg_type.dat | 4 +
src/include/utils/xid8.h | 22 ++++
src/test/regress/expected/opr_sanity.out | 7 ++
src/test/regress/expected/xid.out | 136 +++++++++++++++++++++++
src/test/regress/parallel_schedule | 2 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/xid.sql | 48 ++++++++
19 files changed, 463 insertions(+), 1 deletion(-)
create mode 100644 src/include/utils/xid8.h
create mode 100644 src/test/regress/expected/xid.out
create mode 100644 src/test/regress/sql/xid.sql
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 03971822c2..89f3a7c119 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -4516,6 +4516,10 @@ INSERT INTO mytable VALUES(-1); -- fails
<primary>regtype</primary>
</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 7b08ed5354..b3d1367fec 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 db6fc9dd6b..20389aff1d 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 06096cc86c..66a50f183f 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 6a947b958b..9a808f64eb 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/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..b5dfaad9ec 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 cef63b2a71..37b580883f 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 01c5328ddd..5a58f50fbb 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 ab2f50c9eb..f2342bb328 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 65c7fedf23..00ada7e48f 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 26227df216..4004138d77 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 a649e44d08..6be7e4f157 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 2e6110e3f2..a1f441b8da 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 0000000000..288e62de9c
--- /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 2efd7d7ec7..0c03afe53d 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 0000000000..e9e1fa8259
--- /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 a98dba7b2f..22a5380c45 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 3f66e0b859..e3da6c5cea 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 0000000000..a4fbca5176
--- /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;
--
2.20.1
v10-0002-Introduce-xid8-based-functions-to-replace-txid_X.patchtext/x-patch; charset=US-ASCII; name=v10-0002-Introduce-xid8-based-functions-to-replace-txid_X.patchDownload
From ceef3bfacf871d661d3d287653d9ea9e96c292c0 Mon Sep 17 00:00:00 2001
From: Thomas Munro <tmunro@postgresql.org>
Date: Sat, 4 Apr 2020 16:06:08 +1300
Subject: [PATCH v10 2/2] Introduce xid8-based functions to replace txid_XXX.
The txid_XXX family of fmgr functions exposes 64 bit transaction IDs to
users as int8. Now that we have an SQL type xid8 for FullTransactionId,
define a new set of functions including pg_current_xact_id() and
pg_current_snapshot() based on that. Keep the old functions around too,
for now.
It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions are the ones using the wrong signedness,
and since we'll presumably drop the older ones after a reasonable period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.
Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
---
contrib/test_decoding/expected/ddl.out | 2 +-
.../expected/decoding_in_xact.out | 6 +-
.../test_decoding/expected/oldest_xmin.out | 2 +-
.../test_decoding/expected/ondisk_startup.out | 6 +-
.../expected/snapshot_transfer.out | 4 +-
contrib/test_decoding/specs/oldest_xmin.spec | 2 +-
.../test_decoding/specs/ondisk_startup.spec | 4 +-
.../specs/snapshot_transfer.spec | 2 +-
contrib/test_decoding/sql/ddl.sql | 2 +-
.../test_decoding/sql/decoding_in_xact.sql | 6 +-
doc/src/sgml/datatype.sgml | 8 +-
doc/src/sgml/func.sgml | 166 +++++--
doc/src/sgml/logicaldecoding.sgml | 2 +-
doc/src/sgml/monitoring.sgml | 2 +-
src/backend/utils/adt/Makefile | 2 +-
src/backend/utils/adt/{txid.c => xid8funcs.c} | 424 ++++++++----------
src/include/catalog/pg_proc.dat | 69 ++-
src/include/catalog/pg_type.dat | 5 +
src/test/modules/commit_ts/t/004_restart.pl | 6 +-
.../expected/create_table.out | 2 +-
.../test_ddl_deparse/sql/create_table.sql | 2 +-
src/test/recovery/t/003_recovery_targets.pl | 2 +-
src/test/recovery/t/011_crash_recovery.pl | 10 +-
src/test/regress/expected/alter_table.out | 4 +-
.../regress/expected/hs_standby_functions.out | 6 +-
src/test/regress/expected/opr_sanity.out | 11 +-
src/test/regress/expected/txid.out | 13 +-
src/test/regress/expected/update.out | 6 +-
src/test/regress/expected/xid.out | 326 ++++++++++++++
src/test/regress/sql/alter_table.sql | 4 +-
src/test/regress/sql/hs_standby_functions.sql | 4 +-
src/test/regress/sql/txid.sql | 3 +
src/test/regress/sql/update.sql | 6 +-
src/test/regress/sql/xid.sql | 104 +++++
src/tools/pgindent/typedefs.list | 1 +
35 files changed, 870 insertions(+), 354 deletions(-)
rename src/backend/utils/adt/{txid.c => xid8funcs.c} (54%)
diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out
index cf0318f697..d79cd316b7 100644
--- a/contrib/test_decoding/expected/ddl.out
+++ b/contrib/test_decoding/expected/ddl.out
@@ -382,7 +382,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
-SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
+SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile
?column?
----------
t
diff --git a/contrib/test_decoding/expected/decoding_in_xact.out b/contrib/test_decoding/expected/decoding_in_xact.out
index ab4d3aee72..b65253f463 100644
--- a/contrib/test_decoding/expected/decoding_in_xact.out
+++ b/contrib/test_decoding/expected/decoding_in_xact.out
@@ -2,7 +2,7 @@
SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
?column?
----------
f
@@ -13,7 +13,7 @@ ERROR: cannot create logical replication slot in transaction that has performed
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
?column?
----------
f
@@ -50,7 +50,7 @@ CREATE TABLE nobarf(id serial primary key, data text);
INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
?column?
----------
f
diff --git a/contrib/test_decoding/expected/oldest_xmin.out b/contrib/test_decoding/expected/oldest_xmin.out
index d1b4f17e3a..02a091398f 100644
--- a/contrib/test_decoding/expected/oldest_xmin.out
+++ b/contrib/test_decoding/expected/oldest_xmin.out
@@ -2,7 +2,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_getxid s1_begin s1_insert s0_alter s0_commit s0_checkpoint s0_get_changes s0_get_changes s1_commit s0_vacuum s0_get_changes
step s0_begin: BEGIN;
-step s0_getxid: SELECT txid_current() IS NULL;
+step s0_getxid: SELECT pg_current_xact_id() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/expected/ondisk_startup.out b/contrib/test_decoding/expected/ondisk_startup.out
index c7b1f45b46..586b03d75d 100644
--- a/contrib/test_decoding/expected/ondisk_startup.out
+++ b/contrib/test_decoding/expected/ondisk_startup.out
@@ -2,20 +2,20 @@ Parsed test spec with 3 sessions
starting permutation: s2b s2txid s1init s3b s3txid s2alter s2c s2b s2txid s3c s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start
step s2b: BEGIN;
-step s2txid: SELECT txid_current() IS NULL;
+step s2txid: SELECT pg_current_xact_id() IS NULL;
?column?
f
step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); <waiting ...>
step s3b: BEGIN;
-step s3txid: SELECT txid_current() IS NULL;
+step s3txid: SELECT pg_current_xact_id() IS NULL;
?column?
f
step s2alter: ALTER TABLE do_write ADD COLUMN addedbys2 int;
step s2c: COMMIT;
step s2b: BEGIN;
-step s2txid: SELECT txid_current() IS NULL;
+step s2txid: SELECT pg_current_xact_id() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/expected/snapshot_transfer.out b/contrib/test_decoding/expected/snapshot_transfer.out
index 87bed03f76..c3a0000994 100644
--- a/contrib/test_decoding/expected/snapshot_transfer.out
+++ b/contrib/test_decoding/expected/snapshot_transfer.out
@@ -3,7 +3,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
-step s0_log_assignment: SELECT txid_current() IS NULL;
+step s0_log_assignment: SELECT pg_current_xact_id() IS NULL;
?column?
f
@@ -26,7 +26,7 @@ stop
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_begin_sub1 s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub1 s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
-step s0_log_assignment: SELECT txid_current() IS NULL;
+step s0_log_assignment: SELECT pg_current_xact_id() IS NULL;
?column?
f
diff --git a/contrib/test_decoding/specs/oldest_xmin.spec b/contrib/test_decoding/specs/oldest_xmin.spec
index 6cb13e85ce..da3a8cd512 100644
--- a/contrib/test_decoding/specs/oldest_xmin.spec
+++ b/contrib/test_decoding/specs/oldest_xmin.spec
@@ -19,7 +19,7 @@ teardown
session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
-step "s0_getxid" { SELECT txid_current() IS NULL; }
+step "s0_getxid" { SELECT pg_current_xact_id() IS NULL; }
step "s0_alter" { ALTER TYPE basket DROP ATTRIBUTE mangos; }
step "s0_commit" { COMMIT; }
step "s0_checkpoint" { CHECKPOINT; }
diff --git a/contrib/test_decoding/specs/ondisk_startup.spec b/contrib/test_decoding/specs/ondisk_startup.spec
index 12c57a813d..96ce87f1a4 100644
--- a/contrib/test_decoding/specs/ondisk_startup.spec
+++ b/contrib/test_decoding/specs/ondisk_startup.spec
@@ -25,7 +25,7 @@ session "s2"
setup { SET synchronous_commit=on; }
step "s2b" { BEGIN; }
-step "s2txid" { SELECT txid_current() IS NULL; }
+step "s2txid" { SELECT pg_current_xact_id() IS NULL; }
step "s2alter" { ALTER TABLE do_write ADD COLUMN addedbys2 int; }
step "s2c" { COMMIT; }
@@ -34,7 +34,7 @@ session "s3"
setup { SET synchronous_commit=on; }
step "s3b" { BEGIN; }
-step "s3txid" { SELECT txid_current() IS NULL; }
+step "s3txid" { SELECT pg_current_xact_id() IS NULL; }
step "s3c" { COMMIT; }
# Force usage of ondisk snapshot by starting and not finishing a
diff --git a/contrib/test_decoding/specs/snapshot_transfer.spec b/contrib/test_decoding/specs/snapshot_transfer.spec
index ae81e8f102..152f2fd03d 100644
--- a/contrib/test_decoding/specs/snapshot_transfer.spec
+++ b/contrib/test_decoding/specs/snapshot_transfer.spec
@@ -20,7 +20,7 @@ session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
step "s0_begin_sub0" { SAVEPOINT s0; }
-step "s0_log_assignment" { SELECT txid_current() IS NULL; }
+step "s0_log_assignment" { SELECT pg_current_xact_id() IS NULL; }
step "s0_begin_sub1" { SAVEPOINT s1; }
step "s0_sub_get_base_snap" { INSERT INTO dummy VALUES (0); }
step "s0_insert" { INSERT INTO harvest VALUES (1, 2, 3); }
diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql
index 0f2b9992f7..2c4823e578 100644
--- a/contrib/test_decoding/sql/ddl.sql
+++ b/contrib/test_decoding/sql/ddl.sql
@@ -220,7 +220,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
-SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
+SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile
SAVEPOINT a;
INSERT INTO tr_sub(path) VALUES ('4-top-1-#1');
RELEASE SAVEPOINT a;
diff --git a/contrib/test_decoding/sql/decoding_in_xact.sql b/contrib/test_decoding/sql/decoding_in_xact.sql
index b524eb9a6e..108782dc2e 100644
--- a/contrib/test_decoding/sql/decoding_in_xact.sql
+++ b/contrib/test_decoding/sql/decoding_in_xact.sql
@@ -3,13 +3,13 @@ SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
SAVEPOINT barf;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK TO SAVEPOINT barf;
@@ -29,7 +29,7 @@ INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
-SELECT txid_current() = 0;
+SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 89f3a7c119..c2e42f31c0 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -198,6 +198,12 @@
<entry><productname>PostgreSQL</productname> Log Sequence Number</entry>
</row>
+ <row>
+ <entry><type>pg_snapshot</type></entry>
+ <entry></entry>
+ <entry>user-level transaction ID snapshot</entry>
+ </row>
+
<row>
<entry><type>point</type></entry>
<entry></entry>
@@ -279,7 +285,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
- <entry>user-level transaction ID snapshot</entry>
+ <entry>user-level transaction ID snapshot (deprecated; see <type>pg_snapshot</type>)</entry>
</row>
<row>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 4d88b45e72..cc4a7bf8df 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -18998,6 +18998,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
+ <indexterm>
+ <primary>pg_current_xact_id</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_current_xact_id_if_assigned</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_current_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_snapshot_xip</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_snapshot_xmax</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_snapshot_xmin</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_visible_in_snapshot</primary>
+ </indexterm>
+
+ <indexterm>
+ <primary>pg_xact_status</primary>
+ </indexterm>
+
<indexterm>
<primary>txid_current</primary>
</indexterm>
@@ -19031,76 +19063,136 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
- The functions shown in <xref linkend="functions-txid-snapshot"/>
+ The functions shown in <xref linkend="functions-pg-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
- <table id="functions-txid-snapshot">
+ <table id="functions-pg-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
<thead>
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
</thead>
+ <tbody>
+ <row>
+ <entry><literal><function>pg_current_xact_id()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_current_xact_id_if_assigned()</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>same as <function>pg_current_xact_id()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_xact_status(<parameter>xid8</parameter>)</function></literal></entry>
+ <entry><type>text</type></entry>
+ <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_current_snapshot()</function></literal></entry>
+ <entry><type>pg_snapshot</type></entry>
+ <entry>get current snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_snapshot_xip(<parameter>pg_snapshot</parameter>)</function></literal></entry>
+ <entry><type>setof xid8</type></entry>
+ <entry>get in-progress transaction IDs in snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_snapshot_xmax(<parameter>pg_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmax</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_snapshot_xmin(<parameter>pg_snapshot</parameter>)</function></literal></entry>
+ <entry><type>xid8</type></entry>
+ <entry>get <literal>xmin</literal> of snapshot</entry>
+ </row>
+ <row>
+ <entry><literal><function>pg_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>pg_snapshot</parameter>)</function></literal></entry>
+ <entry><type>boolean</type></entry>
+ <entry>is transaction ID visible in snapshot? (do not use with subtransaction IDs)</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ The internal transaction ID type <type>xid</type> is 32 bits wide and
+ wraps around every 4 billion transactions. However, these functions use a
+ 64-bit variant <type>xid8</type> that does not wrap around during the life
+ of an installation, and can be converted to <type>xid</type> by casting if
+ required. The data type <type>pg_snapshot</type> stores information about
+ transaction ID visibility at a particular moment in time. Its components
+ are described in <xref linkend="functions-pg-snapshot-parts"/>.
+ </para>
+
+ <para>
+ In releases of <productname>PostgreSQL</productname> before 13 there was
+ no <type>xid8</type> type, so variants of these functions were provided
+ that used <type>bigint</type>. These older functions with
+ <literal>txid</literal> in their names are still supported for backward
+ compatibility, but may be removed from a future
+ release. See <xref linkend="functions-txid-snapshot"/>.
+ </para>
+
+ <table id="functions-txid-snapshot">
+ <title>Transaction IDs and Snapshots (Deprecated Functions)</title>
+ <tgroup cols="3">
+ <thead>
+ <row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
+ </thead>
+
<tbody>
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
+ <entry>see <function>pg_current_xact_id()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
+ <entry>see <function>pg_current_xact_id_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
- <entry>get current snapshot</entry>
+ <entry>see <function>pg_current_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
- <entry>get in-progress transaction IDs in snapshot</entry>
+ <entry>see <function>pg_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmax</literal> of snapshot</entry>
+ <entry>see <function>pg_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
- <entry>get <literal>xmin</literal> of snapshot</entry>
+ <entry>see <function>pg_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
- <entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
+ <entry>see <function>pg_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
- <entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
+ <entry>see <function>pg_xact_status()</function></entry>
</row>
</tbody>
</tgroup>
</table>
- <para>
- The internal transaction ID type (<type>xid</type>) is 32 bits wide and
- wraps around every 4 billion transactions. However, these functions
- export a 64-bit format that is extended with an <quote>epoch</quote> counter
- so it will not wrap around during the life of an installation.
- The data type used by these functions, <type>txid_snapshot</type>,
- stores information about transaction ID
- visibility at a particular moment in time. Its components are
- described in <xref linkend="functions-txid-snapshot-parts"/>.
- </para>
-
- <table id="functions-txid-snapshot-parts">
+ <table id="functions-pg-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@@ -19115,31 +19207,29 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
- Earliest transaction ID (txid) that is still active. All earlier
- transactions will either be committed and visible, or rolled
- back and dead.
+ Lowest transaction ID that was still active. All transaction IDs
+ less than <literal>xmin</literal> are either committed and visible,
+ or rolled back and dead.
</entry>
</row>
<row>
<entry><type>xmax</type></entry>
<entry>
- First as-yet-unassigned txid. All txids greater than or equal to this
- are not yet started as of the time of the snapshot, and thus invisible.
+ One past the highest completed transaction ID. All transaction IDs
+ greater than or equal to <literal>xmax</literal> had not yet
+ completed as of the time of the snapshot, and thus are invisible.
</entry>
</row>
<row>
<entry><type>xip_list</type></entry>
<entry>
- Active txids at the time of the snapshot. The list
- includes only those active txids between <literal>xmin</literal>
- and <literal>xmax</literal>; there might be active txids higher
- than <literal>xmax</literal>. A txid that is <literal>xmin <= txid <
- xmax</literal> and not in this list was already completed
- at the time of the snapshot, and thus either visible or
- dead according to its commit status. The list does not
- include txids of subtransactions.
+ Transactions in progress at the time of the snapshot. A transaction
+ ID that is <literal>xmin <= X < xmax</literal> and not in this
+ list was already completed at the time of the snapshot, and is thus
+ either visible or dead according to its commit status. The list does
+ not include the transaction IDs of subtransactions.
</entry>
</row>
@@ -19148,14 +19238,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
- <type>txid_snapshot</type>'s textual representation is
+ <type>pg_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
- <function>txid_status(bigint)</function> reports the commit status of a recent
+ <function>pg_xact_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@@ -19169,7 +19259,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
- need to determine whether the txid is a prepared transaction.
+ need to determine whether the transaction ID belongs to a prepared transaction.
</para>
<para>
diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml
index bce6d379bf..bad3bfe620 100644
--- a/doc/src/sgml/logicaldecoding.sgml
+++ b/doc/src/sgml/logicaldecoding.sgml
@@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
- calling <literal>txid_current()</literal>.
+ calling <literal>pg_current_xact_id()</literal>.
</para>
</sect2>
diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml
index fd8b17ef8f..c50b72137f 100644
--- a/doc/src/sgml/monitoring.sgml
+++ b/doc/src/sgml/monitoring.sgml
@@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
- <entry>Waiting to execute <function>txid_status</function> or update
+ <entry>Waiting to execute <function>pg_xact_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..5d2aca8cfe 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -101,7 +101,6 @@ OBJS = \
tsvector.o \
tsvector_op.o \
tsvector_parser.o \
- txid.o \
uuid.o \
varbit.o \
varchar.o \
@@ -109,6 +108,7 @@ OBJS = \
version.o \
windowfuncs.o \
xid.o \
+ xid8funcs.o \
xml.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/xid8funcs.c
similarity index 54%
rename from src/backend/utils/adt/txid.c
rename to src/backend/utils/adt/xid8funcs.c
index 33272f8030..616f187ad4 100644
--- a/src/backend/utils/adt/txid.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -1,20 +1,25 @@
/*-------------------------------------------------------------------------
- * txid.c
+ * xid8funcs.c
*
* Export internal transaction IDs to user level.
*
- * Note that only top-level transaction IDs are ever converted to TXID.
- * This is important because TXIDs frequently persist beyond the global
+ * Note that only top-level transaction IDs are exposed to user sessions.
+ * This is important because xid8s frequently persist beyond the global
* xmin horizon, or may even be shipped to other machines, so we cannot
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
+ * These functions are used to support the txid_XXX functions and the newer
+ * pg_current_xact, pg_current_snapshot and related fmgr functions, since the
+ * only difference between them is whether they expose xid8 or int8 values to
+ * users. The txid_XXX variants should eventually be dropped.
+ *
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
* 64-bit txids: Marko Kreen, Skype Technologies
*
- * src/backend/utils/adt/txid.c
+ * src/backend/utils/adt/xid8funcs.c
*
*-------------------------------------------------------------------------
*/
@@ -34,25 +39,18 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
+#include "utils/xid8.h"
-/* txid will be signed int8 in database, so must limit to 63 bits */
-#define MAX_TXID ((uint64) PG_INT64_MAX)
-
-/* Use unsigned variant internally */
-typedef uint64 txid;
-
-/* sprintf format code for uint64 */
-#define TXID_FMT UINT64_FORMAT
/*
- * If defined, use bsearch() function for searching for txids in snapshots
+ * If defined, use bsearch() function for searching for xid8s in snapshots
* that have more than the specified number of values.
*/
#define USE_BSEARCH_IF_NXIP_GREATER 30
/*
- * Snapshot containing 8byte txids.
+ * Snapshot containing FullTransactionIds.
*/
typedef struct
{
@@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
- uint32 nxip; /* number of txids in xip array */
- txid xmin;
- txid xmax;
- /* in-progress txids, xmin <= xip[i] < xmax: */
- txid xip[FLEXIBLE_ARRAY_MEMBER];
-} TxidSnapshot;
-
-#define TXID_SNAPSHOT_SIZE(nxip) \
- (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
-#define TXID_SNAPSHOT_MAX_NXIP \
- ((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
-
-/*
- * Epoch values from xact.c
- */
-typedef struct
-{
- TransactionId last_xid;
- uint32 epoch;
-} TxidEpoch;
-
-
-/*
- * Fetch epoch data from xact.c.
- */
-static void
-load_xid_epoch(TxidEpoch *state)
-{
- FullTransactionId fullXid = ReadNextFullTransactionId();
+ uint32 nxip; /* number of fxids in xip array */
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ /* in-progress fxids, xmin <= xip[i] < xmax: */
+ FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
+} pg_snapshot;
- state->last_xid = XidFromFullTransactionId(fullXid);
- state->epoch = EpochFromFullTransactionId(fullXid);
-}
+#define PG_SNAPSHOT_SIZE(nxip) \
+ (offsetof(pg_snapshot, xip) + sizeof(FullTransactionId) * (nxip))
+#define PG_SNAPSHOT_MAX_NXIP \
+ ((MaxAllocSize - offsetof(pg_snapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
-TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
+TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
- TransactionId xid = (TransactionId) xid_with_epoch;
+ uint32 xid_epoch = EpochFromFullTransactionId(fxid);
+ TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
- if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
+ if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
- psprintf(UINT64_FORMAT, xid_with_epoch))));
+ psprintf(UINT64_FORMAT,
+ U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
- * do a TransactionId -> txid conversion for an XID near the given epoch
+ * Convert a TransactionId obtained from a snapshot held by the caller to a
+ * FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
+ * we can compute the high order bits. It must have been obtained by the
+ * caller with ReadNextFullTransactionId() after the snapshot was created.
*/
-static txid
-convert_xid(TransactionId xid, const TxidEpoch *state)
+static FullTransactionId
+widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
{
- uint64 epoch;
+ TransactionId next_xid = XidFromFullTransactionId(next_fxid);
+ uint32 epoch = EpochFromFullTransactionId(next_fxid);
- /* return special xid's as-is */
+ /* Special transaction ID. */
if (!TransactionIdIsNormal(xid))
- return (txid) xid;
+ return FullTransactionIdFromEpochAndXid(0, xid);
- /* xid can be on either side when near wrap-around */
- epoch = (uint64) state->epoch;
- if (xid > state->last_xid &&
- TransactionIdPrecedes(xid, state->last_xid))
+ /*
+ * The 64 bit result must be <= next_fxid, since next_fxid hadn't been
+ * issued yet when the snapshot was created. Every TransactionId in the
+ * snapshot must therefore be from the same epoch as next_fxid, or the
+ * epoch before. We know this because next_fxid is never allow to get
+ * more than one epoch ahead of the TransactionIds in any snapshot.
+ */
+ if (xid > next_xid)
epoch--;
- else if (xid < state->last_xid &&
- TransactionIdFollows(xid, state->last_xid))
- epoch++;
- return (epoch << 32) | xid;
+ return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
-cmp_txid(const void *aa, const void *bb)
+cmp_fxid(const void *aa, const void *bb)
{
- txid a = *(const txid *) aa;
- txid b = *(const txid *) bb;
+ FullTransactionId a = *(const FullTransactionId *) aa;
+ FullTransactionId b = *(const FullTransactionId *) bb;
- if (a < b)
+ if (FullTransactionIdPrecedes(a, b))
return -1;
- if (a > b)
+ if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@@ -211,31 +193,33 @@ cmp_txid(const void *aa, const void *bb)
* will not be used.
*/
static void
-sort_snapshot(TxidSnapshot *snap)
+sort_snapshot(pg_snapshot *snap)
{
if (snap->nxip > 1)
{
- qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
- snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
+ snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
}
}
/*
- * check txid visibility.
+ * check fxid visibility.
*/
static bool
-is_visible_txid(txid value, const TxidSnapshot *snap)
+is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
{
- if (value < snap->xmin)
+ if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
- else if (value >= snap->xmax)
+ else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
- res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
+ res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
+ cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@@ -254,13 +238,13 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
}
/*
- * helper functions to use StringInfo for TxidSnapshot creation.
+ * helper functions to use StringInfo for pg_snapshot creation.
*/
static StringInfo
-buf_init(txid xmin, txid xmax)
+buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
- TxidSnapshot snap;
+ pg_snapshot snap;
StringInfo buf;
snap.xmin = xmin;
@@ -268,25 +252,25 @@ buf_init(txid xmin, txid xmax)
snap.nxip = 0;
buf = makeStringInfo();
- appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
+ appendBinaryStringInfo(buf, (char *) &snap, PG_SNAPSHOT_SIZE(0));
return buf;
}
static void
-buf_add_txid(StringInfo buf, txid xid)
+buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ pg_snapshot *snap = (pg_snapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
- appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
+ appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
-static TxidSnapshot *
+static pg_snapshot *
buf_finalize(StringInfo buf)
{
- TxidSnapshot *snap = (TxidSnapshot *) buf->data;
+ pg_snapshot *snap = (pg_snapshot *) buf->data;
SET_VARSIZE(snap, buf->len);
@@ -297,68 +281,34 @@ buf_finalize(StringInfo buf)
return snap;
}
-/*
- * simple number parser.
- *
- * We return 0 on error, which is invalid value for txid.
- */
-static txid
-str2txid(const char *s, const char **endp)
-{
- txid val = 0;
- txid cutoff = MAX_TXID / 10;
- txid cutlim = MAX_TXID % 10;
-
- for (; *s; s++)
- {
- unsigned d;
-
- if (*s < '0' || *s > '9')
- break;
- d = *s - '0';
-
- /*
- * check for overflow
- */
- if (val > cutoff || (val == cutoff && d > cutlim))
- {
- val = 0;
- break;
- }
-
- val = val * 10 + d;
- }
- if (endp)
- *endp = s;
- return val;
-}
-
/*
* parse snapshot from cstring
*/
-static TxidSnapshot *
+static pg_snapshot *
parse_snapshot(const char *str)
{
- txid xmin;
- txid xmax;
- txid last_val = 0,
- val;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
+ FullTransactionId last_val = InvalidFullTransactionId;
+ FullTransactionId val;
const char *str_start = str;
- const char *endp;
+ char *endp;
StringInfo buf;
- xmin = str2txid(str, &endp);
+ xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
- xmax = str2txid(str, &endp);
+ xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
- if (xmin == 0 || xmax == 0 || xmin > xmax)
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@@ -368,15 +318,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
- val = str2txid(str, &endp);
+ val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
- if (val < xmin || val >= xmax || val < last_val)
+ if (FullTransactionIdPrecedes(val, xmin) ||
+ FullTransactionIdFollowsOrEquals(val, xmax) ||
+ FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
- if (val != last_val)
+ if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@@ -392,108 +344,82 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
- "txid_snapshot", str_start)));
+ "pg_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
/*
- * Public functions.
- *
- * txid_current() and txid_current_snapshot() are the only ones that
- * communicate with core xid machinery. All the others work on data
- * returned by them.
- */
-
-/*
- * txid_current() returns int8
+ * pg_current_xact_id() returns xid8
*
- * Return the current toplevel transaction ID as TXID
+ * Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
- *
- * This value has the epoch as the high 32 bits and the 32-bit xid
- * as the low 32 bits.
*/
Datum
-txid_current(PG_FUNCTION_ARGS)
+pg_current_xact_id(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
-
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
- PreventCommandDuringRecovery("txid_current()");
-
- load_xid_epoch(&state);
+ PreventCommandDuringRecovery("pg_current_xact_id()");
- val = convert_xid(GetTopTransactionId(), &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
- * Same as txid_current() but doesn't assign a new xid if there isn't one
- * yet.
+ * Same as pg_current_xact_if_assigned() but doesn't assign a new xid if there
+ * isn't one yet.
*/
Datum
-txid_current_if_assigned(PG_FUNCTION_ARGS)
+pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
{
- txid val;
- TxidEpoch state;
- TransactionId topxid = GetTopTransactionIdIfAny();
+ FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
- if (topxid == InvalidTransactionId)
+ if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
- load_xid_epoch(&state);
-
- val = convert_xid(topxid, &state);
-
- PG_RETURN_INT64(val);
+ PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
- * txid_current_snapshot() returns txid_snapshot
+ * pg_current_snapshot() returns pg_snapshot
*
- * Return current snapshot in TXID format
+ * Return current snapshot
*
* Note that only top-transaction XIDs are included in the snapshot.
*/
Datum
-txid_current_snapshot(PG_FUNCTION_ARGS)
+pg_current_snapshot(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap;
+ pg_snapshot *snap;
uint32 nxip,
i;
- TxidEpoch state;
Snapshot cur;
+ FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
- load_xid_epoch(&state);
-
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
*/
- StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
- "possible overflow in txid_current_snapshot()");
+ StaticAssertStmt(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
+ "possible overflow in pg_current_snapshot()");
/* allocate */
nxip = cur->xcnt;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(PG_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = convert_xid(cur->xmin, &state);
- snap->xmax = convert_xid(cur->xmax, &state);
+ snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
+ snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = convert_xid(cur->xip[i], &state);
+ snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -505,21 +431,21 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
sort_snapshot(snap);
/* set size after sorting, because it may have removed duplicate xips */
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
+ SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
PG_RETURN_POINTER(snap);
}
/*
- * txid_snapshot_in(cstring) returns txid_snapshot
+ * pg_snapshot_in(cstring) returns pg_snapshot
*
- * input function for type txid_snapshot
+ * input function for type pg_snapshot
*/
Datum
-txid_snapshot_in(PG_FUNCTION_ARGS)
+pg_snapshot_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- TxidSnapshot *snap;
+ pg_snapshot *snap;
snap = parse_snapshot(str);
@@ -527,73 +453,81 @@ txid_snapshot_in(PG_FUNCTION_ARGS)
}
/*
- * txid_snapshot_out(txid_snapshot) returns cstring
+ * pg_snapshot_out(pg_snapshot) returns cstring
*
- * output function for type txid_snapshot
+ * output function for type pg_snapshot
*/
Datum
-txid_snapshot_out(PG_FUNCTION_ARGS)
+pg_snapshot_out(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData str;
uint32 i;
initStringInfo(&str);
- appendStringInfo(&str, TXID_FMT ":", snap->xmin);
- appendStringInfo(&str, TXID_FMT ":", snap->xmax);
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmin));
+ appendStringInfo(&str, UINT64_FORMAT ":",
+ U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
- appendStringInfo(&str, TXID_FMT, snap->xip[i]);
+ appendStringInfo(&str, UINT64_FORMAT,
+ U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
}
/*
- * txid_snapshot_recv(internal) returns txid_snapshot
+ * pg_snapshot_recv(internal) returns pg_snapshot
*
- * binary input function for type txid_snapshot
+ * binary input function for type pg_snapshot
*
* format: int4 nxip, int8 xmin, int8 xmax, int8 xip
*/
Datum
-txid_snapshot_recv(PG_FUNCTION_ARGS)
+pg_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
- TxidSnapshot *snap;
- txid last = 0;
+ pg_snapshot *snap;
+ FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
- txid xmin,
- xmax;
+ FullTransactionId xmin;
+ FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
- if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
+ if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP)
goto bad_format;
- xmin = pq_getmsgint64(buf);
- xmax = pq_getmsgint64(buf);
- if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
+ xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
+ if (!FullTransactionIdIsValid(xmin) ||
+ !FullTransactionIdIsValid(xmax) ||
+ FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
- snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
+ snap = palloc(PG_SNAPSHOT_SIZE(nxip));
snap->xmin = xmin;
snap->xmax = xmax;
for (i = 0; i < nxip; i++)
{
- txid cur = pq_getmsgint64(buf);
+ FullTransactionId cur =
+ FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
- if (cur < last || cur < xmin || cur >= xmax)
+ if (FullTransactionIdPrecedes(cur, last) ||
+ FullTransactionIdPrecedes(cur, xmin) ||
+ FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
- if (cur == last)
+ if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@@ -604,95 +538,95 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
last = cur;
}
snap->nxip = nxip;
- SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
+ SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
PG_RETURN_POINTER(snap);
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
- errmsg("invalid external txid_snapshot data")));
+ errmsg("invalid external pg_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
/*
- * txid_snapshot_send(txid_snapshot) returns bytea
+ * pg_snapshot_send(pg_snapshot) returns bytea
*
- * binary output function for type txid_snapshot
+ * binary output function for type pg_snapshot
*
- * format: int4 nxip, int8 xmin, int8 xmax, int8 xip
+ * format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
-txid_snapshot_send(PG_FUNCTION_ARGS)
+pg_snapshot_send(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData buf;
uint32 i;
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
- pq_sendint64(&buf, snap->xmin);
- pq_sendint64(&buf, snap->xmax);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
- pq_sendint64(&buf, snap->xip[i]);
+ pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
- * txid_visible_in_snapshot(int8, txid_snapshot) returns bool
+ * pg_visible_in_snapshot(xid8, pg_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
-txid_visible_in_snapshot(PG_FUNCTION_ARGS)
+pg_visible_in_snapshot(PG_FUNCTION_ARGS)
{
- txid value = PG_GETARG_INT64(0);
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
+ FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
+ pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
- PG_RETURN_BOOL(is_visible_txid(value, snap));
+ PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
- * txid_snapshot_xmin(txid_snapshot) returns int8
+ * pg_snapshot_xmin(pg_snapshot) returns xid8
*
* return snapshot's xmin
*/
Datum
-txid_snapshot_xmin(PG_FUNCTION_ARGS)
+pg_snapshot_xmin(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmin);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
- * txid_snapshot_xmax(txid_snapshot) returns int8
+ * pg_snapshot_xmax(pg_snapshot) returns xid8
*
* return snapshot's xmax
*/
Datum
-txid_snapshot_xmax(PG_FUNCTION_ARGS)
+pg_snapshot_xmax(PG_FUNCTION_ARGS)
{
- TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
- PG_RETURN_INT64(snap->xmax);
+ PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
- * txid_snapshot_xip(txid_snapshot) returns setof int8
+ * pg_snapshot_xip(pg_snapshot) returns setof xid8
*
- * return in-progress TXIDs in snapshot.
+ * return in-progress xid8s in snapshot.
*/
Datum
-txid_snapshot_xip(PG_FUNCTION_ARGS)
+pg_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
- TxidSnapshot *snap;
- txid value;
+ pg_snapshot *snap;
+ FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
{
- TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
+ pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
fctx = SRF_FIRSTCALL_INIT();
@@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
- SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
+ SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@@ -728,10 +662,10 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
* though the parent xact may still be in progress or may have aborted.
*/
Datum
-txid_status(PG_FUNCTION_ARGS)
+pg_xact_status(PG_FUNCTION_ARGS)
{
const char *status;
- uint64 xid_with_epoch = PG_GETARG_INT64(0);
+ FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 6be7e4f157..2d1862a9d8 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9505,46 +9505,89 @@
proname => 'jsonb_path_match_opr', prorettype => 'bool',
proargtypes => 'jsonb jsonpath', prosrc => 'jsonb_path_match_opr' },
-# txid
+# historical int8/txid_snapshot variants of xid8 functions
{ oid => '2939', descr => 'I/O',
proname => 'txid_snapshot_in', prorettype => 'txid_snapshot',
- proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
+ proargtypes => 'cstring', prosrc => 'pg_snapshot_in' },
{ oid => '2940', descr => 'I/O',
proname => 'txid_snapshot_out', prorettype => 'cstring',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_out' },
+ proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_out' },
{ oid => '2941', descr => 'I/O',
proname => 'txid_snapshot_recv', prorettype => 'txid_snapshot',
- proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
+ proargtypes => 'internal', prosrc => 'pg_snapshot_recv' },
{ oid => '2942', descr => 'I/O',
proname => 'txid_snapshot_send', prorettype => 'bytea',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_send' },
+ proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_send' },
{ oid => '2943', descr => 'get current transaction ID',
proname => 'txid_current', provolatile => 's', proparallel => 'u',
- prorettype => 'int8', proargtypes => '', prosrc => 'txid_current' },
+ prorettype => 'int8', proargtypes => '', prosrc => 'pg_current_xact_id' },
{ oid => '3348', descr => 'get current transaction ID',
proname => 'txid_current_if_assigned', provolatile => 's', proparallel => 'u',
prorettype => 'int8', proargtypes => '',
- prosrc => 'txid_current_if_assigned' },
+ prosrc => 'pg_current_xact_id_if_assigned' },
{ oid => '2944', descr => 'get current snapshot',
proname => 'txid_current_snapshot', provolatile => 's',
prorettype => 'txid_snapshot', proargtypes => '',
- prosrc => 'txid_current_snapshot' },
+ prosrc => 'pg_current_snapshot' },
{ oid => '2945', descr => 'get xmin of snapshot',
proname => 'txid_snapshot_xmin', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmin' },
+ proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmin' },
{ oid => '2946', descr => 'get xmax of snapshot',
proname => 'txid_snapshot_xmax', prorettype => 'int8',
- proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmax' },
+ proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmax' },
{ oid => '2947', descr => 'get set of in-progress txids in snapshot',
proname => 'txid_snapshot_xip', prorows => '50', proretset => 't',
prorettype => 'int8', proargtypes => 'txid_snapshot',
- prosrc => 'txid_snapshot_xip' },
+ prosrc => 'pg_snapshot_xip' },
{ oid => '2948', descr => 'is txid visible in snapshot?',
proname => 'txid_visible_in_snapshot', prorettype => 'bool',
- proargtypes => 'int8 txid_snapshot', prosrc => 'txid_visible_in_snapshot' },
+ proargtypes => 'int8 txid_snapshot', prosrc => 'pg_visible_in_snapshot' },
{ oid => '3360', descr => 'commit status of transaction',
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
- proargtypes => 'int8', prosrc => 'txid_status' },
+ proargtypes => 'int8', prosrc => 'pg_xact_status' },
+
+# pg_snapshot functions
+{ oid => '9247', descr => 'I/O',
+ proname => 'pg_snapshot_in', prorettype => 'pg_snapshot',
+ proargtypes => 'cstring', prosrc => 'pg_snapshot_in' },
+{ oid => '9248', descr => 'I/O',
+ proname => 'pg_snapshot_out', prorettype => 'cstring',
+ proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_out' },
+{ oid => '9249', descr => 'I/O',
+ proname => 'pg_snapshot_recv', prorettype => 'pg_snapshot',
+ proargtypes => 'internal', prosrc => 'pg_snapshot_recv' },
+{ oid => '9250', descr => 'I/O',
+ proname => 'pg_snapshot_send', prorettype => 'bytea',
+ proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_send' },
+{ oid => '9253', descr => 'get current snapshot',
+ proname => 'pg_current_snapshot', provolatile => 's',
+ prorettype => 'pg_snapshot', proargtypes => '',
+ prosrc => 'pg_current_snapshot' },
+{ oid => '9254', descr => 'get xmin of snapshot',
+ proname => 'pg_snapshot_xmin', prorettype => 'xid8',
+ proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmin' },
+{ oid => '9255', descr => 'get xmax of snapshot',
+ proname => 'pg_snapshot_xmax', prorettype => 'xid8',
+ proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmax' },
+{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
+ proname => 'pg_snapshot_xip', prorows => '50', proretset => 't',
+ prorettype => 'xid8', proargtypes => 'pg_snapshot',
+ prosrc => 'pg_snapshot_xip' },
+{ oid => '9257', descr => 'is xid8 visible in snapshot?',
+ proname => 'pg_visible_in_snapshot', prorettype => 'bool',
+ proargtypes => 'xid8 pg_snapshot', prosrc => 'pg_visible_in_snapshot' },
+
+# transaction ID and status functions
+{ oid => '9251', descr => 'get current transaction ID',
+ proname => 'pg_current_xact_id', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '', prosrc => 'pg_current_xact_id' },
+{ oid => '9252', descr => 'get current transaction ID',
+ proname => 'pg_current_xact_id_if_assigned', provolatile => 's', proparallel => 'u',
+ prorettype => 'xid8', proargtypes => '',
+ prosrc => 'pg_current_xact_id_if_assigned' },
+{ oid => '9258', descr => 'commit status of transaction',
+ proname => 'pg_xact_status', provolatile => 'v', prorettype => 'text',
+ proargtypes => 'xid8', prosrc => 'pg_xact_status' },
# record comparison using normal comparison rules
{ oid => '2981',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index a1f441b8da..78f44af5e7 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -460,6 +460,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
+{ oid => '8355', array_type_oid => '8356', descr => 'snapshot',
+ typname => 'pg_snapshot', typlen => '-1', typbyval => 'f',
+ typcategory => 'U', typinput => 'pg_snapshot_in',
+ typoutput => 'pg_snapshot_out', typreceive => 'pg_snapshot_recv',
+ typsend => 'pg_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',
diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl
index bd4b943305..39ca25a06b 100644
--- a/src/test/modules/commit_ts/t/004_restart.pl
+++ b/src/test/modules/commit_ts/t/004_restart.pl
@@ -45,7 +45,7 @@ my $xid = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (1, current_timestamp);
- SELECT txid_current();
+ SELECT pg_current_xact_id()::xid;
COMMIT;
]);
@@ -93,7 +93,7 @@ DECLARE
i int;
BEGIN
FOR i in 1..cnt LOOP
- EXECUTE 'SELECT txid_current()';
+ EXECUTE 'SELECT pg_current_xact_id()';
COMMIT;
END LOOP;
END;
@@ -115,7 +115,7 @@ my $xid_disabled = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (2, current_timestamp);
- SELECT txid_current();
+ SELECT pg_current_xact_id();
COMMIT;
]);
diff --git a/src/test/modules/test_ddl_deparse/expected/create_table.out b/src/test/modules/test_ddl_deparse/expected/create_table.out
index 523c996093..c7c9bf8971 100644
--- a/src/test/modules/test_ddl_deparse/expected/create_table.out
+++ b/src/test/modules/test_ddl_deparse/expected/create_table.out
@@ -43,7 +43,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
- v_txid_snapshot txid_snapshot,
+ v_pg_snapshot pg_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
diff --git a/src/test/modules/test_ddl_deparse/sql/create_table.sql b/src/test/modules/test_ddl_deparse/sql/create_table.sql
index dd3a908638..39cdb9df03 100644
--- a/src/test/modules/test_ddl_deparse/sql/create_table.sql
+++ b/src/test/modules/test_ddl_deparse/sql/create_table.sql
@@ -44,7 +44,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
- v_txid_snapshot txid_snapshot,
+ v_pg_snapshot pg_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index fd14bab208..2a520e6db1 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -68,7 +68,7 @@ $node_master->backup('my_backup');
$node_master->safe_psql('postgres',
"INSERT INTO tab_int VALUES (generate_series(1001,2000))");
my $ret = $node_master->safe_psql('postgres',
- "SELECT pg_current_wal_lsn(), txid_current();");
+ "SELECT pg_current_wal_lsn(), pg_current_xact_id();");
my ($lsn2, $recovery_txid) = split /\|/, $ret;
# More data, with recovery target timestamp
diff --git a/src/test/recovery/t/011_crash_recovery.pl b/src/test/recovery/t/011_crash_recovery.pl
index 526a3481fb..ca6e92b50d 100644
--- a/src/test/recovery/t/011_crash_recovery.pl
+++ b/src/test/recovery/t/011_crash_recovery.pl
@@ -24,7 +24,7 @@ $node->start;
my ($stdin, $stdout, $stderr) = ('', '', '');
-# Ensure that txid_status reports 'aborted' for xacts
+# Ensure that pg_xact_status reports 'aborted' for xacts
# that were in-progress during crash. To do that, we need
# an xact to be in-progress when we crash and we need to know
# its xid.
@@ -42,7 +42,7 @@ my $tx = IPC::Run::start(
$stdin .= q[
BEGIN;
CREATE TABLE mine(x integer);
-SELECT txid_current();
+SELECT pg_current_xact_id();
];
$tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
@@ -50,7 +50,7 @@ $tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
my $xid = $stdout;
chomp($xid);
-is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
+is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]),
'in progress', 'own xid is in-progress');
# Crash and restart the postmaster
@@ -58,11 +58,11 @@ $node->stop('immediate');
$node->start;
# Make sure we really got a new xid
-cmp_ok($node->safe_psql('postgres', 'SELECT txid_current()'),
+cmp_ok($node->safe_psql('postgres', 'SELECT pg_current_xact_id()'),
'>', $xid, 'new xid after restart is greater');
# and make sure we show the in-progress xact as aborted
-is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
+is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]),
'aborted', 'xid is aborted after crash');
$tx->kill_kill;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index fb6d86a269..3f5750ac72 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2550,7 +2550,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -2713,7 +2713,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/expected/hs_standby_functions.out b/src/test/regress/expected/hs_standby_functions.out
index e0af677ea1..ce846b758b 100644
--- a/src/test/regress/expected/hs_standby_functions.out
+++ b/src/test/regress/expected/hs_standby_functions.out
@@ -4,9 +4,9 @@
-- hs_standby_functions.sql
--
-- should fail
-select txid_current();
-ERROR: cannot execute txid_current() during recovery
-select length(txid_current_snapshot()::text) >= 4;
+select pg_current_xact_id();
+ERROR: cannot execute pg_current_xact_id() during recovery
+select length(pg_current_snapshot()::text) >= 4;
?column?
----------
t
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 0c03afe53d..8d350ab61b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
+ 20 | 9419
25 | 1043
1114 | 1184
-(2 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
+ 20 | 9419
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
-(4 rows)
+ 2970 | 8355
+(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
-(3 rows)
+ 2970 | 8355
+(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 015dae3051..95ba66e95e 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "31:12:"
+ERROR: invalid input syntax for type pg_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "0:1:"
+ERROR: invalid input syntax for type pg_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
+ERROR: invalid input syntax for type pg_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
-ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
+ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
+ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index b7f90de52b..bf939d79f6 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -230,10 +230,9 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
-CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
@@ -243,13 +242,12 @@ INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
(1 row)
-DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;
---------------------------
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index e9e1fa8259..b7a1ed0f9e 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -134,3 +134,329 @@ 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;
+-- pg_snapshot data type and related functions
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+-- i/o
+select '12:13:'::pg_snapshot;
+ pg_snapshot
+-------------
+ 12:13:
+(1 row)
+
+select '12:18:14,16'::pg_snapshot;
+ pg_snapshot
+-------------
+ 12:18:14,16
+(1 row)
+
+select '12:16:14,14'::pg_snapshot;
+ pg_snapshot
+-------------
+ 12:16:14
+(1 row)
+
+-- errors
+select '31:12:'::pg_snapshot;
+ERROR: invalid input syntax for type pg_snapshot: "31:12:"
+LINE 1: select '31:12:'::pg_snapshot;
+ ^
+select '0:1:'::pg_snapshot;
+ERROR: invalid input syntax for type pg_snapshot: "0:1:"
+LINE 1: select '0:1:'::pg_snapshot;
+ ^
+select '12:13:0'::pg_snapshot;
+ERROR: invalid input syntax for type pg_snapshot: "12:13:0"
+LINE 1: select '12:13:0'::pg_snapshot;
+ ^
+select '12:16:14,13'::pg_snapshot;
+ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13"
+LINE 1: select '12:16:14,13'::pg_snapshot;
+ ^
+create temp table snapshot_test (
+ nr integer,
+ snap pg_snapshot
+);
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+ snap
+-------------------------------------------------------------------------------------------------------------------------------------
+ 12:13:
+ 12:20:13,15,18
+ 100001:100009:100005,100007,100008
+ 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
+(4 rows)
+
+select pg_snapshot_xmin(snap),
+ pg_snapshot_xmax(snap),
+ pg_snapshot_xip(snap)
+from snapshot_test order by nr;
+ pg_snapshot_xmin | pg_snapshot_xmax | pg_snapshot_xip
+------------------+------------------+-----------------
+ 12 | 20 | 13
+ 12 | 20 | 15
+ 12 | 20 | 18
+ 100001 | 100009 | 100005
+ 100001 | 100009 | 100007
+ 100001 | 100009 | 100008
+ 100 | 150 | 101
+ 100 | 150 | 102
+ 100 | 150 | 103
+ 100 | 150 | 104
+ 100 | 150 | 105
+ 100 | 150 | 106
+ 100 | 150 | 107
+ 100 | 150 | 108
+ 100 | 150 | 109
+ 100 | 150 | 110
+ 100 | 150 | 111
+ 100 | 150 | 112
+ 100 | 150 | 113
+ 100 | 150 | 114
+ 100 | 150 | 115
+ 100 | 150 | 116
+ 100 | 150 | 117
+ 100 | 150 | 118
+ 100 | 150 | 119
+ 100 | 150 | 120
+ 100 | 150 | 121
+ 100 | 150 | 122
+ 100 | 150 | 123
+ 100 | 150 | 124
+ 100 | 150 | 125
+ 100 | 150 | 126
+ 100 | 150 | 127
+ 100 | 150 | 128
+ 100 | 150 | 129
+ 100 | 150 | 130
+ 100 | 150 | 131
+(37 rows)
+
+select id, pg_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+ id | pg_visible_in_snapshot
+----+------------------------
+ 11 | t
+ 12 | t
+ 13 | f
+ 14 | t
+ 15 | f
+ 16 | t
+ 17 | t
+ 18 | f
+ 19 | t
+ 20 | f
+ 21 | f
+(11 rows)
+
+-- test bsearch
+select id, pg_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+ id | pg_visible_in_snapshot
+-----+------------------------
+ 90 | t
+ 91 | t
+ 92 | t
+ 93 | t
+ 94 | t
+ 95 | t
+ 96 | t
+ 97 | t
+ 98 | t
+ 99 | t
+ 100 | t
+ 101 | f
+ 102 | f
+ 103 | f
+ 104 | f
+ 105 | f
+ 106 | f
+ 107 | f
+ 108 | f
+ 109 | f
+ 110 | f
+ 111 | f
+ 112 | f
+ 113 | f
+ 114 | f
+ 115 | f
+ 116 | f
+ 117 | f
+ 118 | f
+ 119 | f
+ 120 | f
+ 121 | f
+ 122 | f
+ 123 | f
+ 124 | f
+ 125 | f
+ 126 | f
+ 127 | f
+ 128 | f
+ 129 | f
+ 130 | f
+ 131 | f
+ 132 | t
+ 133 | t
+ 134 | t
+ 135 | t
+ 136 | t
+ 137 | t
+ 138 | t
+ 139 | t
+ 140 | t
+ 141 | t
+ 142 | t
+ 143 | t
+ 144 | t
+ 145 | t
+ 146 | t
+ 147 | t
+ 148 | t
+ 149 | t
+ 150 | f
+ 151 | f
+ 152 | f
+ 153 | f
+ 154 | f
+ 155 | f
+ 156 | f
+ 157 | f
+ 158 | f
+ 159 | f
+ 160 | f
+(71 rows)
+
+-- test current values also
+select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot());
+ ?column?
+----------
+ t
+(1 row)
+
+-- we can't assume current is always less than xmax, however
+select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot());
+ pg_visible_in_snapshot
+------------------------
+ f
+(1 row)
+
+-- test 64bitness
+select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+ pg_snapshot
+---------------------------------------------------------------------
+ 1000100010001000:1000100010001100:1000100010001012,1000100010001013
+(1 row)
+
+select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ pg_visible_in_snapshot
+------------------------
+ f
+(1 row)
+
+select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+ pg_visible_in_snapshot
+------------------------
+ t
+(1 row)
+
+-- test 64bit overflow
+SELECT pg_snapshot '1:9223372036854775807:3';
+ pg_snapshot
+-------------------------
+ 1:9223372036854775807:3
+(1 row)
+
+SELECT pg_snapshot '1:9223372036854775808:3';
+ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3"
+LINE 1: SELECT pg_snapshot '1:9223372036854775808:3';
+ ^
+-- test pg_current_xact_id_if_assigned
+BEGIN;
+SELECT pg_current_xact_id_if_assigned() IS NULL;
+ ?column?
+----------
+ t
+(1 row)
+
+SELECT pg_current_xact_id() \gset
+SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id';
+ ?column?
+----------
+ t
+(1 row)
+
+COMMIT;
+-- test xid status functions
+BEGIN;
+SELECT pg_current_xact_id() AS committed \gset
+COMMIT;
+BEGIN;
+SELECT pg_current_xact_id() AS rolledback \gset
+ROLLBACK;
+BEGIN;
+SELECT pg_current_xact_id() AS inprogress \gset
+SELECT pg_xact_status(:committed::text::xid8) AS committed;
+ committed
+-----------
+ committed
+(1 row)
+
+SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback;
+ rolledback
+------------
+ aborted
+(1 row)
+
+SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress;
+ inprogress
+-------------
+ in progress
+(1 row)
+
+SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed
+ pg_xact_status
+----------------
+ committed
+(1 row)
+
+SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed
+ pg_xact_status
+----------------
+ committed
+(1 row)
+
+SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+ pg_xact_status
+----------------
+
+(1 row)
+
+COMMIT;
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM pg_xact_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+NOTICE: Got expected error for xid in the future
+ test_future_xid_status
+------------------------
+
+(1 row)
+
+ROLLBACK;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 3801f19c58..89bbdc0043 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1638,7 +1638,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -1725,7 +1725,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = txid_current()::integer)
+ where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/sql/hs_standby_functions.sql b/src/test/regress/sql/hs_standby_functions.sql
index 251bac0a43..b57f67ff8b 100644
--- a/src/test/regress/sql/hs_standby_functions.sql
+++ b/src/test/regress/sql/hs_standby_functions.sql
@@ -5,9 +5,9 @@
--
-- should fail
-select txid_current();
+select pg_current_xact_id();
-select length(txid_current_snapshot()::text) >= 4;
+select length(pg_current_snapshot()::text) >= 4;
select pg_start_backup('should fail');
select pg_switch_wal();
diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql
index bd6decf0ef..8d5ac98a89 100644
--- a/src/test/regress/sql/txid.sql
+++ b/src/test/regress/sql/txid.sql
@@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
+-- Note: these are backward-compatibility functions and types, and have been
+-- replaced by new xid8-based variants. See xid.sql. The txid variants will
+-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index bb9c24e40f..8c558a7bc7 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -117,18 +117,16 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
-CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
-- currently xmax is set after a conflict - that's probably not good,
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
-DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index a4fbca5176..565714d462 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -46,3 +46,107 @@ 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;
+
+
+-- pg_snapshot data type and related functions
+
+-- Note: another set of tests similar to this exists in txid.sql, for a limited
+-- time (the relevant functions share C code)
+
+-- i/o
+select '12:13:'::pg_snapshot;
+select '12:18:14,16'::pg_snapshot;
+select '12:16:14,14'::pg_snapshot;
+
+-- errors
+select '31:12:'::pg_snapshot;
+select '0:1:'::pg_snapshot;
+select '12:13:0'::pg_snapshot;
+select '12:16:14,13'::pg_snapshot;
+
+create temp table snapshot_test (
+ nr integer,
+ snap pg_snapshot
+);
+
+insert into snapshot_test values (1, '12:13:');
+insert into snapshot_test values (2, '12:20:13,15,18');
+insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
+insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
+select snap from snapshot_test order by nr;
+
+select pg_snapshot_xmin(snap),
+ pg_snapshot_xmax(snap),
+ pg_snapshot_xip(snap)
+from snapshot_test order by nr;
+
+select id, pg_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(11, 21) id
+where nr = 2;
+
+-- test bsearch
+select id, pg_visible_in_snapshot(id::text::xid8, snap)
+from snapshot_test, generate_series(90, 160) id
+where nr = 4;
+
+-- test current values also
+select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot());
+
+-- we can't assume current is always less than xmax, however
+
+select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot());
+
+-- test 64bitness
+
+select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
+select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
+
+-- test 64bit overflow
+SELECT pg_snapshot '1:9223372036854775807:3';
+SELECT pg_snapshot '1:9223372036854775808:3';
+
+-- test pg_current_xact_id_if_assigned
+BEGIN;
+SELECT pg_current_xact_id_if_assigned() IS NULL;
+SELECT pg_current_xact_id() \gset
+SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id';
+COMMIT;
+
+-- test xid status functions
+BEGIN;
+SELECT pg_current_xact_id() AS committed \gset
+COMMIT;
+
+BEGIN;
+SELECT pg_current_xact_id() AS rolledback \gset
+ROLLBACK;
+
+BEGIN;
+SELECT pg_current_xact_id() AS inprogress \gset
+
+SELECT pg_xact_status(:committed::text::xid8) AS committed;
+SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback;
+SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress;
+SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed
+SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed
+SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
+
+COMMIT;
+
+BEGIN;
+CREATE FUNCTION test_future_xid_status(xid8)
+RETURNS void
+LANGUAGE plpgsql
+AS
+$$
+BEGIN
+ PERFORM pg_xact_status($1);
+ RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
+EXCEPTION
+ WHEN invalid_parameter_value THEN
+ RAISE NOTICE 'Got expected error for xid in the future';
+END;
+$$;
+SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
+ROLLBACK;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 939de985d3..5f897123e4 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -3098,6 +3098,7 @@ pg_sha224_ctx
pg_sha256_ctx
pg_sha384_ctx
pg_sha512_ctx
+pg_snapshot
pg_stack_base_t
pg_time_t
pg_tz
--
2.20.1
On Apr 4, 2020, at 7:11 AM, Thomas Munro <thomas.munro@gmail.com> wrote:
On Sat, Apr 4, 2020 at 4:45 AM Mark Dilger <mark.dilger@enterprisedb.com> wrote:
FYI, (not the responsibility of this patch), we never quite define what the abbreviation "xip" stands for. If "Active xid8s at the time of the snapshot." were rewritten as "In progress xid8s at the time of the snapshot", it might be slightly easier for the reader to figure out that "xip" = "Xid8s In Progress". As it stands, nothing in the docs seems to explain the abbrevation. See doc/src/sgml/func.sgml
You're right. Done.
Thanks!
However, I am getting cold feet about the new function names. The
existing naming structure made sense when all this stuff originated in
a contrib module with "txid_" as a prefix all over the place, but now
that 64 bit IDs are a core concept, I wonder if we shouldn't aim for
something that looks a little more like core functionality and doesn't
have those "xid8_" warts in the names.
The "xid8_" warts are partly motivated by having a type named "xid8", which is a bit of a wart in itself.
Here's what I now propose:
Transaction ID functions, using names that fit with others (cf
pg_xact_commit_timestamp()):pg_current_xact_id()
pg_current_xact_id_if_assigned()
pg_xact_status(xid8)Snapshot functions (cf pg_export_snapshot()):
pg_current_snapshot()
pg_snapshot_xmin(pg_snapshot)
pg_snapshot_xmax(pg_snapshot)
pg_snapshot_xip(pg_snapshot)
pg_visible_in_snapshot(xid8, pg_snapshot)
I like some aspects of this, but not others. Function pg_stat_get_activity(), which gets exposed through view pg_stat_activity exposes both "backend_xid" and "backend_xmin" as (32-bit) xid. Your new function names are not sufficiently distinct from these older names for users to easily remember the difference:
select pg_snapshot_xmax(st.snap)
from snapshot_test st, pg_stat_activity sa
where pg_snapshot_xmin(st.snap) = sa.backend_xmin;
ERROR: operator does not exist: xid8 = xid
SELECT * FROM pg_stat_activity s WHERE backend_xid = pg_current_xact_id();
ERROR: operator does not exist: xid = xid8
SELECT pg_xact_status(backend_xmin), * FROM pg_stat_activity;
ERROR: function pg_xact_status(xid) does not exist
It's not the end of the world, and users can figure out to put a cast on those, but it's kind of ugly.
It was cleaner before v10, when "backend_xid = xid8_current()" clearly had a xid vs. xid8 mismatch. On the other hand, if the xid columns in pg_stat_activity and elsewhere eventually get upgraded to xid8 fields, then the new naming convention in v10 will be cleaner.
As such, I'm ±0 for the change.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Sun, Apr 5, 2020 at 10:34 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
On Apr 4, 2020, at 7:11 AM, Thomas Munro <thomas.munro@gmail.com> wrote:
However, I am getting cold feet about the new function names. The
existing naming structure made sense when all this stuff originated in
a contrib module with "txid_" as a prefix all over the place, but now
that 64 bit IDs are a core concept, I wonder if we shouldn't aim for
something that looks a little more like core functionality and doesn't
have those "xid8_" warts in the names.The "xid8_" warts are partly motivated by having a type named "xid8", which is a bit of a wart in itself.
Just a thought for the future, not sure if it's a good one: would it
seem less warty in years to come if we introduced xid4 as an alias for
xid, and preferred the name xid4? Then it wouldn't look so much like
xid is the "real" transaction ID type and xid8 is some kind of freaky
extended version; instead it would look like xid4 and xid8 are narrow
and wide transaction IDs, and xid is just a historical name for xid4.
Here's what I now propose:
Transaction ID functions, using names that fit with others (cf
pg_xact_commit_timestamp()):pg_current_xact_id()
pg_current_xact_id_if_assigned()
pg_xact_status(xid8)Snapshot functions (cf pg_export_snapshot()):
pg_current_snapshot()
pg_snapshot_xmin(pg_snapshot)
pg_snapshot_xmax(pg_snapshot)
pg_snapshot_xip(pg_snapshot)
pg_visible_in_snapshot(xid8, pg_snapshot)I like some aspects of this, but not others. Function pg_stat_get_activity(), which gets exposed through view pg_stat_activity exposes both "backend_xid" and "backend_xmin" as (32-bit) xid. Your new function names are not sufficiently distinct from these older names for users to easily remember the difference:
select pg_snapshot_xmax(st.snap)
from snapshot_test st, pg_stat_activity sa
where pg_snapshot_xmin(st.snap) = sa.backend_xmin;
ERROR: operator does not exist: xid8 = xidSELECT * FROM pg_stat_activity s WHERE backend_xid = pg_current_xact_id();
ERROR: operator does not exist: xid = xid8
It's quite tempting to go and widen pg_stat_activity etc... but in
any case I'm sure it'll happen for PG14.
SELECT pg_xact_status(backend_xmin), * FROM pg_stat_activity;
ERROR: function pg_xact_status(xid) does not existIt's not the end of the world, and users can figure out to put a cast on those, but it's kind of ugly.
That particular one can't really be fixed with a cast, either before
or after this patch (I mean, if you add the right casts you can get
the query to run with this function or its txid ancestor, but it'll
only give the right answers during epoch 0 so I would call this
friction a good case of the type system doing its job during the
transition).
It was cleaner before v10, when "backend_xid = xid8_current()" clearly had a xid vs. xid8 mismatch. On the other hand, if the xid columns in pg_stat_activity and elsewhere eventually get upgraded to xid8 fields, then the new naming convention in v10 will be cleaner.
Yeah. Well, my cold feet with the v9 names came from thinking about
how all this is going to look in a couple of years as xid8 flows into
more administration interfaces. It seems inevitable that there will
be some friction along the way, but it seems like a nice goal to have
wider values everywhere possible from functions and views with
non-warty names, and use cast to get narrow values if needed for some
reason.
As such, I'm ±0 for the change.
I'll let this sit for another day and see if some more reactions appear.
On Sun, Apr 5, 2020 at 11:31 AM Thomas Munro <thomas.munro@gmail.com> wrote:
On Sun, Apr 5, 2020 at 10:34 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:The "xid8_" warts are partly motivated by having a type named "xid8", which is a bit of a wart in itself.
Just a thought for the future, not sure if it's a good one: would it
seem less warty in years to come if we introduced xid4 as an alias for
xid, and preferred the name xid4? Then it wouldn't look so much like
xid is the "real" transaction ID type and xid8 is some kind of freaky
extended version; instead it would look like xid4 and xid8 are narrow
and wide transaction IDs, and xid is just a historical name for xid4.
I'll look into proposing that for PG14. One reason I like that idea
is that system view names like backend_xid could potentially retain
their names while switching to xid8 type, (maybe?) breaking fewer
queries and avoiding ugly names, on the theory that _xid doesn't
specify whether it's xid4 or an xid8.
pg_current_xact_id()
pg_current_xact_id_if_assigned()
pg_xact_status(xid8)
pg_current_snapshot()
pg_snapshot_xmin(pg_snapshot)
pg_snapshot_xmax(pg_snapshot)
pg_snapshot_xip(pg_snapshot)
pg_visible_in_snapshot(xid8, pg_snapshot)
As such, I'm ±0 for the change.
I'll let this sit for another day and see if some more reactions appear.
Hearing no objections, pushed. Happy to reconsider these names before
release if someone finds a problem with this scheme.
On Apr 6, 2020, at 5:14 PM, Thomas Munro <thomas.munro@gmail.com> wrote:
On Sun, Apr 5, 2020 at 11:31 AM Thomas Munro <thomas.munro@gmail.com> wrote:
On Sun, Apr 5, 2020 at 10:34 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:The "xid8_" warts are partly motivated by having a type named "xid8", which is a bit of a wart in itself.
Just a thought for the future, not sure if it's a good one: would it
seem less warty in years to come if we introduced xid4 as an alias for
xid, and preferred the name xid4? Then it wouldn't look so much like
xid is the "real" transaction ID type and xid8 is some kind of freaky
extended version; instead it would look like xid4 and xid8 are narrow
and wide transaction IDs, and xid is just a historical name for xid4.I'll look into proposing that for PG14. One reason I like that idea
is that system view names like backend_xid could potentially retain
their names while switching to xid8 type, (maybe?) breaking fewer
queries and avoiding ugly names, on the theory that _xid doesn't
specify whether it's xid4 or an xid8.pg_current_xact_id()
pg_current_xact_id_if_assigned()
pg_xact_status(xid8)pg_current_snapshot()
pg_snapshot_xmin(pg_snapshot)
pg_snapshot_xmax(pg_snapshot)
pg_snapshot_xip(pg_snapshot)
pg_visible_in_snapshot(xid8, pg_snapshot)As such, I'm ±0 for the change.
I'll let this sit for another day and see if some more reactions appear.
Hearing no objections, pushed. Happy to reconsider these names before
release if someone finds a problem with this scheme.
Hmm, I should have spoken sooner...
src/backend/replication/walsender.c:static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch);
src/backend/utils/adt/xid8funcs.c:TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
I don't care much for having two different functions with the same name and related semantics but different argument types.
—
Mark Dilger
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
On Fri, Apr 17, 2020 at 3:44 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:
Hmm, I should have spoken sooner...
src/backend/replication/walsender.c:static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch);
src/backend/utils/adt/xid8funcs.c:TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)I don't care much for having two different functions with the same name and related semantics but different argument types.
Maybe that's not ideal, but it's not because of this patch. Those
functions are from 5737c12df05 and 857ee8e391f.
On Thu, Apr 2, 2020 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
Given that txid_current() "always" has been a plain 64 bit integer, and
the various txid_* functions always have returned 64 bit integers, I
really don't think arguing for some 32bit/32bit situation now makes
sense.
I'm not sure what the best thing to do is here, but the reality is
that there are many places where 32-bit XIDs are going to be showing
up for years to come. With the format printed as a raw 64-bit
quantity, people troubleshooting stuff are going to spend a lot of
time figuring what x%2^32 is. And I can't do that in my head. So I
think saying that the proposal does not makes sense is a gross
overstatement. It may not be what we want to do. But it definitely
would make sense.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
On 2020-04-17 13:33:53 -0400, Robert Haas wrote:
On Thu, Apr 2, 2020 at 5:13 PM Andres Freund <andres@anarazel.de> wrote:
Given that txid_current() "always" has been a plain 64 bit integer, and
the various txid_* functions always have returned 64 bit integers, I
really don't think arguing for some 32bit/32bit situation now makes
sense.I'm not sure what the best thing to do is here, but the reality is
that there are many places where 32-bit XIDs are going to be showing
up for years to come. With the format printed as a raw 64-bit
quantity, people troubleshooting stuff are going to spend a lot of
time figuring what x%2^32 is. And I can't do that in my head. So I
think saying that the proposal does not makes sense is a gross
overstatement. It may not be what we want to do. But it definitely
would make sense.
You seem to be entirely disregarding my actual point, namely that
txid_current(), as well as some other txid_* functions, have returned
64bit xids for many many years. txid_current() is the only function to
get the current xid in a reasonable way. I don't understand how a
proposal to add a 32/32 bit representation *in addition* to the existing
32 and 64bit representations is going to improve the situation. Nor do I
see changing txid_current()'s return format as something we're going to
go for.
I did not argue against a function to turn 64bit xids into epoch/32bit
xid or such.
?
Greetings,
Andres Freund
On Fri, Apr 17, 2020 at 1:45 PM Andres Freund <andres@anarazel.de> wrote:
You seem to be entirely disregarding my actual point, namely that
txid_current(), as well as some other txid_* functions, have returned
64bit xids for many many years. txid_current() is the only function to
get the current xid in a reasonable way. I don't understand how a
proposal to add a 32/32 bit representation *in addition* to the existing
32 and 64bit representations is going to improve the situation. Nor do I
see changing txid_current()'s return format as something we're going to
go for.I did not argue against a function to turn 64bit xids into epoch/32bit
xid or such.
I thought we were talking about how the new xid8 type ought to behave.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
Hi,
On 2020-04-17 14:07:07 -0400, Robert Haas wrote:
On Fri, Apr 17, 2020 at 1:45 PM Andres Freund <andres@anarazel.de> wrote:
You seem to be entirely disregarding my actual point, namely that
txid_current(), as well as some other txid_* functions, have returned
64bit xids for many many years. txid_current() is the only function to
get the current xid in a reasonable way. I don't understand how a
proposal to add a 32/32 bit representation *in addition* to the existing
32 and 64bit representations is going to improve the situation. Nor do I
see changing txid_current()'s return format as something we're going to
go for.I did not argue against a function to turn 64bit xids into epoch/32bit
xid or such.I thought we were talking about how the new xid8 type ought to behave.
Yes? But that type doesn't exist in isolation. Having yet another
significantly different representation of 64bit xids (as plain 64 bit
integers, and as some 32/32 epoch/xid split) would make an already
confusing situation even more complex.
Greetings,
Andres Freund
On 2020-Apr-17, Andres Freund wrote:
Yes? But that type doesn't exist in isolation. Having yet another
significantly different representation of 64bit xids (as plain 64 bit
integers, and as some 32/32 epoch/xid split) would make an already
confusing situation even more complex.
On the contrary -- I think it would clarify a confusing situation.
--
�lvaro Herrera https://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
On Tue, Apr 7, 2020 at 12:14 PM Thomas Munro <thomas.munro@gmail.com> wrote:
On Sun, Apr 5, 2020 at 11:31 AM Thomas Munro <thomas.munro@gmail.com> wrote:
On Sun, Apr 5, 2020 at 10:34 AM Mark Dilger
<mark.dilger@enterprisedb.com> wrote:The "xid8_" warts are partly motivated by having a type named "xid8", which is a bit of a wart in itself.
Just a thought for the future, not sure if it's a good one: would it
seem less warty in years to come if we introduced xid4 as an alias for
xid, and preferred the name xid4? Then it wouldn't look so much like
xid is the "real" transaction ID type and xid8 is some kind of freaky
extended version; instead it would look like xid4 and xid8 are narrow
and wide transaction IDs, and xid is just a historical name for xid4.I'll look into proposing that for PG14. One reason I like that idea
is that system view names like backend_xid could potentially retain
their names while switching to xid8 type, (maybe?) breaking fewer
queries and avoiding ugly names, on the theory that _xid doesn't
specify whether it's xid4 or an xid8.
Here's a patch that renames xid to xid4, but I realised that we lack
the technology to create a suitable backwards compat alias. The
bigint/int8 keyword trick doesn't work here, because it would break
existing queries using xid as, for example, a function argument name.
Perhaps we could invent a new kind of type that is a simple alias for
another type, and is entirely replaced by the base type early in
processing, so that you can do type aliases without bigint-style
keywords. Perhaps all of this is not worth the churn just for a
neatnick project.
Attachments:
0001-Rename-xid-to-xid4.patchtext/x-patch; charset=US-ASCII; name=0001-Rename-xid-to-xid4.patchDownload
From fb6a5555f225121e667f77c16f257d58dcbf56ee Mon Sep 17 00:00:00 2001
From: Thomas Munro <thomas.munro@gmail.com>
Date: Sat, 18 Apr 2020 09:09:07 +1200
Subject: [PATCH] Rename xid to xid4.
Now that we have SQL type xid8, rename SQL type xid to xid4 for
consistency. This makes the width of the type clearer, and avoids
creating the impression that xid is the 'real' transaction ID type,
rather than being a narrower version that lacks upper bits.
XXX Experiment only, not for commit. To actually do this, we'd
probably want to figure out how to provide aliases like SQL xid and
C XIDOID for backwards compatibility. It's not done in this commit,
to demonstrate that nothing in the tree depends on the old name.
XXX Documentation changes needed.
---
src/backend/access/hash/hashvalidate.c | 2 +-
src/backend/access/transam/commit_ts.c | 2 +-
src/backend/access/transam/multixact.c | 2 +-
src/backend/access/transam/twophase.c | 2 +-
src/backend/bootstrap/bootstrap.c | 4 +-
src/backend/catalog/Catalog.pm | 2 +-
src/backend/catalog/genbki.pl | 4 +-
src/backend/catalog/heap.c | 4 +-
src/backend/catalog/system_views.sql | 8 +--
src/backend/utils/adt/lockfuncs.c | 2 +-
src/backend/utils/adt/xid.c | 20 +++---
src/backend/utils/misc/pg_controldata.c | 14 ++--
src/fe_utils/print.c | 2 +-
src/include/catalog/pg_amop.dat | 6 +-
src/include/catalog/pg_amproc.dat | 8 +--
src/include/catalog/pg_cast.dat | 4 +-
src/include/catalog/pg_opclass.dat | 4 +-
src/include/catalog/pg_operator.dat | 18 +++---
src/include/catalog/pg_opfamily.dat | 2 +-
src/include/catalog/pg_proc.dat | 66 +++++++++----------
src/include/catalog/pg_type.dat | 6 +-
src/interfaces/ecpg/ecpglib/execute.c | 2 +-
src/test/regress/expected/alter_table.out | 8 +--
src/test/regress/expected/groupingsets.out | 2 +-
src/test/regress/expected/opr_sanity.out | 8 +--
src/test/regress/expected/update.out | 4 +-
src/test/regress/expected/xid.out | 74 +++++++++++-----------
src/test/regress/sql/alter_table.sql | 8 +--
src/test/regress/sql/groupingsets.sql | 2 +-
src/test/regress/sql/update.sql | 4 +-
src/test/regress/sql/xid.sql | 34 +++++-----
31 files changed, 164 insertions(+), 164 deletions(-)
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index b3d1367fec..a38fa344cb 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -315,7 +315,7 @@ check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
*/
if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
(argtype == DATEOID ||
- argtype == XIDOID || argtype == CIDOID))
+ argtype == XID4OID || argtype == CIDOID))
/* okay, allowed use of hashint4() */ ;
else if ((funcid == F_HASHINT8 || funcid == F_HASHINT8EXTENDED) &&
(argtype == XID8OID))
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 630df672cc..a89d962a20 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -436,7 +436,7 @@ pg_last_committed_xact(PG_FUNCTION_ARGS)
*/
tupdesc = CreateTemplateTupleDesc(2);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "timestamp",
TIMESTAMPTZOID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 70d0e1c215..60d2aef83e 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -3360,7 +3360,7 @@ pg_get_multixact_members(PG_FUNCTION_ARGS)
tupdesc = CreateTemplateTupleDesc(2);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "mode",
TEXTOID, -1, 0);
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 2f7d4ed59a..d8c91ad66e 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -736,7 +736,7 @@ pg_prepared_xact(PG_FUNCTION_ARGS)
/* this had better match pg_prepared_xacts view in system_views.sql */
tupdesc = CreateTemplateTupleDesc(5);
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "transaction",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "gid",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepared",
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 5480a024e0..08a8d642a0 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -131,8 +131,8 @@ static const struct typinfo TypInfo[] = {
F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
F_TIDIN, F_TIDOUT},
- {"xid", XIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
- F_XIDIN, F_XIDOUT},
+ {"xid4", XID4OID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
+ F_XID4IN, F_XID4OUT},
{"cid", CIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_CIDIN, F_CIDOUT},
{"pg_node_tree", PGNODETREEOID, 0, -1, false, TYPALIGN_INT, TYPSTORAGE_EXTENDED, DEFAULT_COLLATION_OID,
diff --git a/src/backend/catalog/Catalog.pm b/src/backend/catalog/Catalog.pm
index dd39a086ce..b8926cea81 100644
--- a/src/backend/catalog/Catalog.pm
+++ b/src/backend/catalog/Catalog.pm
@@ -33,7 +33,7 @@ sub ParseHeader
'int64' => 'int8',
'Oid' => 'oid',
'NameData' => 'name',
- 'TransactionId' => 'xid',
+ 'TransactionId' => 'xid4',
'XLogRecPtr' => 'pg_lsn');
my %catalog;
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index 8e03af4ffc..640b070b09 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -742,9 +742,9 @@ sub gen_pg_attribute
$attnum = 0;
my @SYS_ATTRS = (
{ name => 'ctid', type => 'tid' },
- { name => 'xmin', type => 'xid' },
+ { name => 'xmin', type => 'xid4' },
{ name => 'cmin', type => 'cid' },
- { name => 'xmax', type => 'xid' },
+ { name => 'xmax', type => 'xid4' },
{ name => 'cmax', type => 'cid' },
{ name => 'tableoid', type => 'oid' });
foreach my $attr (@SYS_ATTRS)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 632c058b80..a0b9577b94 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -164,7 +164,7 @@ static const FormData_pg_attribute a1 = {
static const FormData_pg_attribute a2 = {
.attname = {"xmin"},
- .atttypid = XIDOID,
+ .atttypid = XID4OID,
.attlen = sizeof(TransactionId),
.attnum = MinTransactionIdAttributeNumber,
.attcacheoff = -1,
@@ -192,7 +192,7 @@ static const FormData_pg_attribute a3 = {
static const FormData_pg_attribute a4 = {
.attname = {"xmax"},
- .atttypid = XIDOID,
+ .atttypid = XID4OID,
.attlen = sizeof(TransactionId),
.attnum = MaxTransactionIdAttributeNumber,
.attcacheoff = -1,
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index d406ea8118..732a4362c1 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -1233,7 +1233,7 @@ CREATE OR REPLACE FUNCTION
CREATE OR REPLACE FUNCTION pg_logical_slot_get_changes(
IN slot_name name, IN upto_lsn pg_lsn, IN upto_nchanges int, VARIADIC options text[] DEFAULT '{}',
- OUT lsn pg_lsn, OUT xid xid, OUT data text)
+ OUT lsn pg_lsn, OUT xid xid4, OUT data text)
RETURNS SETOF RECORD
LANGUAGE INTERNAL
VOLATILE ROWS 1000 COST 1000
@@ -1241,7 +1241,7 @@ AS 'pg_logical_slot_get_changes';
CREATE OR REPLACE FUNCTION pg_logical_slot_peek_changes(
IN slot_name name, IN upto_lsn pg_lsn, IN upto_nchanges int, VARIADIC options text[] DEFAULT '{}',
- OUT lsn pg_lsn, OUT xid xid, OUT data text)
+ OUT lsn pg_lsn, OUT xid xid4, OUT data text)
RETURNS SETOF RECORD
LANGUAGE INTERNAL
VOLATILE ROWS 1000 COST 1000
@@ -1249,7 +1249,7 @@ AS 'pg_logical_slot_peek_changes';
CREATE OR REPLACE FUNCTION pg_logical_slot_get_binary_changes(
IN slot_name name, IN upto_lsn pg_lsn, IN upto_nchanges int, VARIADIC options text[] DEFAULT '{}',
- OUT lsn pg_lsn, OUT xid xid, OUT data bytea)
+ OUT lsn pg_lsn, OUT xid xid4, OUT data bytea)
RETURNS SETOF RECORD
LANGUAGE INTERNAL
VOLATILE ROWS 1000 COST 1000
@@ -1257,7 +1257,7 @@ AS 'pg_logical_slot_get_binary_changes';
CREATE OR REPLACE FUNCTION pg_logical_slot_peek_binary_changes(
IN slot_name name, IN upto_lsn pg_lsn, IN upto_nchanges int, VARIADIC options text[] DEFAULT '{}',
- OUT lsn pg_lsn, OUT xid xid, OUT data bytea)
+ OUT lsn pg_lsn, OUT xid xid4, OUT data bytea)
RETURNS SETOF RECORD
LANGUAGE INTERNAL
VOLATILE ROWS 1000 COST 1000
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index ecb1bf92ff..55942ee473 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -121,7 +121,7 @@ pg_lock_status(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "virtualxid",
TEXTOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 7, "transactionid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "classid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "objid",
diff --git a/src/backend/utils/adt/xid.c b/src/backend/utils/adt/xid.c
index 20389aff1d..2cd5527772 100644
--- a/src/backend/utils/adt/xid.c
+++ b/src/backend/utils/adt/xid.c
@@ -31,7 +31,7 @@
Datum
-xidin(PG_FUNCTION_ARGS)
+xid4in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
@@ -39,7 +39,7 @@ xidin(PG_FUNCTION_ARGS)
}
Datum
-xidout(PG_FUNCTION_ARGS)
+xid4out(PG_FUNCTION_ARGS)
{
TransactionId transactionId = PG_GETARG_TRANSACTIONID(0);
char *result = (char *) palloc(16);
@@ -49,10 +49,10 @@ xidout(PG_FUNCTION_ARGS)
}
/*
- * xidrecv - converts external binary format to xid
+ * xid4recv - converts external binary format to xid4
*/
Datum
-xidrecv(PG_FUNCTION_ARGS)
+xid4recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
@@ -60,10 +60,10 @@ xidrecv(PG_FUNCTION_ARGS)
}
/*
- * xidsend - converts xid to binary format
+ * xid4send - converts xid4 to binary format
*/
Datum
-xidsend(PG_FUNCTION_ARGS)
+xid4send(PG_FUNCTION_ARGS)
{
TransactionId arg1 = PG_GETARG_TRANSACTIONID(0);
StringInfoData buf;
@@ -74,10 +74,10 @@ xidsend(PG_FUNCTION_ARGS)
}
/*
- * xideq - are two xids equal?
+ * xid4eq - are two xid4s equal?
*/
Datum
-xideq(PG_FUNCTION_ARGS)
+xid4eq(PG_FUNCTION_ARGS)
{
TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);
@@ -86,10 +86,10 @@ xideq(PG_FUNCTION_ARGS)
}
/*
- * xidneq - are two xids different?
+ * xid4ne - are two xid4s different?
*/
Datum
-xidneq(PG_FUNCTION_ARGS)
+xid4ne(PG_FUNCTION_ARGS)
{
TransactionId xid1 = PG_GETARG_TRANSACTIONID(0);
TransactionId xid2 = PG_GETARG_TRANSACTIONID(1);
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 419b58330f..66b1f8eb1a 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -110,23 +110,23 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
TupleDescInitEntry(tupdesc, (AttrNumber) 8, "next_oid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 9, "next_multixact_id",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 10, "next_multi_offset",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 11, "oldest_xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 12, "oldest_xid_dbid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 13, "oldest_active_xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 14, "oldest_multi_xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 15, "oldest_multi_dbid",
OIDOID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 16, "oldest_commit_ts_xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 17, "newest_commit_ts_xid",
- XIDOID, -1, 0);
+ XID4OID, -1, 0);
TupleDescInitEntry(tupdesc, (AttrNumber) 18, "checkpoint_time",
TIMESTAMPTZOID, -1, 0);
tupdesc = BlessTupleDesc(tupdesc);
diff --git a/src/fe_utils/print.c b/src/fe_utils/print.c
index 66a50f183f..2134f9b84c 100644
--- a/src/fe_utils/print.c
+++ b/src/fe_utils/print.c
@@ -3508,7 +3508,7 @@ column_type_alignment(Oid ftype)
case FLOAT8OID:
case NUMERICOID:
case OIDOID:
- case XIDOID:
+ case XID4OID:
case XID8OID:
case CIDOID:
case CASHOID:
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index b5dfaad9ec..3f60752501 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1023,9 +1023,9 @@
amoprighttype => 'bytea', amopstrategy => '1', amopopr => '=(bytea,bytea)',
amopmethod => 'hash' },
-# xid_ops
-{ amopfamily => 'hash/xid_ops', amoplefttype => 'xid', amoprighttype => 'xid',
- amopstrategy => '1', amopopr => '=(xid,xid)', amopmethod => 'hash' },
+# xid4_ops
+{ amopfamily => 'hash/xid4_ops', amoplefttype => 'xid4', amoprighttype => 'xid4',
+ amopstrategy => '1', amopopr => '=(xid4,xid4)', amopmethod => 'hash' },
# xid8_ops
{ amopfamily => 'hash/xid8_ops', amoplefttype => 'xid8', amoprighttype => 'xid8',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 37b580883f..922d8d46f4 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -399,10 +399,10 @@
{ amprocfamily => 'hash/bytea_ops', amproclefttype => 'bytea',
amprocrighttype => 'bytea', amprocnum => '2',
amproc => 'hashvarlenaextended' },
-{ amprocfamily => 'hash/xid_ops', amproclefttype => 'xid',
- amprocrighttype => 'xid', amprocnum => '1', amproc => 'hashint4' },
-{ amprocfamily => 'hash/xid_ops', amproclefttype => 'xid',
- amprocrighttype => 'xid', amprocnum => '2', amproc => 'hashint4extended' },
+{ amprocfamily => 'hash/xid4_ops', amproclefttype => 'xid4',
+ amprocrighttype => 'xid4', amprocnum => '1', amproc => 'hashint4' },
+{ amprocfamily => 'hash/xid4_ops', amproclefttype => 'xid4',
+ amprocrighttype => 'xid4', amprocnum => '2', amproc => 'hashint4extended' },
{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
amprocrighttype => 'xid8', amprocnum => '1', amproc => 'hashint8' },
{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 5a58f50fbb..ec698b12f3 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -93,8 +93,8 @@
{ 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)',
+# Allow explicit coercions between xid8 and xid4
+{ castsource => 'xid8', casttarget => 'xid4', castfunc => 'xid4(xid8)',
castcontext => 'e', castmethod => 'f' },
# OID category: allow implicit conversion from any integral type (including
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index f2342bb328..860d0ed020 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -166,8 +166,8 @@
opcintype => 'bytea' },
{ opcmethod => 'btree', opcname => 'tid_ops', opcfamily => 'btree/tid_ops',
opcintype => 'tid' },
-{ opcmethod => 'hash', opcname => 'xid_ops', opcfamily => 'hash/xid_ops',
- opcintype => 'xid' },
+{ opcmethod => 'hash', opcname => 'xid4_ops', opcfamily => 'hash/xid4_ops',
+ opcintype => 'xid4' },
{ opcmethod => 'hash', opcname => 'xid8_ops', opcfamily => 'hash/xid8_ops',
opcintype => 'xid8' },
{ opcmethod => 'btree', opcname => 'xid8_ops', opcfamily => 'btree/xid8_ops',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 00ada7e48f..22467f5d42 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -178,20 +178,20 @@
oprresult => 'anyarray', oprcode => 'array_cat' },
{ oid => '352', descr => 'equal',
- oprname => '=', oprcanhash => 't', oprleft => 'xid', oprright => 'xid',
- oprresult => 'bool', oprcom => '=(xid,xid)', oprnegate => '<>(xid,xid)',
- oprcode => 'xideq', oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+ oprname => '=', oprcanhash => 't', oprleft => 'xid4', oprright => 'xid4',
+ oprresult => 'bool', oprcom => '=(xid4,xid4)', oprnegate => '<>(xid4,xid4)',
+ oprcode => 'xid4eq', oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
{ oid => '353', descr => 'equal',
- oprname => '=', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
- oprnegate => '<>(xid,int4)', oprcode => 'xideqint4', oprrest => 'eqsel',
+ oprname => '=', oprleft => 'xid4', oprright => 'int4', oprresult => 'bool',
+ oprnegate => '<>(xid4,int4)', oprcode => 'xid4eqint4', oprrest => 'eqsel',
oprjoin => 'eqjoinsel' },
{ oid => '3315', descr => 'not equal',
- oprname => '<>', oprleft => 'xid', oprright => 'xid', oprresult => 'bool',
- oprcom => '<>(xid,xid)', oprnegate => '=(xid,xid)', oprcode => 'xidneq',
+ oprname => '<>', oprleft => 'xid4', oprright => 'xid4', oprresult => 'bool',
+ oprcom => '<>(xid4,xid4)', oprnegate => '=(xid4,xid4)', oprcode => 'xid4ne',
oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
{ oid => '3316', descr => 'not equal',
- oprname => '<>', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
- oprnegate => '=(xid,int4)', oprcode => 'xidneqint4', oprrest => 'neqsel',
+ oprname => '<>', oprleft => 'xid4', oprright => 'int4', oprresult => 'bool',
+ oprnegate => '=(xid4,int4)', oprcode => 'xid4neint4', oprrest => 'neqsel',
oprjoin => 'neqjoinsel' },
{ oid => '9418', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'xid8',
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 4004138d77..adc63eb61a 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -109,7 +109,7 @@
{ oid => '2789',
opfmethod => 'btree', opfname => 'tid_ops' },
{ oid => '2225',
- opfmethod => 'hash', opfname => 'xid_ops' },
+ opfmethod => 'hash', opfname => 'xid4_ops' },
{ oid => '8164',
opfmethod => 'hash', opfname => 'xid8_ops' },
{ oid => '9322',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 4bce3ad8de..54ec931035 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -107,11 +107,11 @@
proname => 'tidout', prorettype => 'cstring', proargtypes => 'tid',
prosrc => 'tidout' },
{ oid => '50', descr => 'I/O',
- proname => 'xidin', prorettype => 'xid', proargtypes => 'cstring',
- prosrc => 'xidin' },
+ proname => 'xid4in', prorettype => 'xid4', proargtypes => 'cstring',
+ prosrc => 'xid4in' },
{ oid => '51', descr => 'I/O',
- proname => 'xidout', prorettype => 'cstring', proargtypes => 'xid',
- prosrc => 'xidout' },
+ proname => 'xid4out', prorettype => 'cstring', proargtypes => 'xid4',
+ prosrc => 'xid4out' },
{ oid => '9420', descr => 'I/O',
proname => 'xid8in', prorettype => 'xid8', proargtypes => 'cstring',
prosrc => 'xid8in' },
@@ -170,11 +170,11 @@
proname => 'starts_with', proleakproof => 't', prorettype => 'bool',
proargtypes => 'text text', prosrc => 'text_starts_with' },
{ oid => '68',
- proname => 'xideq', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid xid', prosrc => 'xideq' },
+ proname => 'xid4eq', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid4 xid4', prosrc => 'xid4eq' },
{ oid => '3308',
- proname => 'xidneq', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid xid', prosrc => 'xidneq' },
+ proname => 'xid4ne', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid4 xid4', prosrc => 'xid4ne' },
{ oid => '9557',
proname => 'xid8eq', proleakproof => 't', prorettype => 'bool',
proargtypes => 'xid8 xid8', prosrc => 'xid8eq' },
@@ -196,8 +196,8 @@
{ 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',
+{ oid => '9421', descr => 'convert xid8 to xid4',
+ proname => 'xid4', prorettype => 'xid4', proargtypes => 'xid8',
prosrc => 'xid8toxid' },
{ oid => '69',
proname => 'cideq', proleakproof => 't', prorettype => 'bool',
@@ -2343,11 +2343,11 @@
{ oid => '1181',
descr => 'age of a transaction ID, in transactions before current transaction',
proname => 'age', provolatile => 's', proparallel => 'r',
- prorettype => 'int4', proargtypes => 'xid', prosrc => 'xid_age' },
+ prorettype => 'int4', proargtypes => 'xid4', prosrc => 'xid_age' },
{ oid => '3939',
descr => 'age of a multi-transaction ID, in multi-transactions before current multi-transaction',
proname => 'mxid_age', provolatile => 's', prorettype => 'int4',
- proargtypes => 'xid', prosrc => 'mxid_age' },
+ proargtypes => 'xid4', prosrc => 'mxid_age' },
{ oid => '1188',
proname => 'timestamptz_mi', prorettype => 'interval',
@@ -2681,11 +2681,11 @@
prosrc => 'bpcharlen' },
{ oid => '1319',
- proname => 'xideqint4', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid int4', prosrc => 'xideq' },
+ proname => 'xid4eqint4', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid4 int4', prosrc => 'xid4eq' },
{ oid => '3309',
- proname => 'xidneqint4', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid int4', prosrc => 'xidneq' },
+ proname => 'xid4neint4', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid4 int4', prosrc => 'xid4ne' },
{ oid => '1326',
proname => 'interval_div', prorettype => 'interval',
@@ -5219,7 +5219,7 @@
proname => 'pg_stat_get_activity', prorows => '100', proisstrict => 'f',
proretset => 't', provolatile => 's', proparallel => 'r',
prorettype => 'record', proargtypes => 'int4',
- proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid,xid,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool,int4}',
+ proallargtypes => '{int4,oid,int4,oid,text,text,text,text,text,timestamptz,timestamptz,timestamptz,timestamptz,inet,text,int4,xid4,xid4,text,bool,text,text,int4,bool,text,numeric,text,bool,text,bool,int4}',
proargmodes => '{i,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{pid,datid,pid,usesysid,application_name,state,query,wait_event_type,wait_event,xact_start,query_start,backend_start,state_change,client_addr,client_hostname,client_port,backend_xid,backend_xmin,backend_type,ssl,sslversion,sslcipher,sslbits,sslcompression,ssl_client_dn,ssl_client_serial,ssl_issuer_dn,gss_auth,gss_princ,gss_enc,leader_pid}',
prosrc => 'pg_stat_get_activity' },
@@ -5910,7 +5910,7 @@
{ oid => '1371', descr => 'view system lock information',
proname => 'pg_lock_status', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
- proallargtypes => '{text,oid,oid,int4,int2,text,xid,oid,oid,int2,text,int4,text,bool,bool}',
+ proallargtypes => '{text,oid,oid,int4,int2,text,xid4,oid,oid,int2,text,int4,text,bool,bool}',
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{locktype,database,relation,page,tuple,virtualxid,transactionid,classid,objid,objsubid,virtualtransaction,pid,mode,granted,fastpath}',
prosrc => 'pg_lock_status' },
@@ -5930,26 +5930,26 @@
{ oid => '1065', descr => 'view two-phase transactions',
proname => 'pg_prepared_xact', prorows => '1000', proretset => 't',
provolatile => 'v', prorettype => 'record', proargtypes => '',
- proallargtypes => '{xid,text,timestamptz,oid,oid}',
+ proallargtypes => '{xid4,text,timestamptz,oid,oid}',
proargmodes => '{o,o,o,o,o}',
proargnames => '{transaction,gid,prepared,ownerid,dbid}',
prosrc => 'pg_prepared_xact' },
{ oid => '3819', descr => 'view members of a multixactid',
proname => 'pg_get_multixact_members', prorows => '1000', proretset => 't',
- provolatile => 'v', prorettype => 'record', proargtypes => 'xid',
- proallargtypes => '{xid,xid,text}', proargmodes => '{i,o,o}',
+ provolatile => 'v', prorettype => 'record', proargtypes => 'xid4',
+ proallargtypes => '{xid4,xid4,text}', proargmodes => '{i,o,o}',
proargnames => '{multixid,xid,mode}', prosrc => 'pg_get_multixact_members' },
{ oid => '3581', descr => 'get commit timestamp of a transaction',
proname => 'pg_xact_commit_timestamp', provolatile => 'v',
- prorettype => 'timestamptz', proargtypes => 'xid',
+ prorettype => 'timestamptz', proargtypes => 'xid4',
prosrc => 'pg_xact_commit_timestamp' },
{ oid => '3583',
descr => 'get transaction Id and commit timestamp of latest transaction commit',
proname => 'pg_last_committed_xact', provolatile => 'v',
prorettype => 'record', proargtypes => '',
- proallargtypes => '{xid,timestamptz}', proargmodes => '{o,o}',
+ proallargtypes => '{xid4,timestamptz}', proargmodes => '{o,o}',
proargnames => '{xid,timestamp}', prosrc => 'pg_last_committed_xact' },
{ oid => '3537', descr => 'get identification of SQL object',
@@ -7507,11 +7507,11 @@
proname => 'tidsend', prorettype => 'bytea', proargtypes => 'tid',
prosrc => 'tidsend' },
{ oid => '2440', descr => 'I/O',
- proname => 'xidrecv', prorettype => 'xid', proargtypes => 'internal',
- prosrc => 'xidrecv' },
+ proname => 'xid4recv', prorettype => 'xid4', proargtypes => 'internal',
+ prosrc => 'xid4recv' },
{ oid => '2441', descr => 'I/O',
- proname => 'xidsend', prorettype => 'bytea', proargtypes => 'xid',
- prosrc => 'xidsend' },
+ proname => 'xid4send', prorettype => 'bytea', proargtypes => 'xid4',
+ prosrc => 'xid4send' },
{ oid => '2442', descr => 'I/O',
proname => 'cidrecv', prorettype => 'cid', proargtypes => 'internal',
prosrc => 'cidrecv' },
@@ -10065,7 +10065,7 @@
proname => 'pg_get_replication_slots', prorows => '10', proisstrict => 'f',
proretset => 't', provolatile => 's', prorettype => 'record',
proargtypes => '',
- proallargtypes => '{name,name,text,oid,bool,bool,int4,xid,xid,pg_lsn,pg_lsn,text,pg_lsn}',
+ proallargtypes => '{name,name,text,oid,bool,bool,int4,xid4,xid4,pg_lsn,pg_lsn,text,pg_lsn}',
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{slot_name,plugin,slot_type,datoid,temporary,active,active_pid,xmin,catalog_xmin,restart_lsn,confirmed_flush_lsn,wal_status,min_safe_lsn}',
prosrc => 'pg_get_replication_slots' },
@@ -10104,7 +10104,7 @@
prorows => '1000', provariadic => 'text', proisstrict => 'f',
proretset => 't', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'name pg_lsn int4 _text',
- proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid,text}',
+ proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid4,text}',
proargmodes => '{i,i,i,v,o,o,o}',
proargnames => '{slot_name,upto_lsn,upto_nchanges,options,lsn,xid,data}',
prosrc => 'pg_logical_slot_get_changes' },
@@ -10113,7 +10113,7 @@
prorows => '1000', provariadic => 'text', proisstrict => 'f',
proretset => 't', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'name pg_lsn int4 _text',
- proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid,bytea}',
+ proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid4,bytea}',
proargmodes => '{i,i,i,v,o,o,o}',
proargnames => '{slot_name,upto_lsn,upto_nchanges,options,lsn,xid,data}',
prosrc => 'pg_logical_slot_get_binary_changes' },
@@ -10122,7 +10122,7 @@
prorows => '1000', provariadic => 'text', proisstrict => 'f',
proretset => 't', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'name pg_lsn int4 _text',
- proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid,text}',
+ proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid4,text}',
proargmodes => '{i,i,i,v,o,o,o}',
proargnames => '{slot_name,upto_lsn,upto_nchanges,options,lsn,xid,data}',
prosrc => 'pg_logical_slot_peek_changes' },
@@ -10131,7 +10131,7 @@
prorows => '1000', provariadic => 'text', proisstrict => 'f',
proretset => 't', provolatile => 'v', proparallel => 'u',
prorettype => 'record', proargtypes => 'name pg_lsn int4 _text',
- proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid,bytea}',
+ proallargtypes => '{name,pg_lsn,int4,_text,pg_lsn,xid4,bytea}',
proargmodes => '{i,i,i,v,o,o,o}',
proargnames => '{slot_name,upto_lsn,upto_nchanges,options,lsn,xid,data}',
prosrc => 'pg_logical_slot_peek_binary_changes' },
@@ -10842,7 +10842,7 @@
descr => 'pg_controldata checkpoint state information as a function',
proname => 'pg_control_checkpoint', provolatile => 'v',
prorettype => 'record', proargtypes => '',
- proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,text,oid,xid,xid,xid,oid,xid,xid,oid,xid,xid,timestamptz}',
+ proallargtypes => '{pg_lsn,pg_lsn,text,int4,int4,bool,text,oid,xid4,xid4,xid4,oid,xid4,xid4,oid,xid4,xid4,timestamptz}',
proargmodes => '{o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o,o}',
proargnames => '{checkpoint_lsn,redo_lsn,redo_wal_file,timeline_id,prev_timeline_id,full_page_writes,next_xid,next_oid,next_multixact_id,next_multi_offset,oldest_xid,oldest_xid_dbid,oldest_active_xid,oldest_multi_xid,oldest_multi_dbid,oldest_commit_ts_xid,newest_commit_ts_xid,checkpoint_time}',
prosrc => 'pg_control_checkpoint' },
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index 78f44af5e7..912d41d5c9 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -97,9 +97,9 @@
typinput => 'tidin', typoutput => 'tidout', typreceive => 'tidrecv',
typsend => 'tidsend', typalign => 's' },
{ oid => '28', array_type_oid => '1011', descr => 'transaction id',
- typname => 'xid', typlen => '4', typbyval => 't', typcategory => 'U',
- typinput => 'xidin', typoutput => 'xidout', typreceive => 'xidrecv',
- typsend => 'xidsend', typalign => 'i' },
+ typname => 'xid4', typlen => '4', typbyval => 't', typcategory => 'U',
+ typinput => 'xid4in', typoutput => 'xid4out', typreceive => 'xid4recv',
+ typsend => 'xid4send', typalign => 'i' },
{ oid => '29', array_type_oid => '1012',
descr => 'command identifier type, sequence in transaction id',
typname => 'cid', typlen => '4', typbyval => 't', typcategory => 'U',
diff --git a/src/interfaces/ecpg/ecpglib/execute.c b/src/interfaces/ecpg/ecpglib/execute.c
index 59e8e3a825..230fec9c41 100644
--- a/src/interfaces/ecpg/ecpglib/execute.c
+++ b/src/interfaces/ecpg/ecpglib/execute.c
@@ -204,7 +204,7 @@ ecpg_is_type_an_array(int type, const struct statement *stmt, const struct varia
return ECPG_ARRAY_ERROR;
if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), TIDOID, ECPG_ARRAY_NONE, stmt->lineno))
return ECPG_ARRAY_ERROR;
- if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), XIDOID, ECPG_ARRAY_NONE, stmt->lineno))
+ if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), XID4OID, ECPG_ARRAY_NONE, stmt->lineno))
return ECPG_ARRAY_ERROR;
if (!ecpg_type_infocache_push(&(stmt->connection->cache_head), CIDOID, ECPG_ARRAY_NONE, stmt->lineno))
return ECPG_ARRAY_ERROR;
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index f343f9b42e..83d09e4205 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -26,7 +26,7 @@ ALTER TABLE attmp ADD COLUMN g polygon;
ALTER TABLE attmp ADD COLUMN i char;
ALTER TABLE attmp ADD COLUMN k int4;
ALTER TABLE attmp ADD COLUMN l tid;
-ALTER TABLE attmp ADD COLUMN m xid;
+ALTER TABLE attmp ADD COLUMN m xid4;
ALTER TABLE attmp ADD COLUMN n oidvector;
--ALTER TABLE attmp ADD COLUMN o lock;
ALTER TABLE attmp ADD COLUMN p boolean;
@@ -68,7 +68,7 @@ ALTER TABLE attmp ADD COLUMN g polygon;
ALTER TABLE attmp ADD COLUMN i char;
ALTER TABLE attmp ADD COLUMN k int4;
ALTER TABLE attmp ADD COLUMN l tid;
-ALTER TABLE attmp ADD COLUMN m xid;
+ALTER TABLE attmp ADD COLUMN m xid4;
ALTER TABLE attmp ADD COLUMN n oidvector;
--ALTER TABLE attmp ADD COLUMN o lock;
ALTER TABLE attmp ADD COLUMN p boolean;
@@ -2556,7 +2556,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = pg_current_xact_id()::xid)
+ where transactionid = pg_current_xact_id()::xid4)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -2719,7 +2719,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = pg_current_xact_id()::xid)
+ where transactionid = pg_current_xact_id()::xid4)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/expected/groupingsets.out b/src/test/regress/expected/groupingsets.out
index 05ff204f02..4301ba5d79 100644
--- a/src/test/regress/expected/groupingsets.out
+++ b/src/test/regress/expected/groupingsets.out
@@ -14,7 +14,7 @@ create temp table gstest3 (a integer, b integer, c integer, d integer);
copy gstest3 from stdin;
alter table gstest3 add primary key (a);
create temp table gstest4(id integer, v integer,
- unhashable_col bit(4), unsortable_col xid);
+ unhashable_col bit(4), unsortable_col xid4);
insert into gstest4
values (1,1,b'0000','1'), (2,2,b'0001','1'),
(3,4,b'0010','2'), (4,8,b'0011','2'),
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 8d350ab61b..74b3abf66c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -542,7 +542,7 @@ int2lt(smallint,smallint)
int4eq(integer,integer)
int4lt(integer,integer)
texteq(text,text)
-xideq(xid,xid)
+xid4eq(xid4,xid4)
cideq(cid,cid)
charne("char","char")
charle("char","char")
@@ -719,7 +719,7 @@ tidne(tid,tid)
tideq(tid,tid)
timestamptz_cmp(timestamp with time zone,timestamp with time zone)
interval_cmp(interval,interval)
-xideqint4(xid,integer)
+xid4eqint4(xid4,integer)
timetz_eq(time with time zone,time with time zone)
timetz_ne(time with time zone,time with time zone)
timetz_lt(time with time zone,time with time zone)
@@ -822,8 +822,8 @@ pg_lsn_ge(pg_lsn,pg_lsn)
pg_lsn_gt(pg_lsn,pg_lsn)
pg_lsn_ne(pg_lsn,pg_lsn)
pg_lsn_cmp(pg_lsn,pg_lsn)
-xidneq(xid,xid)
-xidneqint4(xid,integer)
+xid4ne(xid4,xid4)
+xid4neint4(xid4,integer)
sha224(bytea)
sha256(bytea)
sha384(bytea)
diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out
index bf939d79f6..3a76444dea 100644
--- a/src/test/regress/expected/update.out
+++ b/src/test/regress/expected/update.out
@@ -232,7 +232,7 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid4 AS xmin_correct, xmax = 0 AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
@@ -242,7 +242,7 @@ INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid4 AS xmin_correct, xmax = pg_current_xact_id()::xid4 AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index b7a1ed0f9e..549ad4664a 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -1,29 +1,29 @@
--- xid and xid8
+-- xid4 and xid8
-- values in range, in octal, decimal, hex
-select '010'::xid,
- '42'::xid,
- '0xffffffff'::xid,
- '-1'::xid,
+select '010'::xid4,
+ '42'::xid4,
+ '0xffffffff'::xid4,
+ '-1'::xid4,
'010'::xid8,
'42'::xid8,
'0xffffffffffffffff'::xid8,
'-1'::xid8;
- xid | xid | xid | xid | xid8 | xid8 | xid8 | xid8
------+-----+------------+------------+------+------+----------------------+----------------------
- 8 | 42 | 4294967295 | 4294967295 | 8 | 42 | 18446744073709551615 | 18446744073709551615
+ xid4 | xid4 | xid4 | xid4 | 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
+select ''::xid4;
+ xid4
+------
+ 0
(1 row)
-select 'asdf'::xid;
- xid
------
- 0
+select 'asdf'::xid4;
+ xid4
+------
+ 0
(1 row)
select ''::xid8;
@@ -39,13 +39,13 @@ select 'asdf'::xid8;
(1 row)
-- equality
-select '1'::xid = '1'::xid;
+select '1'::xid4 = '1'::xid4;
?column?
----------
t
(1 row)
-select '1'::xid != '1'::xid;
+select '1'::xid4 != '1'::xid4;
?column?
----------
f
@@ -64,38 +64,38 @@ select '1'::xid8 != '1'::xid8;
(1 row)
-- conversion
-select '1'::xid = '1'::xid8::xid;
+select '1'::xid4 = '1'::xid8::xid4;
?column?
----------
t
(1 row)
-select '1'::xid != '1'::xid8::xid;
+select '1'::xid4 != '1'::xid8::xid4;
?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;
- ^
+-- we don't want relational operators for xid4, due to use of modular arithmetic
+select '1'::xid4 < '2'::xid4;
+ERROR: operator does not exist: xid4 < xid4
+LINE 1: select '1'::xid4 < '2'::xid4;
+ ^
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;
- ^
+select '1'::xid4 <= '2'::xid4;
+ERROR: operator does not exist: xid4 <= xid4
+LINE 1: select '1'::xid4 <= '2'::xid4;
+ ^
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;
- ^
+select '1'::xid4 > '2'::xid4;
+ERROR: operator does not exist: xid4 > xid4
+LINE 1: select '1'::xid4 > '2'::xid4;
+ ^
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;
- ^
+select '1'::xid4 >= '2'::xid4;
+ERROR: operator does not exist: xid4 >= xid4
+LINE 1: select '1'::xid4 >= '2'::xid4;
+ ^
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;
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index fb5c9139d1..50c65160cb 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -41,7 +41,7 @@ ALTER TABLE attmp ADD COLUMN k int4;
ALTER TABLE attmp ADD COLUMN l tid;
-ALTER TABLE attmp ADD COLUMN m xid;
+ALTER TABLE attmp ADD COLUMN m xid4;
ALTER TABLE attmp ADD COLUMN n oidvector;
@@ -104,7 +104,7 @@ ALTER TABLE attmp ADD COLUMN k int4;
ALTER TABLE attmp ADD COLUMN l tid;
-ALTER TABLE attmp ADD COLUMN m xid;
+ALTER TABLE attmp ADD COLUMN m xid4;
ALTER TABLE attmp ADD COLUMN n oidvector;
@@ -1645,7 +1645,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = pg_current_xact_id()::xid)
+ where transactionid = pg_current_xact_id()::xid4)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@@ -1732,7 +1732,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
- where transactionid = pg_current_xact_id()::xid)
+ where transactionid = pg_current_xact_id()::xid4)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'
diff --git a/src/test/regress/sql/groupingsets.sql b/src/test/regress/sql/groupingsets.sql
index 77e196798a..f8c03fa274 100644
--- a/src/test/regress/sql/groupingsets.sql
+++ b/src/test/regress/sql/groupingsets.sql
@@ -32,7 +32,7 @@ copy gstest3 from stdin;
alter table gstest3 add primary key (a);
create temp table gstest4(id integer, v integer,
- unhashable_col bit(4), unsortable_col xid);
+ unhashable_col bit(4), unsortable_col xid4);
insert into gstest4
values (1,1,b'0000','1'), (2,2,b'0001','1'),
(3,4,b'0010','2'), (4,8,b'0011','2'),
diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql
index 8c558a7bc7..d37140f421 100644
--- a/src/test/regress/sql/update.sql
+++ b/src/test/regress/sql/update.sql
@@ -119,12 +119,12 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid4 AS xmin_correct, xmax = 0 AS xmax_correct;
-- currently xmax is set after a conflict - that's probably not good,
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
- RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
+ RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid4 AS xmin_correct, xmax = pg_current_xact_id()::xid4 AS xmax_correct;
DROP TABLE update_test;
diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql
index 565714d462..8d9b40fe26 100644
--- a/src/test/regress/sql/xid.sql
+++ b/src/test/regress/sql/xid.sql
@@ -1,36 +1,36 @@
--- xid and xid8
+-- xid4 and xid8
-- values in range, in octal, decimal, hex
-select '010'::xid,
- '42'::xid,
- '0xffffffff'::xid,
- '-1'::xid,
+select '010'::xid4,
+ '42'::xid4,
+ '0xffffffff'::xid4,
+ '-1'::xid4,
'010'::xid8,
'42'::xid8,
'0xffffffffffffffff'::xid8,
'-1'::xid8;
-- garbage values are not yet rejected (perhaps they should be)
-select ''::xid;
-select 'asdf'::xid;
+select ''::xid4;
+select 'asdf'::xid4;
select ''::xid8;
select 'asdf'::xid8;
-- equality
-select '1'::xid = '1'::xid;
-select '1'::xid != '1'::xid;
+select '1'::xid4 = '1'::xid4;
+select '1'::xid4 != '1'::xid4;
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;
+select '1'::xid4 = '1'::xid8::xid4;
+select '1'::xid4 != '1'::xid8::xid4;
+
+-- we don't want relational operators for xid4, due to use of modular arithmetic
+select '1'::xid4 < '2'::xid4;
+select '1'::xid4 <= '2'::xid4;
+select '1'::xid4 > '2'::xid4;
+select '1'::xid4 >= '2'::xid4;
-- we want them for xid8 though
select '1'::xid8 < '2'::xid8, '2'::xid8 < '2'::xid8, '2'::xid8 < '1'::xid8;
--
2.20.1