/*------------------------------------------------------------------------- * * xid.c * POSTGRES transaction identifier and command identifier datatypes. * * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/utils/adt/xid.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "access/multixact.h" #include "access/transam.h" #include "access/xact.h" #include "libpq/pqformat.h" #include "utils/builtins.h" #include "utils/xid8.h" #define PG_GETARG_COMMANDID(n) DatumGetCommandId(PG_GETARG_DATUM(n)) #define PG_RETURN_COMMANDID(x) return CommandIdGetDatum(x) Datum xidin(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); PG_RETURN_TRANSACTIONID((TransactionId) strtoul(str, NULL, 0)); } Datum xidout(PG_FUNCTION_ARGS) { TransactionId transactionId = PG_GETARG_TRANSACTIONID(0); char *result = (char *) palloc(16); snprintf(result, 16, "%lu", (unsigned long) transactionId); PG_RETURN_CSTRING(result); } /* * xidrecv - converts external binary format to xid */ Datum xidrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_TRANSACTIONID((TransactionId) pq_getmsgint(buf, sizeof(TransactionId))); } /* * xidsend - converts xid to binary format */ Datum xidsend(PG_FUNCTION_ARGS) { TransactionId arg1 = PG_GETARG_TRANSACTIONID(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendint32(&buf, arg1); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } /* * xideq - are two xids equal? */ Datum xideq(PG_FUNCTION_ARGS) { TransactionId xid1 = PG_GETARG_TRANSACTIONID(0); TransactionId xid2 = PG_GETARG_TRANSACTIONID(1); PG_RETURN_BOOL(TransactionIdEquals(xid1, xid2)); } /* * xidneq - are two xids different? */ Datum xidneq(PG_FUNCTION_ARGS) { TransactionId xid1 = PG_GETARG_TRANSACTIONID(0); TransactionId xid2 = PG_GETARG_TRANSACTIONID(1); PG_RETURN_BOOL(!TransactionIdEquals(xid1, xid2)); } /* * xid_age - compute age of an XID (relative to latest stable xid) */ Datum xid_age(PG_FUNCTION_ARGS) { TransactionId xid = PG_GETARG_TRANSACTIONID(0); TransactionId now = GetStableLatestTransactionId(); /* Permanent XIDs are always infinitely old */ if (!TransactionIdIsNormal(xid)) PG_RETURN_INT32(INT_MAX); PG_RETURN_INT32((int32) (now - xid)); } /* * mxid_age - compute age of a multi XID (relative to latest stable mxid) */ Datum mxid_age(PG_FUNCTION_ARGS) { TransactionId xid = PG_GETARG_TRANSACTIONID(0); MultiXactId now = ReadNextMultiXactId(); if (!MultiXactIdIsValid(xid)) PG_RETURN_INT32(INT_MAX); PG_RETURN_INT32((int32) (now - xid)); } /* * xidComparator * qsort comparison function for XIDs * * We can't use wraparound comparison for XIDs because that does not respect * the triangle inequality! Any old sort order will do. */ int xidComparator(const void *arg1, const void *arg2) { TransactionId xid1 = *(const TransactionId *) arg1; TransactionId xid2 = *(const TransactionId *) arg2; if (xid1 > xid2) return 1; if (xid1 < xid2) return -1; return 0; } /* * xidLogicalComparator * qsort comparison function for XIDs * * This is used to compare only XIDs from the same epoch (e.g. for backends * running at the same time). So there must be only normal XIDs, so there's * no issue with triangle inequality. */ int xidLogicalComparator(const void *arg1, const void *arg2) { TransactionId xid1 = *(const TransactionId *) arg1; TransactionId xid2 = *(const TransactionId *) arg2; Assert(TransactionIdIsNormal(xid1)); Assert(TransactionIdIsNormal(xid2)); if (TransactionIdPrecedes(xid1, xid2)) return -1; if (TransactionIdPrecedes(xid2, xid1)) return 1; return 0; } Datum xid8toxid(PG_FUNCTION_ARGS) { FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0); PG_RETURN_TRANSACTIONID(XidFromFullTransactionId(fxid)); } Datum xid8in(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(pg_strtouint64(str, NULL, 0))); } Datum xid8out(PG_FUNCTION_ARGS) { FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0); char *result = (char *) palloc(21); snprintf(result, 21, UINT64_FORMAT, U64FromFullTransactionId(fxid)); PG_RETURN_CSTRING(result); } Datum xid8recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); uint64 value; value = (uint64) pq_getmsgint64(buf); PG_RETURN_FULLTRANSACTIONID(FullTransactionIdFromU64(value)); } Datum xid8send(PG_FUNCTION_ARGS) { FullTransactionId arg1 = PG_GETARG_FULLTRANSACTIONID(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendint64(&buf, (uint64) U64FromFullTransactionId(arg1)); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } Datum xid8eq(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(FullTransactionIdEquals(fxid1, fxid2)); } Datum xid8ne(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(!FullTransactionIdEquals(fxid1, fxid2)); } Datum xid8lt(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(FullTransactionIdPrecedes(fxid1, fxid2)); } Datum xid8gt(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(FullTransactionIdFollows(fxid1, fxid2)); } Datum xid8le(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(FullTransactionIdPrecedesOrEquals(fxid1, fxid2)); } Datum xid8ge(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); PG_RETURN_BOOL(FullTransactionIdFollowsOrEquals(fxid1, fxid2)); } Datum xid8cmp(PG_FUNCTION_ARGS) { FullTransactionId fxid1 = PG_GETARG_FULLTRANSACTIONID(0); FullTransactionId fxid2 = PG_GETARG_FULLTRANSACTIONID(1); if (FullTransactionIdFollows(fxid1, fxid2)) PG_RETURN_INT32(1); else if (FullTransactionIdEquals(fxid1, fxid2)) PG_RETURN_INT32(0); else PG_RETURN_INT32(-1); } /***************************************************************************** * COMMAND IDENTIFIER ROUTINES * *****************************************************************************/ /* * cidin - converts CommandId to internal representation. */ Datum cidin(PG_FUNCTION_ARGS) { char *str = PG_GETARG_CSTRING(0); PG_RETURN_COMMANDID((CommandId) strtoul(str, NULL, 0)); } /* * cidout - converts a cid to external representation. */ Datum cidout(PG_FUNCTION_ARGS) { CommandId c = PG_GETARG_COMMANDID(0); char *result = (char *) palloc(16); snprintf(result, 16, "%lu", (unsigned long) c); PG_RETURN_CSTRING(result); } /* * cidrecv - converts external binary format to cid */ Datum cidrecv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); PG_RETURN_COMMANDID((CommandId) pq_getmsgint(buf, sizeof(CommandId))); } /* * cidsend - converts cid to binary format */ Datum cidsend(PG_FUNCTION_ARGS) { CommandId arg1 = PG_GETARG_COMMANDID(0); StringInfoData buf; pq_begintypsend(&buf); pq_sendint32(&buf, arg1); PG_RETURN_BYTEA_P(pq_endtypsend(&buf)); } Datum cideq(PG_FUNCTION_ARGS) { CommandId arg1 = PG_GETARG_COMMANDID(0); CommandId arg2 = PG_GETARG_COMMANDID(1); PG_RETURN_BOOL(arg1 == arg2); }