diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index cf0318f697..d79cd316b7 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -382,7 +382,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc -- test whether a known, but not yet logged toplevel xact, followed by a -- subxact commit is handled correctly BEGIN; -SELECT txid_current() != 0; -- so no fixed xid apears in the outfile +SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile ?column? ---------- t diff --git a/contrib/test_decoding/expected/decoding_in_xact.out b/contrib/test_decoding/expected/decoding_in_xact.out index ab4d3aee72..b65253f463 100644 --- a/contrib/test_decoding/expected/decoding_in_xact.out +++ b/contrib/test_decoding/expected/decoding_in_xact.out @@ -2,7 +2,7 @@ SET synchronous_commit = on; -- fail because we're creating a slot while in an xact with xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; ?column? ---------- f @@ -13,7 +13,7 @@ ERROR: cannot create logical replication slot in transaction that has performed ROLLBACK; -- fail because we're creating a slot while in a subxact whose topxact has an xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; ?column? ---------- f @@ -50,7 +50,7 @@ CREATE TABLE nobarf(id serial primary key, data text); INSERT INTO nobarf(data) VALUES('1'); -- decoding works in transaction with xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; ?column? ---------- f diff --git a/contrib/test_decoding/expected/oldest_xmin.out b/contrib/test_decoding/expected/oldest_xmin.out index d1b4f17e3a..02a091398f 100644 --- a/contrib/test_decoding/expected/oldest_xmin.out +++ b/contrib/test_decoding/expected/oldest_xmin.out @@ -2,7 +2,7 @@ Parsed test spec with 2 sessions starting permutation: s0_begin s0_getxid s1_begin s1_insert s0_alter s0_commit s0_checkpoint s0_get_changes s0_get_changes s1_commit s0_vacuum s0_get_changes step s0_begin: BEGIN; -step s0_getxid: SELECT txid_current() IS NULL; +step s0_getxid: SELECT pg_current_xact_id() IS NULL; ?column? f diff --git a/contrib/test_decoding/expected/ondisk_startup.out b/contrib/test_decoding/expected/ondisk_startup.out index c7b1f45b46..586b03d75d 100644 --- a/contrib/test_decoding/expected/ondisk_startup.out +++ b/contrib/test_decoding/expected/ondisk_startup.out @@ -2,20 +2,20 @@ Parsed test spec with 3 sessions starting permutation: s2b s2txid s1init s3b s3txid s2alter s2c s2b s2txid s3c s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start step s2b: BEGIN; -step s2txid: SELECT txid_current() IS NULL; +step s2txid: SELECT pg_current_xact_id() IS NULL; ?column? f step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); step s3b: BEGIN; -step s3txid: SELECT txid_current() IS NULL; +step s3txid: SELECT pg_current_xact_id() IS NULL; ?column? f step s2alter: ALTER TABLE do_write ADD COLUMN addedbys2 int; step s2c: COMMIT; step s2b: BEGIN; -step s2txid: SELECT txid_current() IS NULL; +step s2txid: SELECT pg_current_xact_id() IS NULL; ?column? f diff --git a/contrib/test_decoding/expected/snapshot_transfer.out b/contrib/test_decoding/expected/snapshot_transfer.out index 87bed03f76..c3a0000994 100644 --- a/contrib/test_decoding/expected/snapshot_transfer.out +++ b/contrib/test_decoding/expected/snapshot_transfer.out @@ -3,7 +3,7 @@ Parsed test spec with 2 sessions starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub0 s0_commit s0_get_changes step s0_begin: BEGIN; step s0_begin_sub0: SAVEPOINT s0; -step s0_log_assignment: SELECT txid_current() IS NULL; +step s0_log_assignment: SELECT pg_current_xact_id() IS NULL; ?column? f @@ -26,7 +26,7 @@ stop starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_begin_sub1 s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub1 s0_end_sub0 s0_commit s0_get_changes step s0_begin: BEGIN; step s0_begin_sub0: SAVEPOINT s0; -step s0_log_assignment: SELECT txid_current() IS NULL; +step s0_log_assignment: SELECT pg_current_xact_id() IS NULL; ?column? f diff --git a/contrib/test_decoding/specs/oldest_xmin.spec b/contrib/test_decoding/specs/oldest_xmin.spec index 6cb13e85ce..da3a8cd512 100644 --- a/contrib/test_decoding/specs/oldest_xmin.spec +++ b/contrib/test_decoding/specs/oldest_xmin.spec @@ -19,7 +19,7 @@ teardown session "s0" setup { SET synchronous_commit=on; } step "s0_begin" { BEGIN; } -step "s0_getxid" { SELECT txid_current() IS NULL; } +step "s0_getxid" { SELECT pg_current_xact_id() IS NULL; } step "s0_alter" { ALTER TYPE basket DROP ATTRIBUTE mangos; } step "s0_commit" { COMMIT; } step "s0_checkpoint" { CHECKPOINT; } diff --git a/contrib/test_decoding/specs/ondisk_startup.spec b/contrib/test_decoding/specs/ondisk_startup.spec index 12c57a813d..96ce87f1a4 100644 --- a/contrib/test_decoding/specs/ondisk_startup.spec +++ b/contrib/test_decoding/specs/ondisk_startup.spec @@ -25,7 +25,7 @@ session "s2" setup { SET synchronous_commit=on; } step "s2b" { BEGIN; } -step "s2txid" { SELECT txid_current() IS NULL; } +step "s2txid" { SELECT pg_current_xact_id() IS NULL; } step "s2alter" { ALTER TABLE do_write ADD COLUMN addedbys2 int; } step "s2c" { COMMIT; } @@ -34,7 +34,7 @@ session "s3" setup { SET synchronous_commit=on; } step "s3b" { BEGIN; } -step "s3txid" { SELECT txid_current() IS NULL; } +step "s3txid" { SELECT pg_current_xact_id() IS NULL; } step "s3c" { COMMIT; } # Force usage of ondisk snapshot by starting and not finishing a diff --git a/contrib/test_decoding/specs/snapshot_transfer.spec b/contrib/test_decoding/specs/snapshot_transfer.spec index ae81e8f102..152f2fd03d 100644 --- a/contrib/test_decoding/specs/snapshot_transfer.spec +++ b/contrib/test_decoding/specs/snapshot_transfer.spec @@ -20,7 +20,7 @@ session "s0" setup { SET synchronous_commit=on; } step "s0_begin" { BEGIN; } step "s0_begin_sub0" { SAVEPOINT s0; } -step "s0_log_assignment" { SELECT txid_current() IS NULL; } +step "s0_log_assignment" { SELECT pg_current_xact_id() IS NULL; } step "s0_begin_sub1" { SAVEPOINT s1; } step "s0_sub_get_base_snap" { INSERT INTO dummy VALUES (0); } step "s0_insert" { INSERT INTO harvest VALUES (1, 2, 3); } diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql index 0f2b9992f7..2c4823e578 100644 --- a/contrib/test_decoding/sql/ddl.sql +++ b/contrib/test_decoding/sql/ddl.sql @@ -220,7 +220,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc -- test whether a known, but not yet logged toplevel xact, followed by a -- subxact commit is handled correctly BEGIN; -SELECT txid_current() != 0; -- so no fixed xid apears in the outfile +SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile SAVEPOINT a; INSERT INTO tr_sub(path) VALUES ('4-top-1-#1'); RELEASE SAVEPOINT a; diff --git a/contrib/test_decoding/sql/decoding_in_xact.sql b/contrib/test_decoding/sql/decoding_in_xact.sql index b524eb9a6e..108782dc2e 100644 --- a/contrib/test_decoding/sql/decoding_in_xact.sql +++ b/contrib/test_decoding/sql/decoding_in_xact.sql @@ -3,13 +3,13 @@ SET synchronous_commit = on; -- fail because we're creating a slot while in an xact with xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); ROLLBACK; -- fail because we're creating a slot while in a subxact whose topxact has an xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; SAVEPOINT barf; SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding'); ROLLBACK TO SAVEPOINT barf; @@ -29,7 +29,7 @@ INSERT INTO nobarf(data) VALUES('1'); -- decoding works in transaction with xid BEGIN; -SELECT txid_current() = 0; +SELECT pg_current_xact_id() = '0'; -- don't show yet, haven't committed INSERT INTO nobarf(data) VALUES('2'); SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1'); diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 89f3a7c119..c2e42f31c0 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -198,6 +198,12 @@ PostgreSQL Log Sequence Number + + pg_snapshot + + user-level transaction ID snapshot + + point @@ -279,7 +285,7 @@ txid_snapshot - user-level transaction ID snapshot + user-level transaction ID snapshot (deprecated; see pg_snapshot) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4d88b45e72..cc4a7bf8df 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18998,6 +18998,38 @@ SELECT collation for ('foo' COLLATE "de_DE"); are stored globally as well. + + pg_current_xact_id + + + + pg_current_xact_id_if_assigned + + + + pg_current_snapshot + + + + pg_snapshot_xip + + + + pg_snapshot_xmax + + + + pg_snapshot_xmin + + + + pg_visible_in_snapshot + + + + pg_xact_status + + txid_current @@ -19031,76 +19063,136 @@ 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 + + + pg_current_xact_id() + xid8 + get current transaction ID, assigning a new one if the current transaction does not have one + + + pg_current_xact_id_if_assigned() + xid8 + same as pg_current_xact_id() but returns null instead of assigning a new transaction ID if none is already assigned + + + pg_xact_status(xid8) + text + report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old + + + pg_current_snapshot() + pg_snapshot + get current snapshot + + + pg_snapshot_xip(pg_snapshot) + setof xid8 + get in-progress transaction IDs in snapshot + + + pg_snapshot_xmax(pg_snapshot) + xid8 + get xmax of snapshot + + + pg_snapshot_xmin(pg_snapshot) + xid8 + get xmin of snapshot + + + pg_visible_in_snapshot(xid8, pg_snapshot) + boolean + is transaction ID visible in snapshot? (do not use with subtransaction IDs) + + + +
+ + + The internal transaction ID type xid is 32 bits wide and + wraps around every 4 billion transactions. However, these functions use a + 64-bit variant xid8 that does not wrap around during the life + of an installation, and can be converted to xid by casting if + required. The data type pg_snapshot stores information about + transaction ID visibility at a particular moment in time. Its components + are described in . + + + + In releases of PostgreSQL before 13 there was + no xid8 type, so variants of these functions were provided + that used bigint. These older functions with + txid in their names are still supported for backward + compatibility, but may be removed from a future + release. See . + + + + Transaction IDs and Snapshots (Deprecated Functions) + + + Name Return Type Description + + txid_current() bigint - get current transaction ID, assigning a new one if the current transaction does not have one + see pg_current_xact_id() 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 pg_current_xact_id_if_assigned() txid_current_snapshot() txid_snapshot - get current snapshot + see pg_current_snapshot() txid_snapshot_xip(txid_snapshot) setof bigint - get in-progress transaction IDs in snapshot + see pg_snapshot_xip() txid_snapshot_xmax(txid_snapshot) bigint - get xmax of snapshot + see pg_snapshot_xmax() txid_snapshot_xmin(txid_snapshot) bigint - get xmin of snapshot + see pg_snapshot_xmin() txid_visible_in_snapshot(bigint, txid_snapshot) boolean - is transaction ID visible in snapshot? (do not use with subtransaction ids) + see pg_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 pg_xact_status()
- - The internal transaction ID type (xid) is 32 bits wide and - 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, - stores information about transaction ID - visibility at a particular moment in time. Its components are - described in . - - - +
Snapshot Components @@ -19115,31 +19207,29 @@ SELECT collation for ('foo' COLLATE "de_DE"); xmin - 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 xmin are either committed and visible, + or rolled back and dead. xmax - 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 xmax had not yet + completed as of the time of the snapshot, and thus are invisible. 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 < - 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. + Transactions in progress at the time of the snapshot. A transaction + ID that is xmin <= X < xmax 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. @@ -19148,14 +19238,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
- txid_snapshot's textual representation is + pg_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 + pg_xact_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. @@ -19169,7 +19259,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 transaction ID belongs to a prepared transaction. diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml index bce6d379bf..bad3bfe620 100644 --- a/doc/src/sgml/logicaldecoding.sgml +++ b/doc/src/sgml/logicaldecoding.sgml @@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true); Any actions leading to transaction ID assignment are prohibited. That, among others, includes writing to tables, performing DDL changes, and - calling txid_current(). + calling pg_current_xact_id(). diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index fd8b17ef8f..c50b72137f 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
CLogTruncationLock - Waiting to execute txid_status or update + Waiting to execute pg_xact_status or update the oldest transaction id available to it. diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 13efa9338c..5d2aca8cfe 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -101,7 +101,6 @@ OBJS = \ tsvector.o \ tsvector_op.o \ tsvector_parser.o \ - txid.o \ uuid.o \ varbit.o \ varchar.o \ @@ -109,6 +108,7 @@ OBJS = \ version.o \ windowfuncs.o \ xid.o \ + xid8funcs.o \ xml.o jsonpath_scan.c: FLEXFLAGS = -CF -p -p diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/xid8funcs.c similarity index 54% rename from src/backend/utils/adt/txid.c rename to src/backend/utils/adt/xid8funcs.c index 33272f8030..616f187ad4 100644 --- a/src/backend/utils/adt/txid.c +++ b/src/backend/utils/adt/xid8funcs.c @@ -1,20 +1,25 @@ /*------------------------------------------------------------------------- - * txid.c + * xid8funcs.c * * Export internal transaction IDs to user level. * - * Note that only top-level transaction IDs are ever converted to TXID. - * This is important because TXIDs frequently persist beyond the global + * Note that only top-level transaction IDs are exposed to user sessions. + * This is important because xid8s frequently persist beyond the global * xmin horizon, or may even be shipped to other machines, so we cannot * rely on being able to correlate subtransaction IDs with their parents * via functions such as SubTransGetTopmostTransaction(). * + * These functions are used to support the txid_XXX functions and the newer + * pg_current_xact, pg_current_snapshot and related fmgr functions, since the + * only difference between them is whether they expose xid8 or int8 values to + * users. The txid_XXX variants should eventually be dropped. + * * * Copyright (c) 2003-2020, PostgreSQL Global Development Group * Author: Jan Wieck, Afilias USA INC. * 64-bit txids: Marko Kreen, Skype Technologies * - * src/backend/utils/adt/txid.c + * src/backend/utils/adt/xid8funcs.c * *------------------------------------------------------------------------- */ @@ -34,25 +39,18 @@ #include "utils/builtins.h" #include "utils/memutils.h" #include "utils/snapmgr.h" +#include "utils/xid8.h" -/* txid will be signed int8 in database, so must limit to 63 bits */ -#define MAX_TXID ((uint64) PG_INT64_MAX) - -/* Use unsigned variant internally */ -typedef uint64 txid; - -/* sprintf format code for uint64 */ -#define TXID_FMT UINT64_FORMAT /* - * If defined, use bsearch() function for searching for txids in snapshots + * If defined, use bsearch() function for searching for xid8s in snapshots * that have more than the specified number of values. */ #define USE_BSEARCH_IF_NXIP_GREATER 30 /* - * Snapshot containing 8byte txids. + * Snapshot containing FullTransactionIds. */ typedef struct { @@ -63,39 +61,17 @@ typedef struct */ int32 __varsz; - uint32 nxip; /* number of txids in xip array */ - txid xmin; - txid xmax; - /* in-progress txids, xmin <= xip[i] < xmax: */ - txid xip[FLEXIBLE_ARRAY_MEMBER]; -} TxidSnapshot; + 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; -#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(); - - state->last_xid = XidFromFullTransactionId(fullXid); - state->epoch = EpochFromFullTransactionId(fullXid); -} +#define PG_SNAPSHOT_SIZE(nxip) \ + (offsetof(pg_snapshot, xip) + sizeof(FullTransactionId) * (nxip)) +#define PG_SNAPSHOT_MAX_NXIP \ + ((MaxAllocSize - offsetof(pg_snapshot, xip)) / sizeof(FullTransactionId)) /* * Helper to get a TransactionId from a 64-bit xid with wraparound detection. @@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state) * relating to those XIDs. */ static bool -TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid) +TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid) { - uint32 xid_epoch = (uint32) (xid_with_epoch >> 32); - TransactionId xid = (TransactionId) xid_with_epoch; + uint32 xid_epoch = EpochFromFullTransactionId(fxid); + TransactionId xid = XidFromFullTransactionId(fxid); uint32 now_epoch; TransactionId now_epoch_next_xid; FullTransactionId now_fullxid; @@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid) return true; /* If the transaction ID is in the future, throw an error. */ - if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid)) + if (!FullTransactionIdPrecedes(fxid, now_fullxid)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("transaction ID %s is in the future", - psprintf(UINT64_FORMAT, xid_with_epoch)))); + psprintf(UINT64_FORMAT, + U64FromFullTransactionId(fxid))))); /* * ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock, @@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid) } /* - * do a TransactionId -> txid conversion for an XID near the given epoch + * Convert a TransactionId obtained from a snapshot held by the caller to a + * FullTransactionId. Use next_fxid as a reference FullTransactionId, so that + * we can compute the high order bits. It must have been obtained by the + * caller with ReadNextFullTransactionId() after the snapshot was created. */ -static txid -convert_xid(TransactionId xid, const TxidEpoch *state) +static FullTransactionId +widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid) { - uint64 epoch; + TransactionId next_xid = XidFromFullTransactionId(next_fxid); + uint32 epoch = EpochFromFullTransactionId(next_fxid); - /* return special xid's as-is */ + /* Special transaction ID. */ if (!TransactionIdIsNormal(xid)) - return (txid) xid; + return FullTransactionIdFromEpochAndXid(0, xid); - /* xid can be on either side when near wrap-around */ - epoch = (uint64) state->epoch; - if (xid > state->last_xid && - TransactionIdPrecedes(xid, state->last_xid)) + /* + * The 64 bit result must be <= next_fxid, since next_fxid hadn't been + * issued yet when the snapshot was created. Every TransactionId in the + * snapshot must therefore be from the same epoch as next_fxid, or the + * epoch before. We know this because next_fxid is never allow to get + * more than one epoch ahead of the TransactionIds in any snapshot. + */ + if (xid > next_xid) epoch--; - else if (xid < state->last_xid && - TransactionIdFollows(xid, state->last_xid)) - epoch++; - return (epoch << 32) | xid; + return FullTransactionIdFromEpochAndXid(epoch, xid); } /* * txid comparator for qsort/bsearch */ static int -cmp_txid(const void *aa, const void *bb) +cmp_fxid(const void *aa, const void *bb) { - txid a = *(const txid *) aa; - txid b = *(const txid *) bb; + FullTransactionId a = *(const FullTransactionId *) aa; + FullTransactionId b = *(const FullTransactionId *) bb; - if (a < b) + if (FullTransactionIdPrecedes(a, b)) return -1; - if (a > b) + if (FullTransactionIdPrecedes(b, a)) return 1; return 0; } @@ -211,31 +193,33 @@ cmp_txid(const void *aa, const void *bb) * will not be used. */ static void -sort_snapshot(TxidSnapshot *snap) +sort_snapshot(pg_snapshot *snap) { if (snap->nxip > 1) { - qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid); - snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid); + qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid); + snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId), + cmp_fxid); } } /* - * check txid visibility. + * check fxid visibility. */ static bool -is_visible_txid(txid value, const TxidSnapshot *snap) +is_visible_fxid(FullTransactionId value, const pg_snapshot *snap) { - if (value < snap->xmin) + if (FullTransactionIdPrecedes(value, snap->xmin)) return true; - else if (value >= snap->xmax) + else if (!FullTransactionIdPrecedes(value, snap->xmax)) return false; #ifdef USE_BSEARCH_IF_NXIP_GREATER else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER) { void *res; - res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid); + res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId), + cmp_fxid); /* if found, transaction is still in progress */ return (res) ? false : true; } @@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap) for (i = 0; i < snap->nxip; i++) { - if (value == snap->xip[i]) + if (FullTransactionIdEquals(value, snap->xip[i])) return false; } return true; @@ -254,13 +238,13 @@ is_visible_txid(txid value, const TxidSnapshot *snap) } /* - * helper functions to use StringInfo for TxidSnapshot creation. + * helper functions to use StringInfo for pg_snapshot creation. */ static StringInfo -buf_init(txid xmin, txid xmax) +buf_init(FullTransactionId xmin, FullTransactionId xmax) { - TxidSnapshot snap; + pg_snapshot snap; StringInfo buf; snap.xmin = xmin; @@ -268,25 +252,25 @@ buf_init(txid xmin, txid xmax) snap.nxip = 0; buf = makeStringInfo(); - appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0)); + appendBinaryStringInfo(buf, (char *) &snap, PG_SNAPSHOT_SIZE(0)); return buf; } static void -buf_add_txid(StringInfo buf, txid xid) +buf_add_txid(StringInfo buf, FullTransactionId fxid) { - TxidSnapshot *snap = (TxidSnapshot *) buf->data; + pg_snapshot *snap = (pg_snapshot *) buf->data; /* do this before possible realloc */ snap->nxip++; - appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid)); + appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid)); } -static TxidSnapshot * +static pg_snapshot * buf_finalize(StringInfo buf) { - TxidSnapshot *snap = (TxidSnapshot *) buf->data; + pg_snapshot *snap = (pg_snapshot *) buf->data; SET_VARSIZE(snap, buf->len); @@ -297,68 +281,34 @@ buf_finalize(StringInfo buf) return snap; } -/* - * simple number parser. - * - * We return 0 on error, which is invalid value for txid. - */ -static txid -str2txid(const char *s, const char **endp) -{ - txid val = 0; - txid cutoff = MAX_TXID / 10; - txid cutlim = MAX_TXID % 10; - - for (; *s; s++) - { - unsigned d; - - if (*s < '0' || *s > '9') - break; - d = *s - '0'; - - /* - * check for overflow - */ - if (val > cutoff || (val == cutoff && d > cutlim)) - { - val = 0; - break; - } - - val = val * 10 + d; - } - if (endp) - *endp = s; - return val; -} - /* * parse snapshot from cstring */ -static TxidSnapshot * +static pg_snapshot * parse_snapshot(const char *str) { - txid xmin; - txid xmax; - txid last_val = 0, - val; + FullTransactionId xmin; + FullTransactionId xmax; + FullTransactionId last_val = InvalidFullTransactionId; + FullTransactionId val; const char *str_start = str; - const char *endp; + char *endp; StringInfo buf; - xmin = str2txid(str, &endp); + xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); if (*endp != ':') goto bad_format; str = endp + 1; - xmax = str2txid(str, &endp); + xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); if (*endp != ':') goto bad_format; str = endp + 1; /* it should look sane */ - if (xmin == 0 || xmax == 0 || xmin > xmax) + if (!FullTransactionIdIsValid(xmin) || + !FullTransactionIdIsValid(xmax) || + FullTransactionIdPrecedes(xmax, xmin)) goto bad_format; /* allocate buffer */ @@ -368,15 +318,17 @@ parse_snapshot(const char *str) while (*str != '\0') { /* read next value */ - val = str2txid(str, &endp); + val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10)); str = endp; /* require the input to be in order */ - if (val < xmin || val >= xmax || val < last_val) + if (FullTransactionIdPrecedes(val, xmin) || + FullTransactionIdFollowsOrEquals(val, xmax) || + FullTransactionIdPrecedes(val, last_val)) goto bad_format; /* skip duplicates */ - if (val != last_val) + if (!FullTransactionIdEquals(val, last_val)) buf_add_txid(buf, val); last_val = val; @@ -392,108 +344,82 @@ bad_format: ereport(ERROR, (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), errmsg("invalid input syntax for type %s: \"%s\"", - "txid_snapshot", str_start))); + "pg_snapshot", str_start))); return NULL; /* keep compiler quiet */ } /* - * Public functions. + * pg_current_xact_id() returns xid8 * - * 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 - * - * 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()"); + PreventCommandDuringRecovery("pg_current_xact_id()"); - load_xid_epoch(&state); - - val = convert_xid(GetTopTransactionId(), &state); - - PG_RETURN_INT64(val); + PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId()); } /* - * Same as txid_current() but doesn't assign a new xid if there isn't one - * yet. + * Same as pg_current_xact_if_assigned() but doesn't assign a new xid if there + * isn't one yet. */ Datum -txid_current_if_assigned(PG_FUNCTION_ARGS) +pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS) { - txid val; - TxidEpoch state; - TransactionId topxid = GetTopTransactionIdIfAny(); + FullTransactionId topfxid = GetTopFullTransactionIdIfAny(); - if (topxid == InvalidTransactionId) + if (!FullTransactionIdIsValid(topfxid)) PG_RETURN_NULL(); - load_xid_epoch(&state); - - val = convert_xid(topxid, &state); - - PG_RETURN_INT64(val); + PG_RETURN_FULLTRANSACTIONID(topfxid); } /* - * txid_current_snapshot() returns txid_snapshot + * pg_current_snapshot() returns pg_snapshot * - * Return current snapshot in TXID format + * Return current snapshot * * Note that only top-transaction XIDs are included in the snapshot. */ Datum -txid_current_snapshot(PG_FUNCTION_ARGS) +pg_current_snapshot(PG_FUNCTION_ARGS) { - TxidSnapshot *snap; + pg_snapshot *snap; uint32 nxip, i; - TxidEpoch state; Snapshot cur; + FullTransactionId next_fxid = ReadNextFullTransactionId(); cur = GetActiveSnapshot(); if (cur == NULL) elog(ERROR, "no active snapshot set"); - load_xid_epoch(&state); - /* * Compile-time limits on the procarray (MAX_BACKENDS processes plus * MAX_BACKENDS prepared transactions) guarantee nxip won't be too large. */ - StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP, - "possible overflow in txid_current_snapshot()"); + StaticAssertStmt(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP, + "possible overflow in pg_current_snapshot()"); /* allocate */ nxip = cur->xcnt; - snap = palloc(TXID_SNAPSHOT_SIZE(nxip)); + snap = palloc(PG_SNAPSHOT_SIZE(nxip)); /* fill */ - snap->xmin = convert_xid(cur->xmin, &state); - snap->xmax = convert_xid(cur->xmax, &state); + snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid); + snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid); snap->nxip = nxip; for (i = 0; i < nxip; i++) - snap->xip[i] = convert_xid(cur->xip[i], &state); + snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid); /* * We want them guaranteed to be in ascending order. This also removes @@ -505,21 +431,21 @@ txid_current_snapshot(PG_FUNCTION_ARGS) sort_snapshot(snap); /* set size after sorting, because it may have removed duplicate xips */ - SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip)); + SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip)); PG_RETURN_POINTER(snap); } /* - * txid_snapshot_in(cstring) returns txid_snapshot + * pg_snapshot_in(cstring) returns pg_snapshot * - * input function for type txid_snapshot + * input function for type pg_snapshot */ Datum -txid_snapshot_in(PG_FUNCTION_ARGS) +pg_snapshot_in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); - TxidSnapshot *snap; + pg_snapshot *snap; snap = parse_snapshot(str); @@ -527,73 +453,81 @@ txid_snapshot_in(PG_FUNCTION_ARGS) } /* - * txid_snapshot_out(txid_snapshot) returns cstring + * pg_snapshot_out(pg_snapshot) returns cstring * - * output function for type txid_snapshot + * output function for type pg_snapshot */ Datum -txid_snapshot_out(PG_FUNCTION_ARGS) +pg_snapshot_out(PG_FUNCTION_ARGS) { - TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0); StringInfoData str; uint32 i; initStringInfo(&str); - appendStringInfo(&str, TXID_FMT ":", snap->xmin); - appendStringInfo(&str, TXID_FMT ":", snap->xmax); + appendStringInfo(&str, UINT64_FORMAT ":", + U64FromFullTransactionId(snap->xmin)); + appendStringInfo(&str, UINT64_FORMAT ":", + U64FromFullTransactionId(snap->xmax)); for (i = 0; i < snap->nxip; i++) { if (i > 0) appendStringInfoChar(&str, ','); - appendStringInfo(&str, TXID_FMT, snap->xip[i]); + appendStringInfo(&str, UINT64_FORMAT, + U64FromFullTransactionId(snap->xip[i])); } PG_RETURN_CSTRING(str.data); } /* - * txid_snapshot_recv(internal) returns txid_snapshot + * pg_snapshot_recv(internal) returns pg_snapshot * - * binary input function for type txid_snapshot + * binary input function for type pg_snapshot * * format: int4 nxip, int8 xmin, int8 xmax, int8 xip */ Datum -txid_snapshot_recv(PG_FUNCTION_ARGS) +pg_snapshot_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); - TxidSnapshot *snap; - txid last = 0; + pg_snapshot *snap; + FullTransactionId last = InvalidFullTransactionId; int nxip; int i; - txid xmin, - xmax; + FullTransactionId xmin; + FullTransactionId xmax; /* load and validate nxip */ nxip = pq_getmsgint(buf, 4); - if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP) + if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP) goto bad_format; - xmin = pq_getmsgint64(buf); - xmax = pq_getmsgint64(buf); - if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID) + xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf)); + xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf)); + if (!FullTransactionIdIsValid(xmin) || + !FullTransactionIdIsValid(xmax) || + FullTransactionIdPrecedes(xmax, xmin)) goto bad_format; - snap = palloc(TXID_SNAPSHOT_SIZE(nxip)); + snap = palloc(PG_SNAPSHOT_SIZE(nxip)); snap->xmin = xmin; snap->xmax = xmax; for (i = 0; i < nxip; i++) { - txid cur = pq_getmsgint64(buf); + FullTransactionId cur = + FullTransactionIdFromU64((uint64) pq_getmsgint64(buf)); - if (cur < last || cur < xmin || cur >= xmax) + if (FullTransactionIdPrecedes(cur, last) || + FullTransactionIdPrecedes(cur, xmin) || + FullTransactionIdPrecedes(xmax, cur)) goto bad_format; /* skip duplicate xips */ - if (cur == last) + if (FullTransactionIdEquals(cur, last)) { i--; nxip--; @@ -604,95 +538,95 @@ txid_snapshot_recv(PG_FUNCTION_ARGS) last = cur; } snap->nxip = nxip; - SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip)); + SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip)); PG_RETURN_POINTER(snap); bad_format: ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), - errmsg("invalid external txid_snapshot data"))); + errmsg("invalid external pg_snapshot data"))); PG_RETURN_POINTER(NULL); /* keep compiler quiet */ } /* - * txid_snapshot_send(txid_snapshot) returns bytea + * pg_snapshot_send(pg_snapshot) returns bytea * - * binary output function for type txid_snapshot + * binary output function for type pg_snapshot * - * format: int4 nxip, int8 xmin, int8 xmax, int8 xip + * format: int4 nxip, u64 xmin, u64 xmax, u64 xip... */ Datum -txid_snapshot_send(PG_FUNCTION_ARGS) +pg_snapshot_send(PG_FUNCTION_ARGS) { - TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0); StringInfoData buf; uint32 i; pq_begintypsend(&buf); pq_sendint32(&buf, snap->nxip); - pq_sendint64(&buf, snap->xmin); - pq_sendint64(&buf, snap->xmax); + pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin)); + pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax)); for (i = 0; i < snap->nxip; i++) - pq_sendint64(&buf, snap->xip[i]); + pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i])); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } /* - * txid_visible_in_snapshot(int8, txid_snapshot) returns bool + * pg_visible_in_snapshot(xid8, pg_snapshot) returns bool * * is txid visible in snapshot ? */ Datum -txid_visible_in_snapshot(PG_FUNCTION_ARGS) +pg_visible_in_snapshot(PG_FUNCTION_ARGS) { - txid value = PG_GETARG_INT64(0); - TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1); + FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0); + pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1); - PG_RETURN_BOOL(is_visible_txid(value, snap)); + PG_RETURN_BOOL(is_visible_fxid(value, snap)); } /* - * txid_snapshot_xmin(txid_snapshot) returns int8 + * pg_snapshot_xmin(pg_snapshot) returns xid8 * * return snapshot's xmin */ Datum -txid_snapshot_xmin(PG_FUNCTION_ARGS) +pg_snapshot_xmin(PG_FUNCTION_ARGS) { - TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0); - PG_RETURN_INT64(snap->xmin); + PG_RETURN_FULLTRANSACTIONID(snap->xmin); } /* - * txid_snapshot_xmax(txid_snapshot) returns int8 + * pg_snapshot_xmax(pg_snapshot) returns xid8 * * return snapshot's xmax */ Datum -txid_snapshot_xmax(PG_FUNCTION_ARGS) +pg_snapshot_xmax(PG_FUNCTION_ARGS) { - TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0); - PG_RETURN_INT64(snap->xmax); + PG_RETURN_FULLTRANSACTIONID(snap->xmax); } /* - * txid_snapshot_xip(txid_snapshot) returns setof int8 + * pg_snapshot_xip(pg_snapshot) returns setof xid8 * - * return in-progress TXIDs in snapshot. + * return in-progress xid8s in snapshot. */ Datum -txid_snapshot_xip(PG_FUNCTION_ARGS) +pg_snapshot_xip(PG_FUNCTION_ARGS) { FuncCallContext *fctx; - TxidSnapshot *snap; - txid value; + pg_snapshot *snap; + FullTransactionId value; /* on first call initialize fctx and get copy of snapshot */ if (SRF_IS_FIRSTCALL()) { - TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0); fctx = SRF_FIRSTCALL_INIT(); @@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS) if (fctx->call_cntr < snap->nxip) { value = snap->xip[fctx->call_cntr]; - SRF_RETURN_NEXT(fctx, Int64GetDatum(value)); + SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value)); } else { @@ -728,10 +662,10 @@ txid_snapshot_xip(PG_FUNCTION_ARGS) * though the parent xact may still be in progress or may have aborted. */ Datum -txid_status(PG_FUNCTION_ARGS) +pg_xact_status(PG_FUNCTION_ARGS) { const char *status; - uint64 xid_with_epoch = PG_GETARG_INT64(0); + FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0); TransactionId xid; /* @@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS) * an I/O error on SLRU lookup. */ LWLockAcquire(CLogTruncationLock, LW_SHARED); - if (TransactionIdInRecentPast(xid_with_epoch, &xid)) + if (TransactionIdInRecentPast(fxid, &xid)) { Assert(TransactionIdIsValid(xid)); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 4201790ec4..498992ff84 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202004061 +#define CATALOG_VERSION_NO 202004062 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6be7e4f157..2d1862a9d8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -9505,46 +9505,89 @@ proname => 'jsonb_path_match_opr', prorettype => 'bool', proargtypes => 'jsonb jsonpath', prosrc => 'jsonb_path_match_opr' }, -# txid +# historical int8/txid_snapshot variants of xid8 functions { oid => '2939', descr => 'I/O', proname => 'txid_snapshot_in', prorettype => 'txid_snapshot', - proargtypes => 'cstring', prosrc => 'txid_snapshot_in' }, + proargtypes => 'cstring', prosrc => 'pg_snapshot_in' }, { oid => '2940', descr => 'I/O', proname => 'txid_snapshot_out', prorettype => 'cstring', - proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_out' }, + proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_out' }, { oid => '2941', descr => 'I/O', proname => 'txid_snapshot_recv', prorettype => 'txid_snapshot', - proargtypes => 'internal', prosrc => 'txid_snapshot_recv' }, + proargtypes => 'internal', prosrc => 'pg_snapshot_recv' }, { oid => '2942', descr => 'I/O', proname => 'txid_snapshot_send', prorettype => 'bytea', - proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_send' }, + proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_send' }, { oid => '2943', descr => 'get current transaction ID', proname => 'txid_current', provolatile => 's', proparallel => 'u', - prorettype => 'int8', proargtypes => '', prosrc => 'txid_current' }, + prorettype => 'int8', proargtypes => '', prosrc => 'pg_current_xact_id' }, { oid => '3348', descr => 'get current transaction ID', proname => 'txid_current_if_assigned', provolatile => 's', proparallel => 'u', prorettype => 'int8', proargtypes => '', - prosrc => 'txid_current_if_assigned' }, + prosrc => 'pg_current_xact_id_if_assigned' }, { oid => '2944', descr => 'get current snapshot', proname => 'txid_current_snapshot', provolatile => 's', prorettype => 'txid_snapshot', proargtypes => '', - prosrc => 'txid_current_snapshot' }, + prosrc => 'pg_current_snapshot' }, { oid => '2945', descr => 'get xmin of snapshot', proname => 'txid_snapshot_xmin', prorettype => 'int8', - proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmin' }, + proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmin' }, { oid => '2946', descr => 'get xmax of snapshot', proname => 'txid_snapshot_xmax', prorettype => 'int8', - proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmax' }, + proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmax' }, { oid => '2947', descr => 'get set of in-progress txids in snapshot', proname => 'txid_snapshot_xip', prorows => '50', proretset => 't', prorettype => 'int8', proargtypes => 'txid_snapshot', - prosrc => 'txid_snapshot_xip' }, + prosrc => 'pg_snapshot_xip' }, { oid => '2948', descr => 'is txid visible in snapshot?', proname => 'txid_visible_in_snapshot', prorettype => 'bool', - proargtypes => 'int8 txid_snapshot', prosrc => 'txid_visible_in_snapshot' }, + proargtypes => 'int8 txid_snapshot', prosrc => 'pg_visible_in_snapshot' }, { oid => '3360', descr => 'commit status of transaction', proname => 'txid_status', provolatile => 'v', prorettype => 'text', - proargtypes => 'int8', prosrc => 'txid_status' }, + proargtypes => 'int8', prosrc => 'pg_xact_status' }, + +# pg_snapshot functions +{ oid => '9247', descr => 'I/O', + proname => 'pg_snapshot_in', prorettype => 'pg_snapshot', + proargtypes => 'cstring', prosrc => 'pg_snapshot_in' }, +{ oid => '9248', descr => 'I/O', + proname => 'pg_snapshot_out', prorettype => 'cstring', + proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_out' }, +{ oid => '9249', descr => 'I/O', + proname => 'pg_snapshot_recv', prorettype => 'pg_snapshot', + proargtypes => 'internal', prosrc => 'pg_snapshot_recv' }, +{ oid => '9250', descr => 'I/O', + proname => 'pg_snapshot_send', prorettype => 'bytea', + proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_send' }, +{ oid => '9253', descr => 'get current snapshot', + proname => 'pg_current_snapshot', provolatile => 's', + prorettype => 'pg_snapshot', proargtypes => '', + prosrc => 'pg_current_snapshot' }, +{ oid => '9254', descr => 'get xmin of snapshot', + proname => 'pg_snapshot_xmin', prorettype => 'xid8', + proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmin' }, +{ oid => '9255', descr => 'get xmax of snapshot', + proname => 'pg_snapshot_xmax', prorettype => 'xid8', + proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmax' }, +{ oid => '9256', descr => 'get set of in-progress transactions in snapshot', + proname => 'pg_snapshot_xip', prorows => '50', proretset => 't', + prorettype => 'xid8', proargtypes => 'pg_snapshot', + prosrc => 'pg_snapshot_xip' }, +{ oid => '9257', descr => 'is xid8 visible in snapshot?', + proname => 'pg_visible_in_snapshot', prorettype => 'bool', + proargtypes => 'xid8 pg_snapshot', prosrc => 'pg_visible_in_snapshot' }, + +# transaction ID and status functions +{ oid => '9251', descr => 'get current transaction ID', + proname => 'pg_current_xact_id', provolatile => 's', proparallel => 'u', + prorettype => 'xid8', proargtypes => '', prosrc => 'pg_current_xact_id' }, +{ oid => '9252', descr => 'get current transaction ID', + proname => 'pg_current_xact_id_if_assigned', provolatile => 's', proparallel => 'u', + prorettype => 'xid8', proargtypes => '', + prosrc => 'pg_current_xact_id_if_assigned' }, +{ oid => '9258', descr => 'commit status of transaction', + proname => 'pg_xact_status', provolatile => 'v', prorettype => 'text', + proargtypes => 'xid8', prosrc => 'pg_xact_status' }, # record comparison using normal comparison rules { oid => '2981', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a1f441b8da..78f44af5e7 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -460,6 +460,11 @@ typcategory => 'U', typinput => 'txid_snapshot_in', typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv', typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' }, +{ oid => '8355', array_type_oid => '8356', descr => 'snapshot', + typname => 'pg_snapshot', typlen => '-1', typbyval => 'f', + typcategory => 'U', typinput => 'pg_snapshot_in', + typoutput => 'pg_snapshot_out', typreceive => 'pg_snapshot_recv', + typsend => 'pg_snapshot_send', typalign => 'd', typstorage => 'x' }, # range types { oid => '3904', array_type_oid => '3905', descr => 'range of integers', diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl index bd4b943305..39ca25a06b 100644 --- a/src/test/modules/commit_ts/t/004_restart.pl +++ b/src/test/modules/commit_ts/t/004_restart.pl @@ -45,7 +45,7 @@ my $xid = $node_master->safe_psql( 'postgres', qq[ BEGIN; INSERT INTO committs_test(x, y) VALUES (1, current_timestamp); - SELECT txid_current(); + SELECT pg_current_xact_id()::xid; COMMIT; ]); @@ -93,7 +93,7 @@ DECLARE i int; BEGIN FOR i in 1..cnt LOOP - EXECUTE 'SELECT txid_current()'; + EXECUTE 'SELECT pg_current_xact_id()'; COMMIT; END LOOP; END; @@ -115,7 +115,7 @@ my $xid_disabled = $node_master->safe_psql( 'postgres', qq[ BEGIN; INSERT INTO committs_test(x, y) VALUES (2, current_timestamp); - SELECT txid_current(); + SELECT pg_current_xact_id(); COMMIT; ]); diff --git a/src/test/modules/test_ddl_deparse/expected/create_table.out b/src/test/modules/test_ddl_deparse/expected/create_table.out index 523c996093..c7c9bf8971 100644 --- a/src/test/modules/test_ddl_deparse/expected/create_table.out +++ b/src/test/modules/test_ddl_deparse/expected/create_table.out @@ -43,7 +43,7 @@ CREATE TABLE datatype_table ( v_json JSON, v_xml XML, v_uuid UUID, - v_txid_snapshot txid_snapshot, + v_pg_snapshot pg_snapshot, v_enum ENUM_TEST, v_postal_code japanese_postal_code, v_int2range int2range, diff --git a/src/test/modules/test_ddl_deparse/sql/create_table.sql b/src/test/modules/test_ddl_deparse/sql/create_table.sql index dd3a908638..39cdb9df03 100644 --- a/src/test/modules/test_ddl_deparse/sql/create_table.sql +++ b/src/test/modules/test_ddl_deparse/sql/create_table.sql @@ -44,7 +44,7 @@ CREATE TABLE datatype_table ( v_json JSON, v_xml XML, v_uuid UUID, - v_txid_snapshot txid_snapshot, + v_pg_snapshot pg_snapshot, v_enum ENUM_TEST, v_postal_code japanese_postal_code, v_int2range int2range, diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl index fd14bab208..2a520e6db1 100644 --- a/src/test/recovery/t/003_recovery_targets.pl +++ b/src/test/recovery/t/003_recovery_targets.pl @@ -68,7 +68,7 @@ $node_master->backup('my_backup'); $node_master->safe_psql('postgres', "INSERT INTO tab_int VALUES (generate_series(1001,2000))"); my $ret = $node_master->safe_psql('postgres', - "SELECT pg_current_wal_lsn(), txid_current();"); + "SELECT pg_current_wal_lsn(), pg_current_xact_id();"); my ($lsn2, $recovery_txid) = split /\|/, $ret; # More data, with recovery target timestamp diff --git a/src/test/recovery/t/011_crash_recovery.pl b/src/test/recovery/t/011_crash_recovery.pl index 526a3481fb..ca6e92b50d 100644 --- a/src/test/recovery/t/011_crash_recovery.pl +++ b/src/test/recovery/t/011_crash_recovery.pl @@ -24,7 +24,7 @@ $node->start; my ($stdin, $stdout, $stderr) = ('', '', ''); -# Ensure that txid_status reports 'aborted' for xacts +# Ensure that pg_xact_status reports 'aborted' for xacts # that were in-progress during crash. To do that, we need # an xact to be in-progress when we crash and we need to know # its xid. @@ -42,7 +42,7 @@ my $tx = IPC::Run::start( $stdin .= q[ BEGIN; CREATE TABLE mine(x integer); -SELECT txid_current(); +SELECT pg_current_xact_id(); ]; $tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/; @@ -50,7 +50,7 @@ $tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/; my $xid = $stdout; chomp($xid); -is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]), +is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]), 'in progress', 'own xid is in-progress'); # Crash and restart the postmaster @@ -58,11 +58,11 @@ $node->stop('immediate'); $node->start; # Make sure we really got a new xid -cmp_ok($node->safe_psql('postgres', 'SELECT txid_current()'), +cmp_ok($node->safe_psql('postgres', 'SELECT pg_current_xact_id()'), '>', $xid, 'new xid after restart is greater'); # and make sure we show the in-progress xact as aborted -is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]), +is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]), 'aborted', 'xid is aborted after crash'); $tx->kill_kill; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index e46775afbf..f343f9b42e 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -2556,7 +2556,7 @@ from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks - where transactionid = 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' @@ -2719,7 +2719,7 @@ from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks - where transactionid = txid_current()::integer) + where transactionid = pg_current_xact_id()::xid) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname = 'my_locks' diff --git a/src/test/regress/expected/hs_standby_functions.out b/src/test/regress/expected/hs_standby_functions.out index e0af677ea1..ce846b758b 100644 --- a/src/test/regress/expected/hs_standby_functions.out +++ b/src/test/regress/expected/hs_standby_functions.out @@ -4,9 +4,9 @@ -- hs_standby_functions.sql -- -- should fail -select txid_current(); -ERROR: cannot execute txid_current() during recovery -select length(txid_current_snapshot()::text) >= 4; +select pg_current_xact_id(); +ERROR: cannot execute pg_current_xact_id() during recovery +select length(pg_current_snapshot()::text) >= 4; ?column? ---------- t diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 0c03afe53d..8d350ab61b 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND ORDER BY 1, 2; prorettype | prorettype ------------+------------ + 20 | 9419 25 | 1043 1114 | 1184 -(2 rows) + 2970 | 8355 +(4 rows) SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0] FROM pg_proc AS p1, pg_proc AS p2 @@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND ORDER BY 1, 2; proargtypes | proargtypes -------------+------------- + 20 | 9419 25 | 1042 25 | 1043 1114 | 1184 1560 | 1562 -(4 rows) + 2970 | 8355 +(6 rows) SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1] FROM pg_proc AS p1, pg_proc AS p2 @@ -231,7 +235,8 @@ ORDER BY 1, 2; 23 | 28 1114 | 1184 1560 | 1562 -(3 rows) + 2970 | 8355 +(4 rows) SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2] FROM pg_proc AS p1, pg_proc AS p2 diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out index 015dae3051..95ba66e95e 100644 --- a/src/test/regress/expected/txid.out +++ b/src/test/regress/expected/txid.out @@ -1,4 +1,7 @@ -- txid_snapshot data type and related functions +-- Note: these are backward-compatibility functions and types, and have been +-- replaced by new xid8-based variants. See xid.sql. The txid variants will +-- be removed in a future release. -- i/o select '12:13:'::txid_snapshot; txid_snapshot @@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot; -- errors select '31:12:'::txid_snapshot; -ERROR: invalid input syntax for type txid_snapshot: "31:12:" +ERROR: invalid input syntax for type pg_snapshot: "31:12:" LINE 1: select '31:12:'::txid_snapshot; ^ select '0:1:'::txid_snapshot; -ERROR: invalid input syntax for type txid_snapshot: "0:1:" +ERROR: invalid input syntax for type pg_snapshot: "0:1:" LINE 1: select '0:1:'::txid_snapshot; ^ select '12:13:0'::txid_snapshot; -ERROR: invalid input syntax for type txid_snapshot: "12:13:0" +ERROR: invalid input syntax for type pg_snapshot: "12:13:0" LINE 1: select '12:13:0'::txid_snapshot; ^ select '12:16:14,13'::txid_snapshot; -ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13" +ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13" LINE 1: select '12:16:14,13'::txid_snapshot; ^ create temp table snapshot_test ( @@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3'; (1 row) SELECT txid_snapshot '1:9223372036854775808:3'; -ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3" +ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3" LINE 1: SELECT txid_snapshot '1:9223372036854775808:3'; ^ -- test txid_current_if_assigned diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index b7f90de52b..bf939d79f6 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -230,10 +230,9 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a) -- ON CONFLICT using system attributes in RETURNING, testing both the -- inserting and updating paths. See bug report at: -- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au -CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$; INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct; + RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct; tableoid | xmin_correct | xmax_correct -------------+--------------+-------------- upsert_test | t | t @@ -243,13 +242,12 @@ INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) -- but it seems worthwhile to have to be explicit if that changes. INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct; + RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct; tableoid | xmin_correct | xmax_correct -------------+--------------+-------------- upsert_test | t | t (1 row) -DROP FUNCTION xid_current(); DROP TABLE update_test; DROP TABLE upsert_test; --------------------------- diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out index e9e1fa8259..b7a1ed0f9e 100644 --- a/src/test/regress/expected/xid.out +++ b/src/test/regress/expected/xid.out @@ -134,3 +134,329 @@ create table xid8_t1 (x xid8); create index on xid8_t1 using btree(x); create index on xid8_t1 using hash(x); drop table xid8_t1; +-- pg_snapshot data type and related functions +-- Note: another set of tests similar to this exists in txid.sql, for a limited +-- time (the relevant functions share C code) +-- i/o +select '12:13:'::pg_snapshot; + pg_snapshot +------------- + 12:13: +(1 row) + +select '12:18:14,16'::pg_snapshot; + pg_snapshot +------------- + 12:18:14,16 +(1 row) + +select '12:16:14,14'::pg_snapshot; + pg_snapshot +------------- + 12:16:14 +(1 row) + +-- errors +select '31:12:'::pg_snapshot; +ERROR: invalid input syntax for type pg_snapshot: "31:12:" +LINE 1: select '31:12:'::pg_snapshot; + ^ +select '0:1:'::pg_snapshot; +ERROR: invalid input syntax for type pg_snapshot: "0:1:" +LINE 1: select '0:1:'::pg_snapshot; + ^ +select '12:13:0'::pg_snapshot; +ERROR: invalid input syntax for type pg_snapshot: "12:13:0" +LINE 1: select '12:13:0'::pg_snapshot; + ^ +select '12:16:14,13'::pg_snapshot; +ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13" +LINE 1: select '12:16:14,13'::pg_snapshot; + ^ +create temp table snapshot_test ( + nr integer, + snap pg_snapshot +); +insert into snapshot_test values (1, '12:13:'); +insert into snapshot_test values (2, '12:20:13,15,18'); +insert into snapshot_test values (3, '100001:100009:100005,100007,100008'); +insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131'); +select snap from snapshot_test order by nr; + snap +------------------------------------------------------------------------------------------------------------------------------------- + 12:13: + 12:20:13,15,18 + 100001:100009:100005,100007,100008 + 100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131 +(4 rows) + +select pg_snapshot_xmin(snap), + pg_snapshot_xmax(snap), + pg_snapshot_xip(snap) +from snapshot_test order by nr; + pg_snapshot_xmin | pg_snapshot_xmax | pg_snapshot_xip +------------------+------------------+----------------- + 12 | 20 | 13 + 12 | 20 | 15 + 12 | 20 | 18 + 100001 | 100009 | 100005 + 100001 | 100009 | 100007 + 100001 | 100009 | 100008 + 100 | 150 | 101 + 100 | 150 | 102 + 100 | 150 | 103 + 100 | 150 | 104 + 100 | 150 | 105 + 100 | 150 | 106 + 100 | 150 | 107 + 100 | 150 | 108 + 100 | 150 | 109 + 100 | 150 | 110 + 100 | 150 | 111 + 100 | 150 | 112 + 100 | 150 | 113 + 100 | 150 | 114 + 100 | 150 | 115 + 100 | 150 | 116 + 100 | 150 | 117 + 100 | 150 | 118 + 100 | 150 | 119 + 100 | 150 | 120 + 100 | 150 | 121 + 100 | 150 | 122 + 100 | 150 | 123 + 100 | 150 | 124 + 100 | 150 | 125 + 100 | 150 | 126 + 100 | 150 | 127 + 100 | 150 | 128 + 100 | 150 | 129 + 100 | 150 | 130 + 100 | 150 | 131 +(37 rows) + +select id, pg_visible_in_snapshot(id::text::xid8, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + id | pg_visible_in_snapshot +----+------------------------ + 11 | t + 12 | t + 13 | f + 14 | t + 15 | f + 16 | t + 17 | t + 18 | f + 19 | t + 20 | f + 21 | f +(11 rows) + +-- test bsearch +select id, pg_visible_in_snapshot(id::text::xid8, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + id | pg_visible_in_snapshot +-----+------------------------ + 90 | t + 91 | t + 92 | t + 93 | t + 94 | t + 95 | t + 96 | t + 97 | t + 98 | t + 99 | t + 100 | t + 101 | f + 102 | f + 103 | f + 104 | f + 105 | f + 106 | f + 107 | f + 108 | f + 109 | f + 110 | f + 111 | f + 112 | f + 113 | f + 114 | f + 115 | f + 116 | f + 117 | f + 118 | f + 119 | f + 120 | f + 121 | f + 122 | f + 123 | f + 124 | f + 125 | f + 126 | f + 127 | f + 128 | f + 129 | f + 130 | f + 131 | f + 132 | t + 133 | t + 134 | t + 135 | t + 136 | t + 137 | t + 138 | t + 139 | t + 140 | t + 141 | t + 142 | t + 143 | t + 144 | t + 145 | t + 146 | t + 147 | t + 148 | t + 149 | t + 150 | f + 151 | f + 152 | f + 153 | f + 154 | f + 155 | f + 156 | f + 157 | f + 158 | f + 159 | f + 160 | f +(71 rows) + +-- test current values also +select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot()); + ?column? +---------- + t +(1 row) + +-- we can't assume current is always less than xmax, however +select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot()); + pg_visible_in_snapshot +------------------------ + f +(1 row) + +-- test 64bitness +select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; + pg_snapshot +--------------------------------------------------------------------- + 1000100010001000:1000100010001100:1000100010001012,1000100010001013 +(1 row) + +select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + pg_visible_in_snapshot +------------------------ + f +(1 row) + +select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + pg_visible_in_snapshot +------------------------ + t +(1 row) + +-- test 64bit overflow +SELECT pg_snapshot '1:9223372036854775807:3'; + pg_snapshot +------------------------- + 1:9223372036854775807:3 +(1 row) + +SELECT pg_snapshot '1:9223372036854775808:3'; +ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3" +LINE 1: SELECT pg_snapshot '1:9223372036854775808:3'; + ^ +-- test pg_current_xact_id_if_assigned +BEGIN; +SELECT pg_current_xact_id_if_assigned() IS NULL; + ?column? +---------- + t +(1 row) + +SELECT pg_current_xact_id() \gset +SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id'; + ?column? +---------- + t +(1 row) + +COMMIT; +-- test xid status functions +BEGIN; +SELECT pg_current_xact_id() AS committed \gset +COMMIT; +BEGIN; +SELECT pg_current_xact_id() AS rolledback \gset +ROLLBACK; +BEGIN; +SELECT pg_current_xact_id() AS inprogress \gset +SELECT pg_xact_status(:committed::text::xid8) AS committed; + committed +----------- + committed +(1 row) + +SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback; + rolledback +------------ + aborted +(1 row) + +SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress; + inprogress +------------- + in progress +(1 row) + +SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed + pg_xact_status +---------------- + committed +(1 row) + +SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed + pg_xact_status +---------------- + committed +(1 row) + +SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin + pg_xact_status +---------------- + +(1 row) + +COMMIT; +BEGIN; +CREATE FUNCTION test_future_xid_status(xid8) +RETURNS void +LANGUAGE plpgsql +AS +$$ +BEGIN + PERFORM pg_xact_status($1); + RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected'; +EXCEPTION + WHEN invalid_parameter_value THEN + RAISE NOTICE 'Got expected error for xid in the future'; +END; +$$; +SELECT test_future_xid_status((:inprogress + 10000)::text::xid8); +NOTICE: Got expected error for xid in the future + test_future_xid_status +------------------------ + +(1 row) + +ROLLBACK; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 8eca21823d..fb5c9139d1 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1645,7 +1645,7 @@ from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks - where transactionid = 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' @@ -1732,7 +1732,7 @@ from pg_locks l join pg_class c on l.relation = c.oid where virtualtransaction = ( select virtualtransaction from pg_locks - where transactionid = txid_current()::integer) + where transactionid = pg_current_xact_id()::xid) and locktype = 'relation' and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog') and c.relname = 'my_locks' diff --git a/src/test/regress/sql/hs_standby_functions.sql b/src/test/regress/sql/hs_standby_functions.sql index 251bac0a43..b57f67ff8b 100644 --- a/src/test/regress/sql/hs_standby_functions.sql +++ b/src/test/regress/sql/hs_standby_functions.sql @@ -5,9 +5,9 @@ -- -- should fail -select txid_current(); +select pg_current_xact_id(); -select length(txid_current_snapshot()::text) >= 4; +select length(pg_current_snapshot()::text) >= 4; select pg_start_backup('should fail'); select pg_switch_wal(); diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql index bd6decf0ef..8d5ac98a89 100644 --- a/src/test/regress/sql/txid.sql +++ b/src/test/regress/sql/txid.sql @@ -1,4 +1,7 @@ -- txid_snapshot data type and related functions +-- Note: these are backward-compatibility functions and types, and have been +-- replaced by new xid8-based variants. See xid.sql. The txid variants will +-- be removed in a future release. -- i/o select '12:13:'::txid_snapshot; diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql index bb9c24e40f..8c558a7bc7 100644 --- a/src/test/regress/sql/update.sql +++ b/src/test/regress/sql/update.sql @@ -117,18 +117,16 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a) -- ON CONFLICT using system attributes in RETURNING, testing both the -- inserting and updating paths. See bug report at: -- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au -CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$; INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct; + RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct; -- currently xmax is set after a conflict - that's probably not good, -- but it seems worthwhile to have to be explicit if that changes. INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a) DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a) - RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct; + RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct; -DROP FUNCTION xid_current(); DROP TABLE update_test; DROP TABLE upsert_test; diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql index a4fbca5176..565714d462 100644 --- a/src/test/regress/sql/xid.sql +++ b/src/test/regress/sql/xid.sql @@ -46,3 +46,107 @@ create table xid8_t1 (x xid8); create index on xid8_t1 using btree(x); create index on xid8_t1 using hash(x); drop table xid8_t1; + + +-- pg_snapshot data type and related functions + +-- Note: another set of tests similar to this exists in txid.sql, for a limited +-- time (the relevant functions share C code) + +-- i/o +select '12:13:'::pg_snapshot; +select '12:18:14,16'::pg_snapshot; +select '12:16:14,14'::pg_snapshot; + +-- errors +select '31:12:'::pg_snapshot; +select '0:1:'::pg_snapshot; +select '12:13:0'::pg_snapshot; +select '12:16:14,13'::pg_snapshot; + +create temp table snapshot_test ( + nr integer, + snap pg_snapshot +); + +insert into snapshot_test values (1, '12:13:'); +insert into snapshot_test values (2, '12:20:13,15,18'); +insert into snapshot_test values (3, '100001:100009:100005,100007,100008'); +insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131'); +select snap from snapshot_test order by nr; + +select pg_snapshot_xmin(snap), + pg_snapshot_xmax(snap), + pg_snapshot_xip(snap) +from snapshot_test order by nr; + +select id, pg_visible_in_snapshot(id::text::xid8, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + +-- test bsearch +select id, pg_visible_in_snapshot(id::text::xid8, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + +-- test current values also +select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot()); + +-- we can't assume current is always less than xmax, however + +select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot()); + +-- test 64bitness + +select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; +select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); +select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + +-- test 64bit overflow +SELECT pg_snapshot '1:9223372036854775807:3'; +SELECT pg_snapshot '1:9223372036854775808:3'; + +-- test pg_current_xact_id_if_assigned +BEGIN; +SELECT pg_current_xact_id_if_assigned() IS NULL; +SELECT pg_current_xact_id() \gset +SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id'; +COMMIT; + +-- test xid status functions +BEGIN; +SELECT pg_current_xact_id() AS committed \gset +COMMIT; + +BEGIN; +SELECT pg_current_xact_id() AS rolledback \gset +ROLLBACK; + +BEGIN; +SELECT pg_current_xact_id() AS inprogress \gset + +SELECT pg_xact_status(:committed::text::xid8) AS committed; +SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback; +SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress; +SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed +SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed +SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin + +COMMIT; + +BEGIN; +CREATE FUNCTION test_future_xid_status(xid8) +RETURNS void +LANGUAGE plpgsql +AS +$$ +BEGIN + PERFORM pg_xact_status($1); + RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected'; +EXCEPTION + WHEN invalid_parameter_value THEN + RAISE NOTICE 'Got expected error for xid in the future'; +END; +$$; +SELECT test_future_xid_status((:inprogress + 10000)::text::xid8); +ROLLBACK; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 34623523a7..525d58e7f0 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -3099,6 +3099,7 @@ pg_sha224_ctx pg_sha256_ctx pg_sha384_ctx pg_sha512_ctx +pg_snapshot pg_stack_base_t pg_time_t pg_tz