From e1674ee61e0a6d5893cc73ef0404c75fd6515bad Mon Sep 17 00:00:00 2001 From: Thomas Munro 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 Reviewed-by: Takao Fujii Reviewed-by: Yoshikazu Imai Reviewed-by: 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 | 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 @@ txid_snapshot - user-level transaction ID snapshot + user-level transaction ID snapshot (see also xid8_snapshot) @@ -288,6 +288,12 @@ universally unique identifier + + xid8_snapshot + + user-level transaction ID snapshot + + xml 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. + + xid8_current + + + + xid8_current_if_assigned + + + + xid8_current_snapshot + + + + xid8_snapshot_xip + + + + xid8_snapshot_xmax + + + + xid8_snapshot_xmin + + + + xid8_visible_in_snapshot + + + + xid8_status + + txid_current @@ -18968,12 +19000,74 @@ SELECT collation for ('foo' COLLATE "de_DE"); - The functions shown in + The functions shown in provide server transaction information in an exportable form. The main use of these functions is to determine which transactions were committed between two snapshots. + + Transaction IDs and Snapshots + + + Name Return Type Description + + + + + xid8_current() + xid8 + get current transaction ID, assigning a new one if the current transaction does not have one + + + xid8_current_if_assigned() + xid8 + same as xid8_current() but returns null instead of assigning a new transaction ID if none is already assigned + + + xid8_current_snapshot() + xid8_snapshot + get current snapshot + + + xid8_snapshot_xip(xid8_snapshot) + setof xid8 + get in-progress transaction IDs in snapshot + + + xid8_snapshot_xmax(xid8_snapshot) + xid8 + get xmax of snapshot + + + xid8_snapshot_xmin(xid8_snapshot) + xid8 + get xmin of snapshot + + + xid8_visible_in_snapshot(xid8, xid8_snapshot) + boolean + is transaction ID visible in snapshot? (do not use with subtransaction ids) + + + xid8_status(xid8) + text + report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old + + + +
+ + + In releases of PostgreSQL before 13 there was + no xid8 type, so variants of these functions were provided + that used bigint. The older functions with + txid + in the name are still supported for backwards compatibility, but may be + removed from a future release. The bigint variants are shown + in . + + Transaction IDs and Snapshots @@ -18985,42 +19079,42 @@ SELECT collation for ('foo' COLLATE "de_DE"); txid_current() bigint - get current transaction ID, assigning a new one if the current transaction does not have one + see xid8_current() txid_current_if_assigned() bigint - same as txid_current() but returns null instead of assigning a new transaction ID if none is already assigned + see xid8_current_if_assigned() txid_current_snapshot() txid_snapshot - get current snapshot + see xid8_snapshot() txid_snapshot_xip(txid_snapshot) setof bigint - get in-progress transaction IDs in snapshot + see xid8_snapshot_xip() txid_snapshot_xmax(txid_snapshot) bigint - get xmax of snapshot + see xid8_snapshot_xmax() txid_snapshot_xmin(txid_snapshot) bigint - get xmin of snapshot + see xid8_snapshot_xmin() txid_visible_in_snapshot(bigint, txid_snapshot) boolean - is transaction ID visible in snapshot? (do not use with subtransaction ids) + see xid8_visible_in_snapshot() txid_status(bigint) text - report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old + see xid8_status() @@ -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 epoch counter so it will not wrap around during the life of an installation. - The data type used by these functions, txid_snapshot, + The data type used by these functions, xid8_snapshot, stores information about transaction ID visibility at a particular moment in time. Its components are - described in . + described in . The + xid8 values it contains can be converted to xid + by casting, if required. -
+
Snapshot Components @@ -19052,7 +19148,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); xmin - 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. @@ -19061,7 +19157,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); xmax - 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. @@ -19069,14 +19165,14 @@ SELECT collation for ('foo' COLLATE "de_DE"); xip_list - Active txids at the time of the snapshot. The list - includes only those active txids between xmin - and xmax; there might be active txids higher - than xmax. A txid that is xmin <= txid < + Active xid8s at the time of the snapshot. The list + includes only those active xid8s between xmin + and xmax; there might be active xid8s higher + than xmax. An xid8 that is xmin <= xid8 < xmax 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. @@ -19085,14 +19181,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
- txid_snapshot's textual representation is + xid8_snapshot's textual representation is xmin:xmax:xip_list. For example 10:20:10,14,15 means xmin=10, xmax=20, xip_list=10, 14, 15. - txid_status(bigint) reports the commit status of a recent + xid8_status(xid8) 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 COMMIT is in progress. @@ -19106,7 +19202,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); transactions are reported as in progress; applications must check pg_prepared_xacts if they - need to determine whether the txid is a prepared transaction. + need to determine whether the xid8 is a prepared transaction. 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); Any actions leading to transaction ID assignment are prohibited. That, among others, includes writing to tables, performing DDL changes, and - calling txid_current(). + calling xid8_current(). 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
CLogTruncationLock - Waiting to execute txid_status or update + Waiting to execute xid8_status or update the oldest transaction id available to it. 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