From 5d54ea7722406d55078b735fa420d474bee8cafb Mon Sep 17 00:00:00 2001 From: tgl Date: Sat, 13 Oct 2007 23:06:28 +0000 Subject: [PATCH] Migrate the former contrib/txid module into core. This will make it easier for Slony and Skytools to depend on it. Per discussion. --- doc/src/sgml/datatype.sgml | 6 + doc/src/sgml/func.sgml | 78 +++++ src/backend/utils/adt/Makefile | 4 +- src/backend/utils/adt/txid.c | 583 +++++++++++++++++++++++++++++++++++++ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.h | 22 ++ src/include/catalog/pg_type.h | 4 + src/include/utils/builtins.h | 28 +- src/test/regress/expected/txid.out | 226 ++++++++++++++ src/test/regress/parallel_schedule | 2 +- src/test/regress/serial_schedule | 1 + src/test/regress/sql/txid.sql | 54 ++++ 12 files changed, 998 insertions(+), 12 deletions(-) create mode 100644 src/backend/utils/adt/txid.c create mode 100644 src/test/regress/expected/txid.out create mode 100644 src/test/regress/sql/txid.sql diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 94bdb63214..2e9c80621a 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -247,6 +247,12 @@ + txid_snapshot + + user-level transaction ID snapshot + + + uuid universally unique identifier diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 905cf518b9..bb95da121c 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -12048,6 +12048,84 @@ SELECT pg_type_is_visible('myschema.widget'::regtype); databases within each cluster and their descriptions are stored globally as well. + + + txid_current + + + + txid_current_snapshot + + + + txid_snapshot_xmin + + + + txid_snapshot_xmax + + + + txid_snapshot_xip + + + + txid_visible_in_snapshot + + + + The functions shown in + export server internal transaction info to user level. + + + + Transaction IDs and snapshots + + + Name Return Type Description + + + + + txid_current() + bigint + get current transaction ID + + + txid_current_snapshot() + txid_snapshot + get current snapshot + + + txid_snapshot_xmin(txid_snapshot) + bigint + get xmin of snapshot + + + txid_snapshot_xmax(txid_snapshot) + bigint + get xmax of snapshot + + + txid_snapshot_xip(txid_snapshot) + setof bigint + get in-progress transaction IDs in snapshot + + + txid_visible_in_snapshot(bigint, txid_snapshot) + boolean + is transaction ID visible in snapshot? + + + +
+ + + The internal transaction ID type (xid) is 32 bits wide and so + it wraps around every 4 billion transactions. However, these functions + export a 64-bit format that is extended with an epoch counter + so that it will not wrap around for the life of an installation. + diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index f9a5097f0e..aa2b922a87 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -28,8 +28,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \ ascii.o quote.o pgstatfuncs.o encode.o dbsize.o genfile.o \ tsginidx.o tsgistidx.o tsquery.o tsquery_cleanup.o tsquery_gist.o \ tsquery_op.o tsquery_rewrite.o tsquery_util.o tsrank.o \ - tsvector.o tsvector_op.o tsvector_parser.o\ - uuid.o xml.o + tsvector.o tsvector_op.o tsvector_parser.o \ + txid.o uuid.o xml.o like.o: like.c like_match.c diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/txid.c new file mode 100644 index 0000000000..f63d654f7b --- /dev/null +++ b/src/backend/utils/adt/txid.c @@ -0,0 +1,583 @@ +/*------------------------------------------------------------------------- + * txid.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 + * 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(). + * + * + * Copyright (c) 2003-2007, PostgreSQL Global Development Group + * Author: Jan Wieck, Afilias USA INC. + * 64-bit txids: Marko Kreen, Skype Technologies + * + * $PostgreSQL$ + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include "access/transam.h" +#include "access/xact.h" +#include "funcapi.h" +#include "libpq/pqformat.h" +#include "utils/builtins.h" + + +#ifndef INT64_IS_BUSTED +/* txid will be signed int8 in database, so must limit to 63 bits */ +#define MAX_TXID UINT64CONST(0x7FFFFFFFFFFFFFFF) +#else +/* we only really have 32 bits to work with :-( */ +#define MAX_TXID UINT64CONST(0x7FFFFFFF) +#endif + +/* Use unsigned variant internally */ +typedef uint64 txid; + +/* sprintf format code for uint64 */ +#define TXID_FMT UINT64_FORMAT + +/* + * If defined, use bsearch() function for searching for txids in snapshots + * that have more than the specified number of values. + */ +#define USE_BSEARCH_IF_NXIP_GREATER 30 + + +/* + * Snapshot containing 8byte txids. + */ +typedef struct +{ + /* + * 4-byte length hdr, should not be touched directly. + * + * Explicit embedding is ok as we want always correct + * alignment anyway. + */ + int32 __varsz; + + uint32 nxip; /* number of txids in xip array */ + txid xmin; + txid xmax; + txid xip[1]; /* in-progress txids, xmin <= xip[i] < xmax */ +} TxidSnapshot; + +#define TXID_SNAPSHOT_SIZE(nxip) \ + (offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip)) + +/* + * 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) +{ + GetNextXidAndEpoch(&state->last_xid, &state->epoch); +} + +/* + * do a TransactionId -> txid conversion for an XID near the given epoch + */ +static txid +convert_xid(TransactionId xid, const TxidEpoch *state) +{ +#ifndef INT64_IS_BUSTED + uint64 epoch; + + /* return special xid's as-is */ + if (!TransactionIdIsNormal(xid)) + return (txid) 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)) + epoch--; + else if (xid < state->last_xid && + TransactionIdFollows(xid, state->last_xid)) + epoch++; + + return (epoch << 32) | xid; +#else /* INT64_IS_BUSTED */ + /* we can't do anything with the epoch, so ignore it */ + return (txid) xid & MAX_TXID; +#endif /* INT64_IS_BUSTED */ +} + +/* + * txid comparator for qsort/bsearch + */ +static int +cmp_txid(const void *aa, const void *bb) +{ + txid a = *(const txid *) aa; + txid b = *(const txid *) bb; + + if (a < b) + return -1; + if (a > b) + return 1; + return 0; +} + +/* + * sort a snapshot's txids, so we can use bsearch() later. + * + * For consistency of on-disk representation, we always sort even if bsearch + * will not be used. + */ +static void +sort_snapshot(TxidSnapshot *snap) +{ + if (snap->nxip > 1) + qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid); +} + +/* + * check txid visibility. + */ +static bool +is_visible_txid(txid value, const TxidSnapshot *snap) +{ + if (value < snap->xmin) + return true; + else if (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); + /* if found, transaction is still in progress */ + return (res) ? false : true; + } +#endif + else + { + uint32 i; + + for (i = 0; i < snap->nxip; i++) + { + if (value == snap->xip[i]) + return false; + } + return true; + } +} + +/* + * helper functions to use StringInfo for TxidSnapshot creation. + */ + +static StringInfo +buf_init(txid xmin, txid xmax) +{ + TxidSnapshot snap; + StringInfo buf; + + snap.xmin = xmin; + snap.xmax = xmax; + snap.nxip = 0; + + buf = makeStringInfo(); + appendBinaryStringInfo(buf, (char *)&snap, TXID_SNAPSHOT_SIZE(0)); + return buf; +} + +static void +buf_add_txid(StringInfo buf, txid xid) +{ + TxidSnapshot *snap = (TxidSnapshot *)buf->data; + + /* do this before possible realloc */ + snap->nxip++; + + appendBinaryStringInfo(buf, (char *)&xid, sizeof(xid)); +} + +static TxidSnapshot * +buf_finalize(StringInfo buf) +{ + TxidSnapshot *snap = (TxidSnapshot *)buf->data; + + SET_VARSIZE(snap, buf->len); + + /* buf is not needed anymore */ + buf->data = NULL; + pfree(buf); + + return snap; +} + +/* + * simple number parser. + * + * We return 0 on error, which is invalid value for txid. + */ +static txid +str2txid(const char *s, const char **endp) +{ + txid val = 0; + txid cutoff = MAX_TXID / 10; + txid cutlim = MAX_TXID % 10; + + for (; *s; s++) + { + unsigned d; + + if (*s < '0' || *s > '9') + break; + d = *s - '0'; + + /* + * check for overflow + */ + if (val > cutoff || (val == cutoff && d > cutlim)) + { + val = 0; + break; + } + + val = val * 10 + d; + } + if (endp) + *endp = s; + return val; +} + +/* + * parse snapshot from cstring + */ +static TxidSnapshot * +parse_snapshot(const char *str) +{ + txid xmin; + txid xmax; + txid last_val = 0, val; + const char *str_start = str; + const char *endp; + StringInfo buf; + + xmin = str2txid(str, &endp); + if (*endp != ':') + goto bad_format; + str = endp + 1; + + xmax = str2txid(str, &endp); + if (*endp != ':') + goto bad_format; + str = endp + 1; + + /* it should look sane */ + if (xmin == 0 || xmax == 0 || xmin > xmax) + goto bad_format; + + /* allocate buffer */ + buf = buf_init(xmin, xmax); + + /* loop over values */ + while (*str != '\0') + { + /* read next value */ + val = str2txid(str, &endp); + str = endp; + + /* require the input to be in order */ + if (val < xmin || val >= xmax || val <= last_val) + goto bad_format; + + buf_add_txid(buf, val); + last_val = val; + + if (*str == ',') + str++; + else if (*str != '\0') + goto bad_format; + } + + return buf_finalize(buf); + +bad_format: + elog(ERROR, "invalid input for txid_snapshot: \"%s\"", str_start); + return NULL; +} + +/* + * 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 + * + * Return the current toplevel transaction ID as TXID + */ +Datum +txid_current(PG_FUNCTION_ARGS) +{ + txid val; + TxidEpoch state; + + load_xid_epoch(&state); + + val = convert_xid(GetTopTransactionId(), &state); + + PG_RETURN_INT64(val); +} + +/* + * txid_current_snapshot() returns txid_snapshot + * + * Return current snapshot in TXID format + * + * Note that only top-transaction XIDs are included in the snapshot. + */ +Datum +txid_current_snapshot(PG_FUNCTION_ARGS) +{ + TxidSnapshot *snap; + uint32 nxip, i, size; + TxidEpoch state; + Snapshot cur; + + cur = ActiveSnapshot; + if (cur == NULL) + elog(ERROR, "txid_current_snapshot: ActiveSnapshot == NULL"); + + load_xid_epoch(&state); + + /* allocate */ + nxip = cur->xcnt; + size = TXID_SNAPSHOT_SIZE(nxip); + snap = palloc(size); + SET_VARSIZE(snap, size); + + /* fill */ + snap->xmin = convert_xid(cur->xmin, &state); + snap->xmax = convert_xid(cur->xmax, &state); + snap->nxip = nxip; + for (i = 0; i < nxip; i++) + snap->xip[i] = convert_xid(cur->xip[i], &state); + + /* we want them guaranteed to be in ascending order */ + sort_snapshot(snap); + + PG_RETURN_POINTER(snap); +} + +/* + * txid_snapshot_in(cstring) returns txid_snapshot + * + * input function for type txid_snapshot + */ +Datum +txid_snapshot_in(PG_FUNCTION_ARGS) +{ + char *str = PG_GETARG_CSTRING(0); + TxidSnapshot *snap; + + snap = parse_snapshot(str); + + PG_RETURN_POINTER(snap); +} + +/* + * txid_snapshot_out(txid_snapshot) returns cstring + * + * output function for type txid_snapshot + */ +Datum +txid_snapshot_out(PG_FUNCTION_ARGS) +{ + TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + StringInfoData str; + uint32 i; + + initStringInfo(&str); + + appendStringInfo(&str, TXID_FMT ":", snap->xmin); + appendStringInfo(&str, TXID_FMT ":", snap->xmax); + + for (i = 0; i < snap->nxip; i++) + { + if (i > 0) + appendStringInfoChar(&str, ','); + appendStringInfo(&str, TXID_FMT, snap->xip[i]); + } + + PG_RETURN_CSTRING(str.data); +} + +/* + * txid_snapshot_recv(internal) returns txid_snapshot + * + * binary input function for type txid_snapshot + * + * format: int4 nxip, int8 xmin, int8 xmax, int8 xip + */ +Datum +txid_snapshot_recv(PG_FUNCTION_ARGS) +{ + StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); + TxidSnapshot *snap; + txid last = 0; + int nxip; + int i; + int avail; + int expect; + txid xmin, xmax; + + /* + * load nxip and check for nonsense. + * + * (nxip > avail) check is against int overflows in 'expect'. + */ + nxip = pq_getmsgint(buf, 4); + avail = buf->len - buf->cursor; + expect = 8 + 8 + nxip * 8; + if (nxip < 0 || nxip > avail || expect > avail) + goto bad_format; + + xmin = pq_getmsgint64(buf); + xmax = pq_getmsgint64(buf); + if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID) + goto bad_format; + + snap = palloc(TXID_SNAPSHOT_SIZE(nxip)); + snap->xmin = xmin; + snap->xmax = xmax; + snap->nxip = nxip; + SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip)); + + for (i = 0; i < nxip; i++) + { + txid cur = pq_getmsgint64(buf); + if (cur <= last || cur < xmin || cur >= xmax) + goto bad_format; + snap->xip[i] = cur; + last = cur; + } + PG_RETURN_POINTER(snap); + +bad_format: + elog(ERROR, "invalid snapshot data"); + return (Datum)NULL; +} + +/* + * txid_snapshot_send(txid_snapshot) returns bytea + * + * binary output function for type txid_snapshot + * + * format: int4 nxip, int8 xmin, int8 xmax, int8 xip + */ +Datum +txid_snapshot_send(PG_FUNCTION_ARGS) +{ + TxidSnapshot *snap = (TxidSnapshot *)PG_GETARG_VARLENA_P(0); + StringInfoData buf; + uint32 i; + + pq_begintypsend(&buf); + pq_sendint(&buf, snap->nxip, 4); + pq_sendint64(&buf, snap->xmin); + pq_sendint64(&buf, snap->xmax); + for (i = 0; i < snap->nxip; i++) + pq_sendint64(&buf, snap->xip[i]); + PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); +} + +/* + * txid_visible_in_snapshot(int8, txid_snapshot) returns bool + * + * is txid visible in snapshot ? + */ +Datum +txid_visible_in_snapshot(PG_FUNCTION_ARGS) +{ + txid value = PG_GETARG_INT64(0); + TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1); + + PG_RETURN_BOOL(is_visible_txid(value, snap)); +} + +/* + * txid_snapshot_xmin(txid_snapshot) returns int8 + * + * return snapshot's xmin + */ +Datum +txid_snapshot_xmin(PG_FUNCTION_ARGS) +{ + TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + + PG_RETURN_INT64(snap->xmin); +} + +/* + * txid_snapshot_xmax(txid_snapshot) returns int8 + * + * return snapshot's xmax + */ +Datum +txid_snapshot_xmax(PG_FUNCTION_ARGS) +{ + TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + + PG_RETURN_INT64(snap->xmax); +} + +/* + * txid_snapshot_xip(txid_snapshot) returns setof int8 + * + * return in-progress TXIDs in snapshot. + */ +Datum +txid_snapshot_xip(PG_FUNCTION_ARGS) +{ + FuncCallContext *fctx; + TxidSnapshot *snap; + txid value; + + /* on first call initialize snap_state and get copy of snapshot */ + if (SRF_IS_FIRSTCALL()) { + TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0); + + fctx = SRF_FIRSTCALL_INIT(); + + /* make a copy of user snapshot */ + snap = MemoryContextAlloc(fctx->multi_call_memory_ctx, VARSIZE(arg)); + memcpy(snap, arg, VARSIZE(arg)); + + fctx->user_fctx = snap; + } + + /* return values one-by-one */ + fctx = SRF_PERCALL_SETUP(); + snap = fctx->user_fctx; + if (fctx->call_cntr < snap->nxip) { + value = snap->xip[fctx->call_cntr]; + SRF_RETURN_NEXT(fctx, Int64GetDatum(value)); + } else { + SRF_RETURN_DONE(fctx); + } +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 75a32e5730..8d1c54f6c4 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200710131 +#define CATALOG_VERSION_NO 200710141 #endif diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 4fd6e22a2d..8b1c0e7451 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -4399,6 +4399,28 @@ DESCR("I/O"); DATA(insert OID = 3774 ( regdictionarysend PGNSP PGUID 12 1 0 f f t f i 1 17 "3769" _null_ _null_ _null_ regdictionarysend - _null_ _null_ )); DESCR("I/O"); +/* txid */ +DATA(insert OID = 2939 ( txid_snapshot_in PGNSP PGUID 12 1 0 f f t f i 1 2970 "2275" _null_ _null_ _null_ txid_snapshot_in - _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 2940 ( txid_snapshot_out PGNSP PGUID 12 1 0 f f t f i 1 2275 "2970" _null_ _null_ _null_ txid_snapshot_out - _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 2941 ( txid_snapshot_recv PGNSP PGUID 12 1 0 f f t f i 1 2970 "2281" _null_ _null_ _null_ txid_snapshot_recv - _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 2942 ( txid_snapshot_send PGNSP PGUID 12 1 0 f f t f i 1 17 "2970" _null_ _null_ _null_ txid_snapshot_send - _null_ _null_ )); +DESCR("I/O"); +DATA(insert OID = 2943 ( txid_current PGNSP PGUID 12 1 0 f f t f s 0 20 "" _null_ _null_ _null_ txid_current - _null_ _null_ )); +DESCR("get current transaction ID"); +DATA(insert OID = 2944 ( txid_current_snapshot PGNSP PGUID 12 1 0 f f t f s 0 2970 "" _null_ _null_ _null_ txid_current_snapshot - _null_ _null_ )); +DESCR("get current snapshot"); +DATA(insert OID = 2945 ( txid_snapshot_xmin PGNSP PGUID 12 1 0 f f t f i 1 20 "2970" _null_ _null_ _null_ txid_snapshot_xmin - _null_ _null_ )); +DESCR("get xmin of snapshot"); +DATA(insert OID = 2946 ( txid_snapshot_xmax PGNSP PGUID 12 1 0 f f t f i 1 20 "2970" _null_ _null_ _null_ txid_snapshot_xmax - _null_ _null_ )); +DESCR("get xmax of snapshot"); +DATA(insert OID = 2947 ( txid_snapshot_xip PGNSP PGUID 12 1 50 f f t t i 1 20 "2970" _null_ _null_ _null_ txid_snapshot_xip - _null_ _null_ )); +DESCR("get set of in-progress txids in snapshot"); +DATA(insert OID = 2948 ( txid_visible_in_snapshot PGNSP PGUID 12 1 0 f f t f i 2 16 "20 2970" _null_ _null_ _null_ txid_visible_in_snapshot - _null_ _null_ )); +DESCR("is txid visible in snapshot?"); + /* * Symbolic values for provolatile column: these indicate whether the result diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 8a6c909e70..3b3de74eaa 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -572,6 +572,10 @@ DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b t \054 0 3615 0 array_in a DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b t \054 0 3734 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b t \054 0 3769 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); +DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 _null_ _null_ )); +DESCR("txid snapshot"); +DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b t \054 0 2970 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ )); + /* * pseudo-types * diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 6aacf922ba..04237d907a 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -938,14 +938,17 @@ extern Datum pg_advisory_unlock_int4(PG_FUNCTION_ARGS); extern Datum pg_advisory_unlock_shared_int4(PG_FUNCTION_ARGS); extern Datum pg_advisory_unlock_all(PG_FUNCTION_ARGS); -/* access/transam/twophase.c */ -extern Datum pg_prepared_xact(PG_FUNCTION_ARGS); - -/* commands/prepare.c */ -extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); - -/* utils/mmgr/portalmem.c */ -extern Datum pg_cursor(PG_FUNCTION_ARGS); +/* txid.c */ +extern Datum txid_snapshot_in(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_out(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_recv(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_send(PG_FUNCTION_ARGS); +extern Datum txid_current(PG_FUNCTION_ARGS); +extern Datum txid_current_snapshot(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_xmin(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_xmax(PG_FUNCTION_ARGS); +extern Datum txid_snapshot_xip(PG_FUNCTION_ARGS); +extern Datum txid_visible_in_snapshot(PG_FUNCTION_ARGS); /* uuid.c */ extern Datum uuid_in(PG_FUNCTION_ARGS); @@ -961,4 +964,13 @@ extern Datum uuid_ne(PG_FUNCTION_ARGS); extern Datum uuid_cmp(PG_FUNCTION_ARGS); extern Datum uuid_hash(PG_FUNCTION_ARGS); +/* access/transam/twophase.c */ +extern Datum pg_prepared_xact(PG_FUNCTION_ARGS); + +/* commands/prepare.c */ +extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); + +/* utils/mmgr/portalmem.c */ +extern Datum pg_cursor(PG_FUNCTION_ARGS); + #endif /* BUILTINS_H */ diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out new file mode 100644 index 0000000000..59b629358a --- /dev/null +++ b/src/test/regress/expected/txid.out @@ -0,0 +1,226 @@ +-- txid_snapshot data type and related functions +-- i/o +select '12:13:'::txid_snapshot; + txid_snapshot +--------------- + 12:13: +(1 row) + +select '12:18:14,16'::txid_snapshot; + txid_snapshot +--------------- + 12:18:14,16 +(1 row) + +-- errors +select '31:12:'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "31:12:" +select '0:1:'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "0:1:" +select '12:13:0'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:13:0" +select '12:16:14,13'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:16:14,13" +select '12:16:14,14'::txid_snapshot; +ERROR: invalid input for txid_snapshot: "12:16:14,14" +create temp table snapshot_test ( + nr integer, + snap txid_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 txid_snapshot_xmin(snap), + txid_snapshot_xmax(snap), + txid_snapshot_xip(snap) +from snapshot_test order by nr; + txid_snapshot_xmin | txid_snapshot_xmax | txid_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, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + id | txid_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, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + id | txid_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 txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); + ?column? +---------- + t +(1 row) + +-- we can't assume current is always less than xmax, however +select txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); + txid_visible_in_snapshot +-------------------------- + f +(1 row) + +-- test 64bitness +select txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; + txid_snapshot +--------------------------------------------------------------------- + 1000100010001000:1000100010001100:1000100010001012,1000100010001013 +(1 row) + +select txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + txid_visible_in_snapshot +-------------------------- + f +(1 row) + +select txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + txid_visible_in_snapshot +-------------------------- + t +(1 row) + +-- test 64bit overflow +SELECT txid_snapshot '1:9223372036854775807:3'; + txid_snapshot +------------------------- + 1:9223372036854775807:3 +(1 row) + +SELECT txid_snapshot '1:9223372036854775808:3'; +ERROR: invalid input for txid_snapshot: "1:9223372036854775808:3" diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index 1ff2bdb85c..a62515fe48 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -8,7 +8,7 @@ # ---------- # The first group of parallel tests # ---------- -test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid enum +test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum # Depends on things setup during char, varchar and text test: strings diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 20568c51b3..ad26d46eb0 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -13,6 +13,7 @@ test: float4 test: float8 test: bit test: numeric +test: txid test: uuid test: enum test: strings diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql new file mode 100644 index 0000000000..ecae10e024 --- /dev/null +++ b/src/test/regress/sql/txid.sql @@ -0,0 +1,54 @@ +-- txid_snapshot data type and related functions + +-- i/o +select '12:13:'::txid_snapshot; +select '12:18:14,16'::txid_snapshot; + +-- errors +select '31:12:'::txid_snapshot; +select '0:1:'::txid_snapshot; +select '12:13:0'::txid_snapshot; +select '12:16:14,13'::txid_snapshot; +select '12:16:14,14'::txid_snapshot; + +create temp table snapshot_test ( + nr integer, + snap txid_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 txid_snapshot_xmin(snap), + txid_snapshot_xmax(snap), + txid_snapshot_xip(snap) +from snapshot_test order by nr; + +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(11, 21) id +where nr = 2; + +-- test bsearch +select id, txid_visible_in_snapshot(id, snap) +from snapshot_test, generate_series(90, 160) id +where nr = 4; + +-- test current values also +select txid_current() >= txid_snapshot_xmin(txid_current_snapshot()); + +-- we can't assume current is always less than xmax, however + +select txid_visible_in_snapshot(txid_current(), txid_current_snapshot()); + +-- test 64bitness + +select txid_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013'; +select txid_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); +select txid_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013'); + +-- test 64bit overflow +SELECT txid_snapshot '1:9223372036854775807:3'; +SELECT txid_snapshot '1:9223372036854775808:3'; -- 2.11.4.GIT