-- 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
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
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
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
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
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
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
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
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; }
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; }
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
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); }
-- 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;
-- 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;
-- 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');
<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>
<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>
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>
</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>
<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>
</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.
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>
</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>
</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>
tsvector.o \
tsvector_op.o \
tsvector_parser.o \
- txid.o \
uuid.o \
varbit.o \
varchar.o \
version.o \
windowfuncs.o \
xid.o \
+ xid8funcs.o \
xml.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p
/*-------------------------------------------------------------------------
- * 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
*
*-------------------------------------------------------------------------
*/
#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
{
*/
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.
* 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;
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,
}
/*
- * 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;
}
* 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;
}
for (i = 0; i < snap->nxip; i++)
{
- if (value == snap->xip[i])
+ if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
}
/*
- * 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;
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);
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 */
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;
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
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);
}
/*
- * 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--;
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();
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
{
* 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;
/*
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
- if (TransactionIdInRecentPast(xid_with_epoch, &xid))
+ if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 202004061
+#define CATALOG_VERSION_NO 202004062
#endif
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',
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',
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (1, current_timestamp);
- SELECT txid_current();
+ SELECT pg_current_xact_id()::xid;
COMMIT;
]);
i int;
BEGIN
FOR i in 1..cnt LOOP
- EXECUTE 'SELECT txid_current()';
+ EXECUTE 'SELECT pg_current_xact_id()';
COMMIT;
END LOOP;
END;
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (2, current_timestamp);
- SELECT txid_current();
+ SELECT pg_current_xact_id();
COMMIT;
]);
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,
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,
$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
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.
$stdin .= q[
BEGIN;
CREATE TABLE mine(x integer);
-SELECT txid_current();
+SELECT pg_current_xact_id();
];
$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
$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;
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'
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'
-- 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
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
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
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
-- 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
-- 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 (
(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
-- 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
-- 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;
---------------------------
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;
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'
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'
--
-- 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();
-- 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;
-- 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;
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;
pg_sha256_ctx
pg_sha384_ctx
pg_sha512_ctx
+pg_snapshot
pg_stack_base_t
pg_time_t
pg_tz