I played around with this idea yesterday. Experiment-grade patch attached. Approach:
1. Introduce a new type BigTransactionId (better names welcome).
txid_current() should be changed to BigTransactionId too. It's currently bigint.
Users are currently left in a real mess when it comes to pg_stat_activity xids, etc, which are epoch-unqualified. txid_current() reports an epoch-qualified xid, but there's no sensible and safe conversion from txid_current()'s bigint to an epoch-qualified ID. age() takes 'xid', everything takes 'xid', but is completely oblivious to epoch.
IMO all user-facing xids should be moved to BigTransactionId, with the 'xid' type ceasing to appear in any user facing role.
I think it's probably a good idea to make it very explicit when moving between big and small transaction IDs, hence the including of the word 'big' in variable and function names and the use of a function-like macro (rather than implicit conversion, which C doesn't give me a good way to prevent).
The only way I know of to prevent it is to use a wrapper by-value struct. I've used this technique in the past where it's critical that implicit conversions don't occurr, but it sure isn't pretty.
typedef struct BigTransactionId
{
uint64 value;
} BigTransactionId;
instead of
typedef uint64 BigTransactionId;
It's annoying having to get the value member, but it's definitely highly protective against errors. Pass-by-value isn't a problem, neither is return-by-value.
BigTransactionId
add_one(BigTransactionId xid)
{
BigTransactionId ret;
ret.value = xid.value + 1;
return ret;
}
Personally I think it's potentially worth the required gymnastics if older compilers do a sane job of code generation with this.