v7-0002-Add-64bit-xid.patch
application/octet-stream
Filename: v7-0002-Add-64bit-xid.patch
Type: application/octet-stream
Part: 2
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v7-0002
Subject: Add 64bit xid.
| File | + | − |
|---|---|---|
| contrib/amcheck/verify_heapam.c | 37 | 54 |
| contrib/amcheck/verify_nbtree.c | 1 | 1 |
| contrib/hstore/hstore_io.c | 2 | 0 |
| contrib/pageinspect/brinfuncs.c | 5 | 5 |
| contrib/pageinspect/btreefuncs.c | 14 | 11 |
| contrib/pageinspect/expected/hash_1.out | 166 | 0 |
| contrib/pageinspect/expected/oldextversions.out | 5 | 5 |
| contrib/pageinspect/expected/page.out | 14 | 14 |
| contrib/pageinspect/fsmfuncs.c | 1 | 1 |
| contrib/pageinspect/heapfuncs.c | 6 | 3 |
| contrib/pageinspect/Makefile | 2 | 1 |
| contrib/pageinspect/pageinspect--1.10--1.11.sql | 145 | 0 |
| contrib/pageinspect/pageinspect--1.5.sql | 2 | 0 |
| contrib/pageinspect/pageinspect.control | 1 | 1 |
| contrib/pageinspect/rawpage.c | 22 | 4 |
| contrib/pgrowlocks/pgrowlocks.c | 4 | 4 |
| contrib/pgstattuple/pgstatapprox.c | 1 | 0 |
| contrib/pgstattuple/pgstatindex.c | 1 | 1 |
| contrib/pg_surgery/heap_surgery.c | 8 | 2 |
| contrib/pg_visibility/expected/pg_visibility.out | 17 | 0 |
| contrib/pg_visibility/pg_visibility.c | 2 | 1 |
| contrib/pg_visibility/sql/pg_visibility.sql | 18 | 0 |
| contrib/postgres_fdw/expected/postgres_fdw.out | 36 | 19 |
| contrib/postgres_fdw/postgres_fdw.c | 5 | 4 |
| contrib/postgres_fdw/sql/postgres_fdw.sql | 10 | 5 |
| contrib/test_decoding/test_decoding.c | 12 | 12 |
| src/backend/access/common/heaptuple.c | 8 | 2 |
| src/backend/access/common/reloptions.c | 60 | 58 |
| src/backend/access/hash/hashvalidate.c | 2 | 3 |
| src/backend/access/heap/heapam.c | 925 | 134 |
| src/backend/access/heap/heapam_handler.c | 17 | 13 |
| src/backend/access/heap/heapam_visibility.c | 87 | 86 |
| src/backend/access/heap/heaptoast.c | 3 | 0 |
| src/backend/access/heap/hio.c | 12 | 4 |
| src/backend/access/heap/pruneheap.c | 42 | 18 |
| src/backend/access/heap/rewriteheap.c | 39 | 17 |
| src/backend/access/heap/vacuumlazy.c | 24 | 92 |
| src/backend/access/nbtree/nbtpage.c | 2 | 0 |
| src/backend/access/nbtree/nbtsplitloc.c | 12 | 1 |
| src/backend/access/nbtree/nbtxlog.c | 2 | 0 |
| src/backend/access/rmgrdesc/clogdesc.c | 4 | 4 |
| src/backend/access/rmgrdesc/committsdesc.c | 4 | 4 |
| src/backend/access/rmgrdesc/gistdesc.c | 3 | 5 |
| src/backend/access/rmgrdesc/hashdesc.c | 1 | 1 |
| src/backend/access/rmgrdesc/heapdesc.c | 41 | 9 |
| src/backend/access/rmgrdesc/mxactdesc.c | 6 | 6 |
| src/backend/access/rmgrdesc/nbtdesc.c | 3 | 5 |
| src/backend/access/rmgrdesc/spgdesc.c | 1 | 1 |
| src/backend/access/rmgrdesc/standbydesc.c | 3 | 3 |
| src/backend/access/rmgrdesc/xactdesc.c | 9 | 7 |
| src/backend/access/rmgrdesc/xlogdesc.c | 4 | 5 |
| src/backend/access/transam/clog.c | 41 | 51 |
| src/backend/access/transam/commit_ts.c | 22 | 48 |
| src/backend/access/transam/multixact.c | 154 | 731 |
| src/backend/access/transam/slru.c | 66 | 59 |
| src/backend/access/transam/subtrans.c | 26 | 25 |
| src/backend/access/transam/transam.c | 2 | 66 |
| src/backend/access/transam/twophase.c | 15 | 16 |
| src/backend/access/transam/varsup.c | 4 | 149 |
| src/backend/access/transam/xact.c | 26 | 24 |
| src/backend/access/transam/xlog.c | 22 | 18 |
| src/backend/access/transam/xlogreader.c | 0 | 34 |
| src/backend/bootstrap/bootstrap.c | 27 | 2 |
| src/backend/catalog/heap.c | 4 | 4 |
| src/backend/catalog/pg_inherits.c | 1 | 1 |
| src/backend/commands/async.c | 5 | 11 |
| src/backend/commands/sequence.c | 12 | 32 |
| src/backend/commands/vacuum.c | 46 | 30 |
| src/backend/executor/execExprInterp.c | 1 | 0 |
| src/backend/executor/execUtils.c | 1 | 0 |
| src/backend/executor/nodeModifyTable.c | 1 | 0 |
| src/backend/executor/spi.c | 1 | 0 |
| src/backend/nodes/outfuncs.c | 2 | 2 |
| src/backend/optimizer/util/plancat.c | 1 | 1 |
| src/backend/postmaster/autovacuum.c | 38 | 34 |
| src/backend/replication/logical/decode.c | 17 | 3 |
| src/backend/replication/logical/logical.c | 1 | 1 |
| src/backend/replication/logical/proto.c | 2 | 2 |
| src/backend/replication/logical/reorderbuffer.c | 18 | 11 |
| src/backend/replication/logical/snapbuild.c | 9 | 9 |
| src/backend/replication/logical/worker.c | 6 | 6 |
| src/backend/replication/walreceiver.c | 4 | 23 |
| src/backend/replication/walsender.c | 3 | 60 |
| src/backend/statistics/extended_stats.c | 1 | 0 |
| src/backend/storage/buffer/bufmgr.c | 15 | 4 |
| src/backend/storage/buffer/heap_convert.c | 496 | 0 |
| src/backend/storage/buffer/Makefile | 2 | 1 |
| src/backend/storage/ipc/procarray.c | 34 | 86 |
| src/backend/storage/ipc/standby.c | 5 | 5 |
| src/backend/storage/lmgr/lmgr.c | 10 | 3 |
| src/backend/storage/lmgr/predicate.c | 12 | 16 |
| src/backend/storage/page/bufpage.c | 188 | 11 |
| src/backend/utils/adt/enum.c | 1 | 1 |
| src/backend/utils/adt/jsonfuncs.c | 2 | 0 |
| src/backend/utils/adt/lockfuncs.c | 7 | 4 |
| src/backend/utils/adt/numutils.c | 22 | 0 |
| src/backend/utils/adt/pgstatfuncs.c | 1 | 0 |
| src/backend/utils/adt/rowtypes.c | 12 | 0 |
| src/backend/utils/adt/xid8funcs.c | 8 | 49 |
| src/backend/utils/adt/xid.c | 18 | 9 |
| src/backend/utils/cache/catcache.c | 1 | 0 |
| src/backend/utils/cache/relcache.c | 3 | 2 |
| src/backend/utils/error/csvlog.c | 2 | 2 |
| src/backend/utils/error/elog.c | 4 | 4 |
| src/backend/utils/error/jsonlog.c | 2 | 2 |
| src/backend/utils/fmgr/fmgr.c | 2 | 2 |
| src/backend/utils/misc/guc.c | 94 | 90 |
| src/backend/utils/misc/help_config.c | 7 | 1 |
| src/backend/utils/misc/pg_controldata.c | 1 | 2 |
| src/backend/utils/misc/postgresql.conf.sample | 2 | 2 |
| src/backend/utils/sort/tuplesort.c | 6 | 2 |
| src/backend/utils/time/combocid.c | 10 | 8 |
| src/backend/utils/time/snapmgr.c | 12 | 11 |
| src/bin/initdb/initdb.c | 65 | 2 |
| src/bin/pg_amcheck/t/004_verify_heapam.pl | 156 | 28 |
| src/bin/pg_controldata/pg_controldata.c | 8 | 9 |
| src/bin/pg_dump/pg_dump.c | 38 | 13 |
| src/bin/pg_dump/pg_dump.h | 4 | 4 |
| src/bin/pg_resetwal/pg_resetwal.c | 67 | 76 |
| src/bin/pg_upgrade/check.c | 114 | 0 |
| src/bin/pg_upgrade/controldata.c | 19 | 9 |
| src/bin/pg_upgrade/file.c | 116 | 56 |
| src/bin/pg_upgrade/Makefile | 1 | 0 |
| src/bin/pg_upgrade/pg_upgrade.c | 98 | 34 |
| src/bin/pg_upgrade/pg_upgrade.h | 18 | 6 |
| src/bin/pg_upgrade/segresize.c | 571 | 0 |
| src/bin/pg_upgrade/test.sh | 18 | 1 |
| src/bin/pg_upgrade/util.c | 17 | 0 |
| src/bin/pg_upgrade/version.c | 101 | 33 |
| src/bin/pg_verifybackup/t/003_corruption.pl | 1 | 1 |
| src/bin/pg_waldump/pg_waldump.c | 2 | 2 |
| src/include/access/clog.h | 1 | 1 |
| src/include/access/commit_ts.h | 1 | 1 |
| src/include/access/ginblock.h | 9 | 2 |
| src/include/access/gist.h | 1 | 1 |
| src/include/access/heapam.h | 11 | 5 |
| src/include/access/heapam_xlog.h | 19 | 3 |
| src/include/access/heaptoast.h | 1 | 1 |
| src/include/access/htup_details.h | 119 | 23 |
| src/include/access/htup.h | 11 | 5 |
| src/include/access/multixact.h | 5 | 9 |
| src/include/access/nbtree.h | 10 | 0 |
| src/include/access/rewriteheap.h | 1 | 1 |
| src/include/access/rmgrlist.h | 1 | 0 |
| src/include/access/slru.h | 14 | 14 |
| src/include/access/tableam.h | 1 | 1 |
| src/include/access/transam.h | 53 | 23 |
| src/include/access/xact.h | 9 | 4 |
| src/include/access/xlog.h | 8 | 0 |
| src/include/access/xlogreader.h | 0 | 4 |
| src/include/catalog/pg_amproc.dat | 2 | 2 |
| src/include/catalog/pg_control.h | 6 | 0 |
| src/include/catalog/pg_operator.dat | 4 | 4 |
| src/include/catalog/pg_proc.dat | 6 | 6 |
| src/include/catalog/pg_type.dat | 2 | 2 |
| src/include/c.h | 40 | 4 |
| src/include/commands/vacuum.h | 14 | 14 |
| src/include/fmgr.h | 2 | 0 |
| src/include/pg_config.h.in | 3 | 0 |
| src/include/postgres.h | 3 | 3 |
| src/include/postmaster/autovacuum.h | 2 | 2 |
| src/include/storage/bufmgr.h | 2 | 0 |
| src/include/storage/bufpage.h | 97 | 8 |
| src/include/storage/itemid.h | 2 | 0 |
| src/include/storage/lock.h | 7 | 7 |
| src/include/storage/proc.h | 1 | 1 |
| src/include/storage/standby.h | 1 | 1 |
| src/include/storage/sync.h | 1 | 1 |
| src/include/utils/builtins.h | 1 | 0 |
| src/include/utils/combocid.h | 1 | 1 |
| src/include/utils/rel.h | 6 | 6 |
| src/pl/plperl/plperl.c | 2 | 2 |
| src/pl/plpgsql/src/pl_comp.c | 2 | 2 |
| src/pl/plpgsql/src/pl_exec.c | 2 | 0 |
| src/pl/plpython/plpy_procedure.c | 2 | 2 |
| src/pl/tcl/pltcl.c | 2 | 2 |
| src/test/Makefile | 2 | 1 |
| src/test/perl/PostgreSQL/Test/Cluster.pm | 1 | 0 |
| src/test/recovery/t/003_recovery_targets.pl | 1 | 1 |
| src/test/regress/expected/indirect_toast.out | 8 | 0 |
| src/test/regress/expected/insert.out | 15 | 1 |
| src/test/regress/expected/opr_sanity.out | 3 | 3 |
| src/test/regress/expected/select_views.out | 35 | 35 |
| src/test/regress/expected/txid.out | 5 | 3 |
| src/test/regress/expected/xid.out | 8 | 6 |
| src/test/regress/pg_regress.c | 1 | 1 |
| src/test/regress/regress.c | 1 | 0 |
| src/test/regress/sql/indirect_toast.sql | 11 | 0 |
| src/test/regress/sql/insert.sql | 16 | 1 |
| src/test/regress/sql/select_views.sql | 1 | 1 |
| src/test/xid-64/Makefile | 22 | 0 |
| src/test/xid-64/README | 16 | 0 |
| src/test/xid-64/t/001_test_large_xids.pl | 53 | 0 |
| src/test/xid-64/t/002_test_gucs.pl | 78 | 0 |
| src/test/xid-64/t/003_test_integrity.pl | 56 | 0 |
| src/tools/msvc/Solution.pm | 1 | 0 |
From ccb59a5545dc1c94956395783e4447e9954a34d6 Mon Sep 17 00:00:00 2001
From: Yura Sokolov <y.sokolov@postgrespro.ru>
Date: Wed, 29 Dec 2021 18:54:31 +0300
Subject: [PATCH v7 2/3] Add 64bit xid.
- change TransactionId to 64bit
- disk tuple format (HeapTupleHeader) is (almost) unchanged: xmin and xmax remains 32bit
-- now 32bit xid is named ShortTransactionId
- heap page format is changed to contain xid and multixact base value, tuple's
xmin and xmax are offsets from.
-- xid_base and multi_base are stored as a page special data. PageHeader remains unmodified.
- in-memory tuple (HeapTuple) were enriched with copy of xid_base and multi_base from a page.
-- todo: replace xid_base/multi_base with precalculated 64bit xmin/xmax.
Authors:
- Alexander Korotkov <aekorotkov@gmail.com>
- Teodor Sigaev <teodor@sigaev.ru>
- Nikita Glukhov <n.gluhov@postgrespro.ru>
- Maxim Orlov <orlovmg@gmail.com>
- Pavel Borisov <pashkin.elfe@gmail.com>
- Yura Sokolov<y.sokolov@postgrespro.ru> <funny.falcon@gmail.com>
---
contrib/amcheck/verify_heapam.c | 91 +-
contrib/amcheck/verify_nbtree.c | 2 +-
contrib/hstore/hstore_io.c | 2 +
contrib/pageinspect/Makefile | 3 +-
contrib/pageinspect/brinfuncs.c | 10 +-
contrib/pageinspect/btreefuncs.c | 25 +-
contrib/pageinspect/expected/hash_1.out | 166 +++
.../pageinspect/expected/oldextversions.out | 10 +-
contrib/pageinspect/expected/page.out | 28 +-
contrib/pageinspect/fsmfuncs.c | 2 +-
contrib/pageinspect/heapfuncs.c | 9 +-
.../pageinspect/pageinspect--1.10--1.11.sql | 145 +++
contrib/pageinspect/pageinspect--1.5.sql | 2 +
contrib/pageinspect/pageinspect.control | 2 +-
contrib/pageinspect/rawpage.c | 26 +-
contrib/pg_surgery/heap_surgery.c | 10 +-
.../pg_visibility/expected/pg_visibility.out | 17 +
contrib/pg_visibility/pg_visibility.c | 3 +-
contrib/pg_visibility/sql/pg_visibility.sql | 18 +
contrib/pgrowlocks/pgrowlocks.c | 8 +-
contrib/pgstattuple/pgstatapprox.c | 1 +
contrib/pgstattuple/pgstatindex.c | 2 +-
.../postgres_fdw/expected/postgres_fdw.out | 55 +-
contrib/postgres_fdw/postgres_fdw.c | 9 +-
contrib/postgres_fdw/sql/postgres_fdw.sql | 15 +-
contrib/test_decoding/test_decoding.c | 24 +-
src/backend/access/common/heaptuple.c | 10 +-
src/backend/access/common/reloptions.c | 118 +-
src/backend/access/hash/hashvalidate.c | 5 +-
src/backend/access/heap/heapam.c | 1059 ++++++++++++++---
src/backend/access/heap/heapam_handler.c | 30 +-
src/backend/access/heap/heapam_visibility.c | 173 +--
src/backend/access/heap/heaptoast.c | 3 +
src/backend/access/heap/hio.c | 16 +-
src/backend/access/heap/pruneheap.c | 60 +-
src/backend/access/heap/rewriteheap.c | 56 +-
src/backend/access/heap/vacuumlazy.c | 116 +-
src/backend/access/nbtree/nbtpage.c | 2 +
src/backend/access/nbtree/nbtsplitloc.c | 13 +-
src/backend/access/nbtree/nbtxlog.c | 2 +
src/backend/access/rmgrdesc/clogdesc.c | 8 +-
src/backend/access/rmgrdesc/committsdesc.c | 8 +-
src/backend/access/rmgrdesc/gistdesc.c | 8 +-
src/backend/access/rmgrdesc/hashdesc.c | 2 +-
src/backend/access/rmgrdesc/heapdesc.c | 50 +-
src/backend/access/rmgrdesc/mxactdesc.c | 12 +-
src/backend/access/rmgrdesc/nbtdesc.c | 8 +-
src/backend/access/rmgrdesc/spgdesc.c | 2 +-
src/backend/access/rmgrdesc/standbydesc.c | 6 +-
src/backend/access/rmgrdesc/xactdesc.c | 16 +-
src/backend/access/rmgrdesc/xlogdesc.c | 9 +-
src/backend/access/transam/clog.c | 92 +-
src/backend/access/transam/commit_ts.c | 70 +-
src/backend/access/transam/multixact.c | 885 +++-----------
src/backend/access/transam/slru.c | 125 +-
src/backend/access/transam/subtrans.c | 51 +-
src/backend/access/transam/transam.c | 68 +-
src/backend/access/transam/twophase.c | 31 +-
src/backend/access/transam/varsup.c | 153 +--
src/backend/access/transam/xact.c | 50 +-
src/backend/access/transam/xlog.c | 40 +-
src/backend/access/transam/xlogreader.c | 34 -
src/backend/bootstrap/bootstrap.c | 29 +-
src/backend/catalog/heap.c | 8 +-
src/backend/catalog/pg_inherits.c | 2 +-
src/backend/commands/async.c | 16 +-
src/backend/commands/sequence.c | 44 +-
src/backend/commands/vacuum.c | 76 +-
src/backend/executor/execExprInterp.c | 1 +
src/backend/executor/execUtils.c | 1 +
src/backend/executor/nodeModifyTable.c | 1 +
src/backend/executor/spi.c | 1 +
src/backend/nodes/outfuncs.c | 4 +-
src/backend/optimizer/util/plancat.c | 2 +-
src/backend/postmaster/autovacuum.c | 72 +-
src/backend/replication/logical/decode.c | 20 +-
src/backend/replication/logical/logical.c | 2 +-
src/backend/replication/logical/proto.c | 4 +-
.../replication/logical/reorderbuffer.c | 29 +-
src/backend/replication/logical/snapbuild.c | 18 +-
src/backend/replication/logical/worker.c | 12 +-
src/backend/replication/walreceiver.c | 27 +-
src/backend/replication/walsender.c | 63 +-
src/backend/statistics/extended_stats.c | 1 +
src/backend/storage/buffer/Makefile | 3 +-
src/backend/storage/buffer/bufmgr.c | 19 +-
src/backend/storage/buffer/heap_convert.c | 496 ++++++++
src/backend/storage/ipc/procarray.c | 120 +-
src/backend/storage/ipc/standby.c | 10 +-
src/backend/storage/lmgr/lmgr.c | 13 +-
src/backend/storage/lmgr/predicate.c | 28 +-
src/backend/storage/page/bufpage.c | 199 +++-
src/backend/utils/adt/enum.c | 2 +-
src/backend/utils/adt/jsonfuncs.c | 2 +
src/backend/utils/adt/lockfuncs.c | 11 +-
src/backend/utils/adt/numutils.c | 22 +
src/backend/utils/adt/pgstatfuncs.c | 1 +
src/backend/utils/adt/rowtypes.c | 12 +
src/backend/utils/adt/xid.c | 27 +-
src/backend/utils/adt/xid8funcs.c | 57 +-
src/backend/utils/cache/catcache.c | 1 +
src/backend/utils/cache/relcache.c | 5 +-
src/backend/utils/error/csvlog.c | 4 +-
src/backend/utils/error/elog.c | 8 +-
src/backend/utils/error/jsonlog.c | 4 +-
src/backend/utils/fmgr/fmgr.c | 4 +-
src/backend/utils/misc/guc.c | 184 +--
src/backend/utils/misc/help_config.c | 8 +-
src/backend/utils/misc/pg_controldata.c | 3 +-
src/backend/utils/misc/postgresql.conf.sample | 4 +-
src/backend/utils/sort/tuplesort.c | 8 +-
src/backend/utils/time/combocid.c | 18 +-
src/backend/utils/time/snapmgr.c | 23 +-
src/bin/initdb/initdb.c | 67 +-
src/bin/pg_amcheck/t/004_verify_heapam.pl | 184 ++-
src/bin/pg_controldata/pg_controldata.c | 17 +-
src/bin/pg_dump/pg_dump.c | 51 +-
src/bin/pg_dump/pg_dump.h | 8 +-
src/bin/pg_resetwal/pg_resetwal.c | 143 ++-
src/bin/pg_upgrade/Makefile | 1 +
src/bin/pg_upgrade/check.c | 114 ++
src/bin/pg_upgrade/controldata.c | 28 +-
src/bin/pg_upgrade/file.c | 172 ++-
src/bin/pg_upgrade/pg_upgrade.c | 132 +-
src/bin/pg_upgrade/pg_upgrade.h | 24 +-
src/bin/pg_upgrade/segresize.c | 571 +++++++++
src/bin/pg_upgrade/test.sh | 19 +-
src/bin/pg_upgrade/util.c | 17 +
src/bin/pg_upgrade/version.c | 134 ++-
src/bin/pg_verifybackup/t/003_corruption.pl | 2 +-
src/bin/pg_waldump/pg_waldump.c | 4 +-
src/include/access/clog.h | 2 +-
src/include/access/commit_ts.h | 2 +-
src/include/access/ginblock.h | 11 +-
src/include/access/gist.h | 2 +-
src/include/access/heapam.h | 16 +-
src/include/access/heapam_xlog.h | 22 +-
src/include/access/heaptoast.h | 2 +-
src/include/access/htup.h | 16 +-
src/include/access/htup_details.h | 142 ++-
src/include/access/multixact.h | 14 +-
src/include/access/nbtree.h | 10 +
src/include/access/rewriteheap.h | 2 +-
src/include/access/rmgrlist.h | 1 +
src/include/access/slru.h | 28 +-
src/include/access/tableam.h | 2 +-
src/include/access/transam.h | 76 +-
src/include/access/xact.h | 13 +-
src/include/access/xlog.h | 8 +
src/include/access/xlogreader.h | 4 -
src/include/c.h | 44 +-
src/include/catalog/pg_amproc.dat | 4 +-
src/include/catalog/pg_control.h | 6 +
src/include/catalog/pg_operator.dat | 8 +-
src/include/catalog/pg_proc.dat | 12 +-
src/include/catalog/pg_type.dat | 4 +-
src/include/commands/vacuum.h | 28 +-
src/include/fmgr.h | 2 +
src/include/pg_config.h.in | 3 +
src/include/postgres.h | 6 +-
src/include/postmaster/autovacuum.h | 4 +-
src/include/storage/bufmgr.h | 2 +
src/include/storage/bufpage.h | 105 +-
src/include/storage/itemid.h | 2 +
src/include/storage/lock.h | 14 +-
src/include/storage/proc.h | 2 +-
src/include/storage/standby.h | 2 +-
src/include/storage/sync.h | 2 +-
src/include/utils/builtins.h | 1 +
src/include/utils/combocid.h | 2 +-
src/include/utils/rel.h | 12 +-
src/pl/plperl/plperl.c | 4 +-
src/pl/plpgsql/src/pl_comp.c | 4 +-
src/pl/plpgsql/src/pl_exec.c | 2 +
src/pl/plpython/plpy_procedure.c | 4 +-
src/pl/tcl/pltcl.c | 4 +-
src/test/Makefile | 3 +-
src/test/perl/PostgreSQL/Test/Cluster.pm | 1 +
src/test/recovery/t/003_recovery_targets.pl | 2 +-
src/test/regress/expected/indirect_toast.out | 8 +
src/test/regress/expected/insert.out | 16 +-
src/test/regress/expected/opr_sanity.out | 6 +-
src/test/regress/expected/select_views.out | 70 +-
src/test/regress/expected/txid.out | 8 +-
src/test/regress/expected/xid.out | 14 +-
src/test/regress/pg_regress.c | 2 +-
src/test/regress/regress.c | 1 +
src/test/regress/sql/indirect_toast.sql | 11 +
src/test/regress/sql/insert.sql | 17 +-
src/test/regress/sql/select_views.sql | 2 +-
src/test/xid-64/Makefile | 22 +
src/test/xid-64/README | 16 +
src/test/xid-64/t/001_test_large_xids.pl | 53 +
src/test/xid-64/t/002_test_gucs.pl | 78 ++
src/test/xid-64/t/003_test_integrity.pl | 56 +
src/tools/msvc/Solution.pm | 1 +
196 files changed, 5645 insertions(+), 2891 deletions(-)
create mode 100644 contrib/pageinspect/expected/hash_1.out
create mode 100644 contrib/pageinspect/pageinspect--1.10--1.11.sql
create mode 100644 src/backend/storage/buffer/heap_convert.c
create mode 100644 src/bin/pg_upgrade/segresize.c
create mode 100644 src/test/xid-64/Makefile
create mode 100644 src/test/xid-64/README
create mode 100644 src/test/xid-64/t/001_test_large_xids.pl
create mode 100644 src/test/xid-64/t/002_test_gucs.pl
create mode 100644 src/test/xid-64/t/003_test_integrity.pl
diff --git a/contrib/amcheck/verify_heapam.c b/contrib/amcheck/verify_heapam.c
index f996f9a5727..95ea09b931e 100644
--- a/contrib/amcheck/verify_heapam.c
+++ b/contrib/amcheck/verify_heapam.c
@@ -126,6 +126,7 @@ typedef struct HeapCheckContext
uint16 lp_len;
uint16 lp_off;
HeapTupleHeader tuphdr;
+ HeapTupleData tuple;
int natts;
/* Values for iterating over attributes within the tuple */
@@ -523,6 +524,11 @@ verify_heapam(PG_FUNCTION_ARGS)
ctx.tuphdr = (HeapTupleHeader) PageGetItem(ctx.page, ctx.itemid);
ctx.natts = HeapTupleHeaderGetNatts(ctx.tuphdr);
+ ctx.tuple.t_data = ctx.tuphdr;
+ ctx.tuple.t_len = ItemIdGetLength(ctx.itemid);
+ ctx.tuple.t_tableOid = RelationGetRelid(ctx.rel);
+ HeapTupleCopyBaseFromPage(&ctx.tuple, ctx.page);
+
/* Ok, ready to check this next tuple */
check_tuple(&ctx);
}
@@ -768,12 +774,13 @@ check_tuple_visibility(HeapCheckContext *ctx)
XidCommitStatus xmin_status;
XidCommitStatus xvac_status;
XidCommitStatus xmax_status;
+ HeapTuple tuple = &ctx->tuple;
HeapTupleHeader tuphdr = ctx->tuphdr;
ctx->tuple_could_be_pruned = true; /* have not yet proven otherwise */
/* If xmin is normal, it should be within valid range */
- xmin = HeapTupleHeaderGetXmin(tuphdr);
+ xmin = HeapTupleGetXmin(tuple);
switch (get_xid_status(xmin, ctx, &xmin_status))
{
case XID_INVALID:
@@ -781,23 +788,20 @@ check_tuple_visibility(HeapCheckContext *ctx)
break;
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("xmin %u equals or exceeds next valid transaction ID %u:%u",
+ psprintf("xmin " XID_FMT " equals or exceeds next valid transaction ID " XID_FMT,
xmin,
- EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid)));
return false;
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("xmin %u precedes oldest valid transaction ID %u:%u",
+ psprintf("xmin " XID_FMT " precedes oldest valid transaction ID " XID_FMT,
xmin,
- EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid)));
return false;
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("xmin %u precedes relation freeze threshold %u:%u",
+ psprintf("xmin " XID_FMT " precedes relation freeze threshold " XID_FMT,
xmin,
- EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid)));
return false;
}
@@ -822,23 +826,20 @@ check_tuple_visibility(HeapCheckContext *ctx)
return false;
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple equals or exceeds next valid transaction ID %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved off tuple equals or exceeds next valid transaction ID " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid)));
return false;
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple precedes relation freeze threshold %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved off tuple precedes relation freeze threshold " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid)));
return false;
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple precedes oldest valid transaction ID %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved off tuple precedes oldest valid transaction ID " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid)));
return false;
case XID_BOUNDS_OK:
@@ -849,12 +850,12 @@ check_tuple_visibility(HeapCheckContext *ctx)
{
case XID_IS_CURRENT_XID:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple matches our current transaction ID",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved off tuple matches our current transaction ID",
xvac));
return false;
case XID_IN_PROGRESS:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved off tuple appears to be in progress",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved off tuple appears to be in progress",
xvac));
return false;
@@ -891,23 +892,20 @@ check_tuple_visibility(HeapCheckContext *ctx)
return false;
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple equals or exceeds next valid transaction ID %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved in tuple equals or exceeds next valid transaction ID " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid)));
return false;
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple precedes relation freeze threshold %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved in tuple precedes relation freeze threshold " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid)));
return false;
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple precedes oldest valid transaction ID %u:%u",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved in tuple precedes oldest valid transaction ID " XID_FMT,
xvac,
- EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid)));
return false;
case XID_BOUNDS_OK:
@@ -918,12 +916,12 @@ check_tuple_visibility(HeapCheckContext *ctx)
{
case XID_IS_CURRENT_XID:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple matches our current transaction ID",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved in tuple matches our current transaction ID",
xvac));
return false;
case XID_IN_PROGRESS:
report_corruption(ctx,
- psprintf("old-style VACUUM FULL transaction ID %u for moved in tuple appears to be in progress",
+ psprintf("old-style VACUUM FULL transaction ID " XID_FMT " for moved in tuple appears to be in progress",
xvac));
return false;
@@ -985,7 +983,7 @@ check_tuple_visibility(HeapCheckContext *ctx)
* HEAP_XMAX_IS_LOCKED_ONLY is true, but for now we err on the side of
* avoiding possibly-bogus complaints about missing TOAST entries.
*/
- xmax = HeapTupleHeaderGetRawXmax(tuphdr);
+ xmax = HeapTupleGetRawXmax(tuple);
switch (check_mxid_valid_in_rel(xmax, ctx))
{
case XID_INVALID:
@@ -994,17 +992,17 @@ check_tuple_visibility(HeapCheckContext *ctx)
return true;
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("multitransaction ID %u precedes relation minimum multitransaction ID threshold %u",
+ psprintf("multitransaction ID " XID_FMT " precedes relation minimum multitransaction ID threshold " XID_FMT,
xmax, ctx->relminmxid));
return true;
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("multitransaction ID %u precedes oldest valid multitransaction ID threshold %u",
+ psprintf("multitransaction ID " XID_FMT " precedes oldest valid multitransaction ID threshold " XID_FMT,
xmax, ctx->oldest_mxact));
return true;
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("multitransaction ID %u equals or exceeds next valid multitransaction ID %u",
+ psprintf("multitransaction ID " XID_FMT " equals or exceeds next valid multitransaction ID " XID_FMT,
xmax,
ctx->next_mxact));
return true;
@@ -1042,7 +1040,7 @@ check_tuple_visibility(HeapCheckContext *ctx)
* We already checked above that this multixact is within limits for
* this table. Now check the update xid from this multixact.
*/
- xmax = HeapTupleGetUpdateXid(tuphdr);
+ xmax = HeapTupleGetUpdateXid(tuple);
switch (get_xid_status(xmax, ctx, &xmax_status))
{
case XID_INVALID:
@@ -1052,23 +1050,20 @@ check_tuple_visibility(HeapCheckContext *ctx)
return true;
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("update xid %u equals or exceeds next valid transaction ID %u:%u",
+ psprintf("update xid " XID_FMT " equals or exceeds next valid transaction ID " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid)));
return true;
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("update xid %u precedes relation freeze threshold %u:%u",
+ psprintf("update xid " XID_FMT " precedes relation freeze threshold " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid)));
return true;
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("update xid %u precedes oldest valid transaction ID %u:%u",
+ psprintf("update xid " XID_FMT " precedes oldest valid transaction ID " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid)));
return true;
case XID_BOUNDS_OK:
@@ -1109,28 +1104,25 @@ check_tuple_visibility(HeapCheckContext *ctx)
}
/* xmax is an XID, not a MXID. Sanity check it. */
- xmax = HeapTupleHeaderGetRawXmax(tuphdr);
+ xmax = HeapTupleGetRawXmax(tuple);
switch (get_xid_status(xmax, ctx, &xmax_status))
{
case XID_IN_FUTURE:
report_corruption(ctx,
- psprintf("xmax %u equals or exceeds next valid transaction ID %u:%u",
+ psprintf("xmax " XID_FMT " equals or exceeds next valid transaction ID " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->next_fxid),
XidFromFullTransactionId(ctx->next_fxid)));
return false; /* corrupt */
case XID_PRECEDES_RELMIN:
report_corruption(ctx,
- psprintf("xmax %u precedes relation freeze threshold %u:%u",
+ psprintf("xmax " XID_FMT " precedes relation freeze threshold " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->relfrozenfxid),
XidFromFullTransactionId(ctx->relfrozenfxid)));
return false; /* corrupt */
case XID_PRECEDES_CLUSTERMIN:
report_corruption(ctx,
- psprintf("xmax %u precedes oldest valid transaction ID %u:%u",
+ psprintf("xmax " XID_FMT " precedes oldest valid transaction ID " XID_FMT,
xmax,
- EpochFromFullTransactionId(ctx->oldest_fxid),
XidFromFullTransactionId(ctx->oldest_fxid)));
return false; /* corrupt */
case XID_BOUNDS_OK:
@@ -1615,21 +1607,13 @@ check_tuple(HeapCheckContext *ctx)
}
/*
- * Convert a TransactionId into a FullTransactionId using our cached values of
- * the valid transaction ID range. It is the caller's responsibility to have
- * already updated the cached values, if necessary.
+ * With 64xid this function is just wrapper for FullTransactionIdFromXid,
+ * but we left it in order for its calls remain "vanilla" like.
*/
static FullTransactionId
-FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx)
+FullTransactionIdFromXidAndCtx(TransactionId xid, const HeapCheckContext *ctx pg_attribute_unused())
{
- uint32 epoch;
-
- if (!TransactionIdIsNormal(xid))
- return FullTransactionIdFromEpochAndXid(0, xid);
- epoch = EpochFromFullTransactionId(ctx->next_fxid);
- if (xid > ctx->next_xid)
- epoch--;
- return FullTransactionIdFromEpochAndXid(epoch, xid);
+ return FullTransactionIdFromXid(xid);
}
/*
@@ -1754,7 +1738,6 @@ get_xid_status(TransactionId xid, HeapCheckContext *ctx,
* performed the full xid conversion, reconvert.
*/
update_cached_xid_range(ctx);
- fxid = FullTransactionIdFromXidAndCtx(xid, ctx);
}
if (FullTransactionIdPrecedesOrEquals(ctx->next_fxid, fxid))
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index d2510ee6480..e44e192dcef 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -498,7 +498,7 @@ bt_check_every_level(Relation rel, Relation heaprel, bool heapkeyspace,
* avoid this.
*/
if (IsolationUsesXactSnapshot() && rel->rd_index->indcheckxmin &&
- !TransactionIdPrecedes(HeapTupleHeaderGetXmin(rel->rd_indextuple->t_data),
+ !TransactionIdPrecedes(HeapTupleGetXmin(rel->rd_indextuple),
snapshot->xmin))
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c
index b3304ff8445..f54741330c5 100644
--- a/contrib/hstore/hstore_io.c
+++ b/contrib/hstore/hstore_io.c
@@ -859,6 +859,7 @@ hstore_from_record(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = rec;
+ HeapTupleSetZeroBase(&tuple);
values = (Datum *) palloc(ncolumns * sizeof(Datum));
nulls = (bool *) palloc(ncolumns * sizeof(bool));
@@ -1012,6 +1013,7 @@ hstore_populate_record(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = rec;
+ HeapTupleSetZeroBase(&tuple);
}
/*
diff --git a/contrib/pageinspect/Makefile b/contrib/pageinspect/Makefile
index 5c0736564ab..5ca80c9d766 100644
--- a/contrib/pageinspect/Makefile
+++ b/contrib/pageinspect/Makefile
@@ -13,7 +13,8 @@ OBJS = \
rawpage.o
EXTENSION = pageinspect
-DATA = pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \
+DATA = pageinspect--1.10--1.11.sql \
+ pageinspect--1.9--1.10.sql pageinspect--1.8--1.9.sql \
pageinspect--1.7--1.8.sql pageinspect--1.6--1.7.sql \
pageinspect--1.5.sql pageinspect--1.5--1.6.sql \
pageinspect--1.4--1.5.sql pageinspect--1.3--1.4.sql \
diff --git a/contrib/pageinspect/brinfuncs.c b/contrib/pageinspect/brinfuncs.c
index f1e64a39ef2..b80b9afac4d 100644
--- a/contrib/pageinspect/brinfuncs.c
+++ b/contrib/pageinspect/brinfuncs.c
@@ -45,7 +45,7 @@ Datum
brin_page_type(PG_FUNCTION_ARGS)
{
bytea *raw_page = PG_GETARG_BYTEA_P(0);
- Page page = VARDATA(raw_page);
+ Page page = get_page_from_raw(raw_page);
int raw_page_size;
char *type;
@@ -101,7 +101,7 @@ verify_brin_page(bytea *raw_page, uint16 type, const char *strtype)
errdetail("Expected size %d, got %d",
BLCKSZ, raw_page_size)));
- page = VARDATA(raw_page);
+ page = get_page_from_raw(raw_page);
/* verify the special space says this page is what we want */
if (BrinPageType(page) != type)
@@ -393,15 +393,15 @@ brin_revmap_data(PG_FUNCTION_ARGS)
MemoryContext mctx;
Page page;
- /* minimally verify the page we got */
- page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
-
/* create a function context for cross-call persistence */
fctx = SRF_FIRSTCALL_INIT();
/* switch to memory context appropriate for multiple function calls */
mctx = MemoryContextSwitchTo(fctx->multi_call_memory_ctx);
+ /* minimally verify the page we got */
+ page = verify_brin_page(raw_page, BRIN_PAGETYPE_REVMAP, "revmap");
+
state = palloc(sizeof(*state));
state->tids = ((RevmapContents *) PageGetContents(page))->rm_tids;
state->idx = 0;
diff --git a/contrib/pageinspect/btreefuncs.c b/contrib/pageinspect/btreefuncs.c
index 03debe336ba..21463cf65c4 100644
--- a/contrib/pageinspect/btreefuncs.c
+++ b/contrib/pageinspect/btreefuncs.c
@@ -105,9 +105,14 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
stat->page_size = PageGetPageSize(page);
+ stat->btpo_prev = opaque->btpo_prev;
+ stat->btpo_level = opaque->btpo_level;
+
/* page type (flags) */
if (P_ISDELETED(opaque))
{
+ TransactionId safexid;
+
/* We divide deleted pages into leaf ('d') or internal ('D') */
if (P_ISLEAF(opaque) || !P_HAS_FULLXID(opaque))
stat->type = 'd';
@@ -122,16 +127,16 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
* called "bpto").
*/
if (P_HAS_FULLXID(opaque))
+ safexid = XidFromFullTransactionId(BTPageGetDeleteXid(page));
+ else
{
- FullTransactionId safexid = BTPageGetDeleteXid(page);
-
- elog(DEBUG2, "deleted page from block %u has safexid %u:%u",
- blkno, EpochFromFullTransactionId(safexid),
- XidFromFullTransactionId(safexid));
+ safexid = BTP_GET_XACT(opaque);
+ stat->btpo_prev = 0;
+ stat->btpo_level = 0;
}
- else
- elog(DEBUG2, "deleted page from block %u has safexid %u",
- blkno, opaque->btpo_level);
+
+ elog(DEBUG2, "deleted page from block %u has safexid " XID_FMT,
+ blkno, safexid);
/* Don't interpret BTDeletedPageData as index tuples */
maxoff = InvalidOffsetNumber;
@@ -146,9 +151,7 @@ GetBTPageStatistics(BlockNumber blkno, Buffer buffer, BTPageStat *stat)
stat->type = 'i';
/* btpage opaque data */
- stat->btpo_prev = opaque->btpo_prev;
stat->btpo_next = opaque->btpo_next;
- stat->btpo_level = opaque->btpo_level;
stat->btpo_flags = opaque->btpo_flags;
stat->btpo_cycleid = opaque->btpo_cycleid;
@@ -613,7 +616,7 @@ bt_page_items_bytea(PG_FUNCTION_ARGS)
uargs = palloc(sizeof(struct user_args));
- uargs->page = VARDATA(raw_page);
+ uargs->page = get_page_from_raw(raw_page);
uargs->offset = FirstOffsetNumber;
diff --git a/contrib/pageinspect/expected/hash_1.out b/contrib/pageinspect/expected/hash_1.out
new file mode 100644
index 00000000000..5e64eb92602
--- /dev/null
+++ b/contrib/pageinspect/expected/hash_1.out
@@ -0,0 +1,166 @@
+CREATE TABLE test_hash (a int, b text);
+INSERT INTO test_hash VALUES (1, 'one');
+CREATE INDEX test_hash_a_idx ON test_hash USING hash (a);
+\x
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 0));
+-[ RECORD 1 ]--+---------
+hash_page_type | metapage
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 1));
+-[ RECORD 1 ]--+-------
+hash_page_type | bucket
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 2));
+-[ RECORD 1 ]--+-------
+hash_page_type | bucket
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 3));
+-[ RECORD 1 ]--+-------
+hash_page_type | bucket
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 4));
+-[ RECORD 1 ]--+-------
+hash_page_type | bucket
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 5));
+-[ RECORD 1 ]--+-------
+hash_page_type | bitmap
+
+SELECT hash_page_type(get_raw_page('test_hash_a_idx', 6));
+ERROR: block number 6 is out of range for relation "test_hash_a_idx"
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', -1);
+ERROR: invalid block number
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 0);
+ERROR: invalid overflow block number 0
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 1);
+ERROR: invalid overflow block number 1
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 2);
+ERROR: invalid overflow block number 2
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 3);
+ERROR: invalid overflow block number 3
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 4);
+ERROR: invalid overflow block number 4
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 5);
+ERROR: invalid overflow block number 5
+SELECT * FROM hash_bitmap_info('test_hash_a_idx', 6);
+ERROR: block number 6 is out of range for relation "test_hash_a_idx"
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 0));
+-[ RECORD 1 ]--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+magic | 105121344
+version | 4
+ntuples | 1
+bsize | 8156
+bmsize | 4096
+bmshift | 15
+maxbucket | 3
+highmask | 7
+lowmask | 3
+ovflpoint | 2
+firstfree | 0
+nmaps | 1
+procid | 450
+spares | {0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
+mapp | {5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
+
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 1));
+ERROR: page is not a hash meta page
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 2));
+ERROR: page is not a hash meta page
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 3));
+ERROR: page is not a hash meta page
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 4));
+ERROR: page is not a hash meta page
+SELECT magic, version, ntuples, bsize, bmsize, bmshift, maxbucket, highmask,
+lowmask, ovflpoint, firstfree, nmaps, procid, spares, mapp FROM
+hash_metapage_info(get_raw_page('test_hash_a_idx', 5));
+ERROR: page is not a hash meta page
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 0));
+ERROR: page is not a hash bucket or overflow page
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 1));
+-[ RECORD 1 ]---+-----------
+live_items | 0
+dead_items | 0
+page_size | 8192
+hasho_prevblkno | 3
+hasho_nextblkno | 4294967295
+hasho_bucket | 0
+hasho_flag | 2
+hasho_page_id | 65408
+
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 2));
+-[ RECORD 1 ]---+-----------
+live_items | 0
+dead_items | 0
+page_size | 8192
+hasho_prevblkno | 3
+hasho_nextblkno | 4294967295
+hasho_bucket | 1
+hasho_flag | 2
+hasho_page_id | 65408
+
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 3));
+-[ RECORD 1 ]---+-----------
+live_items | 1
+dead_items | 0
+page_size | 8192
+hasho_prevblkno | 3
+hasho_nextblkno | 4294967295
+hasho_bucket | 2
+hasho_flag | 2
+hasho_page_id | 65408
+
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 4));
+-[ RECORD 1 ]---+-----------
+live_items | 0
+dead_items | 0
+page_size | 8192
+hasho_prevblkno | 3
+hasho_nextblkno | 4294967295
+hasho_bucket | 3
+hasho_flag | 2
+hasho_page_id | 65408
+
+SELECT live_items, dead_items, page_size, hasho_prevblkno, hasho_nextblkno,
+hasho_bucket, hasho_flag, hasho_page_id FROM
+hash_page_stats(get_raw_page('test_hash_a_idx', 5));
+ERROR: page is not a hash bucket or overflow page
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 0));
+ERROR: page is not a hash bucket or overflow page
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 1));
+(0 rows)
+
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 2));
+(0 rows)
+
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 3));
+-[ RECORD 1 ]----------
+itemoffset | 1
+ctid | (0,1)
+data | 2389907270
+
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 4));
+(0 rows)
+
+SELECT * FROM hash_page_items(get_raw_page('test_hash_a_idx', 5));
+ERROR: page is not a hash bucket or overflow page
+DROP TABLE test_hash;
diff --git a/contrib/pageinspect/expected/oldextversions.out b/contrib/pageinspect/expected/oldextversions.out
index f5c4b61bd79..00323d392d6 100644
--- a/contrib/pageinspect/expected/oldextversions.out
+++ b/contrib/pageinspect/expected/oldextversions.out
@@ -40,16 +40,16 @@ SELECT * FROM bt_page_items('test1_a_idx', 1);
-- pagesize in pageinspect >= 1.10.
ALTER EXTENSION pageinspect UPDATE TO '1.9';
\df page_header
- List of functions
- Schema | Name | Result data type | Argument data types | Type
---------+-------------+------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------
- public | page_header | record | page bytea, OUT lsn pg_lsn, OUT checksum smallint, OUT flags smallint, OUT lower smallint, OUT upper smallint, OUT special smallint, OUT pagesize smallint, OUT version smallint, OUT prune_xid xid | func
+ List of functions
+ Schema | Name | Result data type | Argument data types | Type
+--------+-------------+------------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+------
+ public | page_header | record | page bytea, OUT lsn pg_lsn, OUT checksum smallint, OUT flags smallint, OUT lower smallint, OUT upper smallint, OUT special smallint, OUT pagesize smallint, OUT version smallint, OUT xid_base xid, OUT multi_base xid, OUT prune_xid xid | func
(1 row)
SELECT pagesize, version FROM page_header(get_raw_page('test1', 0));
pagesize | version
----------+---------
- 8192 | 4
+ 8192 | 5
(1 row)
DROP TABLE test1;
diff --git a/contrib/pageinspect/expected/page.out b/contrib/pageinspect/expected/page.out
index 4e325ae56dd..804be53c6dc 100644
--- a/contrib/pageinspect/expected/page.out
+++ b/contrib/pageinspect/expected/page.out
@@ -48,7 +48,7 @@ SELECT get_raw_page('test1', 0) = get_raw_page('test1', 'main', 0);
SELECT pagesize, version FROM page_header(get_raw_page('test1', 0));
pagesize | version
----------+---------
- 8192 | 4
+ 8192 | 5
(1 row)
SELECT page_checksum(get_raw_page('test1', 0), 0) IS NOT NULL AS silly_checksum_test;
@@ -69,19 +69,19 @@ SELECT tuple_data_split('test1'::regclass, t_data, t_infomask, t_infomask2, t_bi
SELECT * FROM fsm_page_contents(get_raw_page('test1', 'fsm', 0));
fsm_page_contents
-------------------
- 0: 254 +
- 1: 254 +
- 3: 254 +
- 7: 254 +
- 15: 254 +
- 31: 254 +
- 63: 254 +
- 127: 254 +
- 255: 254 +
- 511: 254 +
- 1023: 254 +
- 2047: 254 +
- 4095: 254 +
+ 0: 253 +
+ 1: 253 +
+ 3: 253 +
+ 7: 253 +
+ 15: 253 +
+ 31: 253 +
+ 63: 253 +
+ 127: 253 +
+ 255: 253 +
+ 511: 253 +
+ 1023: 253 +
+ 2047: 253 +
+ 4095: 253 +
fp_next_slot: 0 +
(1 row)
diff --git a/contrib/pageinspect/fsmfuncs.c b/contrib/pageinspect/fsmfuncs.c
index dadd62aa20a..7aa1a0558f0 100644
--- a/contrib/pageinspect/fsmfuncs.c
+++ b/contrib/pageinspect/fsmfuncs.c
@@ -44,7 +44,7 @@ fsm_page_contents(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to use raw page functions")));
- fsmpage = (FSMPage) PageGetContents(VARDATA(raw_page));
+ fsmpage = (FSMPage) PageGetContents(get_page_from_raw(raw_page));
initStringInfo(&sinfo);
diff --git a/contrib/pageinspect/heapfuncs.c b/contrib/pageinspect/heapfuncs.c
index 3dd1a9bc2ab..3a22c4794dd 100644
--- a/contrib/pageinspect/heapfuncs.c
+++ b/contrib/pageinspect/heapfuncs.c
@@ -163,7 +163,7 @@ heap_page_items(PG_FUNCTION_ARGS)
inter_call_data->tupd = tupdesc;
inter_call_data->offset = FirstOffsetNumber;
- inter_call_data->page = VARDATA(raw_page);
+ inter_call_data->page = get_page_from_raw(raw_page);
fctx->max_calls = PageGetMaxOffsetNumber(inter_call_data->page);
fctx->user_fctx = inter_call_data;
@@ -211,6 +211,7 @@ heap_page_items(PG_FUNCTION_ARGS)
lp_offset == MAXALIGN(lp_offset) &&
lp_offset + lp_len <= raw_page_size)
{
+ HeapTupleData tup;
HeapTupleHeader tuphdr;
bytea *tuple_data_bytea;
int tuple_data_len;
@@ -218,9 +219,11 @@ heap_page_items(PG_FUNCTION_ARGS)
/* Extract information from the tuple header */
tuphdr = (HeapTupleHeader) PageGetItem(page, id);
+ tup.t_data = tuphdr;
+ HeapTupleCopyBaseFromPage(&tup, page);
- values[4] = UInt32GetDatum(HeapTupleHeaderGetRawXmin(tuphdr));
- values[5] = UInt32GetDatum(HeapTupleHeaderGetRawXmax(tuphdr));
+ values[4] = TransactionIdGetDatum(HeapTupleGetXmin(&tup));
+ values[5] = TransactionIdGetDatum(HeapTupleGetRawXmax(&tup));
/* shared with xvac */
values[6] = UInt32GetDatum(HeapTupleHeaderGetRawCommandId(tuphdr));
values[7] = PointerGetDatum(&tuphdr->t_ctid);
diff --git a/contrib/pageinspect/pageinspect--1.10--1.11.sql b/contrib/pageinspect/pageinspect--1.10--1.11.sql
new file mode 100644
index 00000000000..236f18aa2f8
--- /dev/null
+++ b/contrib/pageinspect/pageinspect--1.10--1.11.sql
@@ -0,0 +1,145 @@
+/* contrib/pageinspect/pageinspect--1.10--1.11.sql */
+
+-- complain if script is sourced in psql, rather than via ALTER EXTENSION
+\echo Use "ALTER EXTENSION pageinspect UPDATE TO '1.11'" to load this file. \quit
+
+--
+-- gist_page_opaque_info()
+--
+DROP FUNCTION gist_page_opaque_info(bytea);
+CREATE FUNCTION gist_page_opaque_info(IN page bytea,
+ OUT lsn pg_lsn,
+ OUT nsn pg_lsn,
+ OUT rightlink bigint,
+ OUT flags text[])
+AS 'MODULE_PATHNAME', 'gist_page_opaque_info'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+
+--
+-- gist_page_items_bytea()
+--
+DROP FUNCTION gist_page_items_bytea(bytea);
+CREATE FUNCTION gist_page_items_bytea(IN page bytea,
+ OUT itemoffset smallint,
+ OUT ctid tid,
+ OUT itemlen smallint,
+ OUT dead boolean,
+ OUT key_data bytea)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'gist_page_items_bytea'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- gist_page_items()
+--
+DROP FUNCTION gist_page_items(bytea, regclass);
+CREATE FUNCTION gist_page_items(IN page bytea,
+ IN index_oid regclass,
+ OUT itemoffset smallint,
+ OUT ctid tid,
+ OUT itemlen smallint,
+ OUT dead boolean,
+ OUT keys text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'gist_page_items'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- get_raw_page()
+--
+DROP FUNCTION get_raw_page(text, int8);
+DROP FUNCTION IF EXISTS get_raw_page(text, int4);
+CREATE FUNCTION get_raw_page(text, int8)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'get_raw_page_1_9'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+DROP FUNCTION get_raw_page(text, text, int8);
+DROP FUNCTION IF EXISTS get_raw_page(text, text, int4);
+CREATE FUNCTION get_raw_page(text, text, int8)
+RETURNS bytea
+AS 'MODULE_PATHNAME', 'get_raw_page_fork_1_9'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- page_checksum()
+--
+DROP FUNCTION page_checksum(IN page bytea, IN blkno int8);
+DROP FUNCTION IF EXISTS page_checksum(IN page bytea, IN blkno int4);
+CREATE FUNCTION page_checksum(IN page bytea, IN blkno int8)
+RETURNS smallint
+AS 'MODULE_PATHNAME', 'page_checksum_1_9'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- bt_metap()
+--
+DROP FUNCTION bt_metap(text);
+CREATE FUNCTION bt_metap(IN relname text,
+ OUT magic int4,
+ OUT version int4,
+ OUT root int8,
+ OUT level int8,
+ OUT fastroot int8,
+ OUT fastlevel int8,
+ OUT last_cleanup_num_delpages int8,
+ OUT last_cleanup_num_tuples float8,
+ OUT allequalimage boolean)
+AS 'MODULE_PATHNAME', 'bt_metap'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- bt_page_stats()
+--
+DROP FUNCTION bt_page_stats(text, int8);
+DROP FUNCTION IF EXISTS bt_page_stats(text, int4);
+CREATE FUNCTION bt_page_stats(IN relname text, IN blkno int8,
+ OUT blkno int8,
+ OUT type "char",
+ OUT live_items int4,
+ OUT dead_items int4,
+ OUT avg_item_size int4,
+ OUT page_size int4,
+ OUT free_size int4,
+ OUT btpo_prev int8,
+ OUT btpo_next int8,
+ OUT btpo_level int8,
+ OUT btpo_flags int4)
+AS 'MODULE_PATHNAME', 'bt_page_stats_1_9'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- bt_page_items()
+--
+DROP FUNCTION bt_page_items(text, int8);
+DROP FUNCTION IF EXISTS bt_page_items(text, int4);
+CREATE FUNCTION bt_page_items(IN relname text, IN blkno int8,
+ OUT itemoffset smallint,
+ OUT ctid tid,
+ OUT itemlen smallint,
+ OUT nulls bool,
+ OUT vars bool,
+ OUT data text,
+ OUT dead boolean,
+ OUT htid tid,
+ OUT tids tid[])
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'bt_page_items_1_9'
+LANGUAGE C STRICT PARALLEL SAFE;
+
+--
+-- brin_page_items()
+--
+DROP FUNCTION brin_page_items(IN page bytea, IN index_oid regclass);
+CREATE FUNCTION brin_page_items(IN page bytea, IN index_oid regclass,
+ OUT itemoffset int,
+ OUT blknum int8,
+ OUT attnum int,
+ OUT allnulls bool,
+ OUT hasnulls bool,
+ OUT placeholder bool,
+ OUT value text)
+RETURNS SETOF record
+AS 'MODULE_PATHNAME', 'brin_page_items'
+LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/pageinspect--1.5.sql b/contrib/pageinspect/pageinspect--1.5.sql
index 1e40c3c97e2..fdbd2995a22 100644
--- a/contrib/pageinspect/pageinspect--1.5.sql
+++ b/contrib/pageinspect/pageinspect--1.5.sql
@@ -28,6 +28,8 @@ CREATE FUNCTION page_header(IN page bytea,
OUT special smallint,
OUT pagesize smallint,
OUT version smallint,
+ OUT xid_base xid,
+ OUT multi_base xid,
OUT prune_xid xid)
AS 'MODULE_PATHNAME', 'page_header'
LANGUAGE C STRICT PARALLEL SAFE;
diff --git a/contrib/pageinspect/pageinspect.control b/contrib/pageinspect/pageinspect.control
index 7cdf37913da..f277413dd8c 100644
--- a/contrib/pageinspect/pageinspect.control
+++ b/contrib/pageinspect/pageinspect.control
@@ -1,5 +1,5 @@
# pageinspect extension
comment = 'inspect the contents of database pages at a low level'
-default_version = '1.10'
+default_version = '1.11'
module_pathname = '$libdir/pageinspect'
relocatable = true
diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c
index 7e41af045f3..5caa9c100a6 100644
--- a/contrib/pageinspect/rawpage.c
+++ b/contrib/pageinspect/rawpage.c
@@ -17,6 +17,7 @@
#include "access/htup_details.h"
#include "access/relation.h"
+#include "commands/sequence.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
@@ -252,8 +253,8 @@ page_header(PG_FUNCTION_ARGS)
Datum result;
HeapTuple tuple;
- Datum values[9];
- bool nulls[9];
+ Datum values[11];
+ bool nulls[11];
PageHeader page;
XLogRecPtr lsn;
@@ -274,7 +275,7 @@ page_header(PG_FUNCTION_ARGS)
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("input page too small (%d bytes)", raw_page_size)));
- page = (PageHeader) VARDATA(raw_page);
+ page = (PageHeader) get_page_from_raw(raw_page);
/* Build a tuple descriptor for our result type */
if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
@@ -324,12 +325,29 @@ page_header(PG_FUNCTION_ARGS)
}
values[7] = UInt16GetDatum(PageGetPageLayoutVersion(page));
- values[8] = TransactionIdGetDatum(page->pd_prune_xid);
/* Build and return the tuple. */
memset(nulls, 0, sizeof(nulls));
+ if (PageGetSpecialSize(page) == MAXALIGN(sizeof(HeapPageSpecialData)))
+ {
+ HeapPageSpecial pageSpecial = HeapPageGetSpecial(page);
+
+ values[8] = TransactionIdGetDatum(pageSpecial->pd_xid_base);
+ values[9] = TransactionIdGetDatum(pageSpecial->pd_multi_base);
+ values[10] = TransactionIdGetDatum(HeapPageGetPruneXid(page));
+ nulls[8] = false;
+ nulls[9] = false;
+ nulls[10] = false;
+ }
+ else
+ {
+ nulls[8] = true;
+ nulls[9] = true;
+ nulls[10] = true;
+ }
+
tuple = heap_form_tuple(tupdesc, values, nulls);
result = HeapTupleGetDatum(tuple);
diff --git a/contrib/pg_surgery/heap_surgery.c b/contrib/pg_surgery/heap_surgery.c
index 3d8eb0abc29..84c8601f8c4 100644
--- a/contrib/pg_surgery/heap_surgery.c
+++ b/contrib/pg_surgery/heap_surgery.c
@@ -270,11 +270,17 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
else
{
HeapTupleHeader htup;
+ HeapTupleData tuple;
Assert(heap_force_opt == HEAP_FORCE_FREEZE);
htup = (HeapTupleHeader) PageGetItem(page, itemid);
+ tuple.t_data = htup;
+ tuple.t_len = ItemIdGetLength(itemid);
+ tuple.t_tableOid = RelationGetRelid(rel);
+ HeapTupleCopyBaseFromPage(&tuple, page);
+
/*
* Reset all visibility-related fields of the tuple. This
* logic should mimic heap_execute_freeze_tuple(), but we
@@ -282,8 +288,8 @@ heap_force_common(FunctionCallInfo fcinfo, HeapTupleForceOption heap_force_opt)
* potentially-garbled data is left behind.
*/
ItemPointerSet(&htup->t_ctid, blkno, curoff);
- HeapTupleHeaderSetXmin(htup, FrozenTransactionId);
- HeapTupleHeaderSetXmax(htup, InvalidTransactionId);
+ HeapTupleSetXmin(&tuple, FrozenTransactionId);
+ HeapTupleSetXmax(&tuple, InvalidTransactionId);
if (htup->t_infomask & HEAP_MOVED)
{
if (htup->t_infomask & HEAP_MOVED_OFF)
diff --git a/contrib/pg_visibility/expected/pg_visibility.out b/contrib/pg_visibility/expected/pg_visibility.out
index 9de54db2a29..32bfbaa84e0 100644
--- a/contrib/pg_visibility/expected/pg_visibility.out
+++ b/contrib/pg_visibility/expected/pg_visibility.out
@@ -267,6 +267,22 @@ select * from pg_check_frozen('copyfreeze');
--------
(0 rows)
+create table vacuum_test as select 42 i;
+vacuum (disable_page_skipping) vacuum_test;
+-- pg_check_visible() can report false positive due to autovacuum activity.
+-- To workaround this issue, repeat the call. See PGPRO-4776.
+do $$
+declare
+ non_visible_count bigint;
+ i integer;
+begin
+ for i in 1 .. 10 loop
+ if i > 1 then perform pg_sleep(1); end if;
+ select count(*) from pg_check_visible('vacuum_test') into non_visible_count;
+ if non_visible_count = 0 then exit; end if;
+ end loop;
+ if non_visible_count > 0 then raise exception 'The visibility map is corrupt.'; end if;
+end $$;
-- cleanup
drop table test_partitioned;
drop view test_view;
@@ -277,3 +293,4 @@ drop foreign data wrapper dummy;
drop materialized view matview_visibility_test;
drop table regular_table;
drop table copyfreeze;
+drop table vacuum_test;
diff --git a/contrib/pg_visibility/pg_visibility.c b/contrib/pg_visibility/pg_visibility.c
index 44331c6f496..fbd67927a42 100644
--- a/contrib/pg_visibility/pg_visibility.c
+++ b/contrib/pg_visibility/pg_visibility.c
@@ -653,6 +653,7 @@ collect_corrupt_items(Oid relid, bool all_visible, bool all_frozen)
/* Initialize a HeapTupleData structure for checks below. */
ItemPointerSet(&(tuple.t_self), blkno, offnum);
+ HeapTupleCopyBaseFromPage(&tuple, page);
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = relid;
@@ -762,7 +763,7 @@ tuple_all_visible(HeapTuple tup, TransactionId OldestXmin, Buffer buffer)
* be set here. So just check the xmin.
*/
- xmin = HeapTupleHeaderGetXmin(tup->t_data);
+ xmin = HeapTupleGetXmin(tup);
if (!TransactionIdPrecedes(xmin, OldestXmin))
return false; /* xmin not old enough for all to see */
diff --git a/contrib/pg_visibility/sql/pg_visibility.sql b/contrib/pg_visibility/sql/pg_visibility.sql
index ff3538f9964..42c4fad36ad 100644
--- a/contrib/pg_visibility/sql/pg_visibility.sql
+++ b/contrib/pg_visibility/sql/pg_visibility.sql
@@ -170,6 +170,23 @@ commit;
select * from pg_visibility_map('copyfreeze');
select * from pg_check_frozen('copyfreeze');
+create table vacuum_test as select 42 i;
+vacuum (disable_page_skipping) vacuum_test;
+-- pg_check_visible() can report false positive due to autovacuum activity.
+-- To workaround this issue, repeat the call. See PGPRO-4776.
+do $$
+declare
+ non_visible_count bigint;
+ i integer;
+begin
+ for i in 1 .. 10 loop
+ if i > 1 then perform pg_sleep(1); end if;
+ select count(*) from pg_check_visible('vacuum_test') into non_visible_count;
+ if non_visible_count = 0 then exit; end if;
+ end loop;
+ if non_visible_count > 0 then raise exception 'The visibility map is corrupt.'; end if;
+end $$;
+
-- cleanup
drop table test_partitioned;
drop view test_view;
@@ -180,3 +197,4 @@ drop foreign data wrapper dummy;
drop materialized view matview_visibility_test;
drop table regular_table;
drop table copyfreeze;
+drop table vacuum_test;
diff --git a/contrib/pgrowlocks/pgrowlocks.c b/contrib/pgrowlocks/pgrowlocks.c
index d8946dc5107..e26451c9d5a 100644
--- a/contrib/pgrowlocks/pgrowlocks.c
+++ b/contrib/pgrowlocks/pgrowlocks.c
@@ -156,7 +156,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
htsu = HeapTupleSatisfiesUpdate(tuple,
GetCurrentCommandId(false),
hscan->rs_cbuf);
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
+ xmax = HeapTupleGetRawXmax(tuple);
infomask = tuple->t_data->t_infomask;
/*
@@ -168,7 +168,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
PointerGetDatum(&tuple->t_self));
values[Atnum_xmax] = palloc(NCHARS * sizeof(char));
- snprintf(values[Atnum_xmax], NCHARS, "%u", xmax);
+ snprintf(values[Atnum_xmax], NCHARS, XID_FMT, xmax);
if (infomask & HEAP_XMAX_IS_MULTI)
{
MultiXactMember *members;
@@ -209,7 +209,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
strcat(values[Atnum_modes], ",");
strcat(values[Atnum_pids], ",");
}
- snprintf(buf, NCHARS, "%u", members[j].xid);
+ snprintf(buf, NCHARS, XID_FMT, members[j].xid);
strcat(values[Atnum_xids], buf);
switch (members[j].status)
{
@@ -250,7 +250,7 @@ pgrowlocks(PG_FUNCTION_ARGS)
values[Atnum_ismulti] = pstrdup("false");
values[Atnum_xids] = palloc(NCHARS * sizeof(char));
- snprintf(values[Atnum_xids], NCHARS, "{%u}", xmax);
+ snprintf(values[Atnum_xids], NCHARS, "{" XID_FMT "}", xmax);
values[Atnum_modes] = palloc(NCHARS);
if (infomask & HEAP_XMAX_LOCK_ONLY)
diff --git a/contrib/pgstattuple/pgstatapprox.c b/contrib/pgstattuple/pgstatapprox.c
index 15ddc322392..6d9e481013d 100644
--- a/contrib/pgstattuple/pgstatapprox.c
+++ b/contrib/pgstattuple/pgstatapprox.c
@@ -153,6 +153,7 @@ statapprox_heap(Relation rel, output_type *stat)
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
+ HeapTupleCopyBaseFromPage(&tuple, page);
/*
* We follow VACUUM's lead in counting INSERT_IN_PROGRESS tuples
diff --git a/contrib/pgstattuple/pgstatindex.c b/contrib/pgstattuple/pgstatindex.c
index 6c4b053dd07..7ed7f1381a2 100644
--- a/contrib/pgstattuple/pgstatindex.c
+++ b/contrib/pgstattuple/pgstatindex.c
@@ -605,7 +605,7 @@ pgstathashindex(PG_FUNCTION_ARGS)
metabuf = _hash_getbuf(rel, HASH_METAPAGE, HASH_READ, LH_META_PAGE);
metap = HashPageGetMeta(BufferGetPage(metabuf));
stats.version = metap->hashm_version;
- stats.space_per_page = metap->hashm_bsize;
+ stats.space_per_page = BLCKSZ - SizeOfPageHeaderData - MAXALIGN(sizeof(HashPageOpaqueData));
_hash_relbuf(rel, metabuf);
/* Get the current relation length */
diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index 7d6f7d9e3df..1ac9b34577d 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -4447,16 +4447,24 @@ UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
EXPLAIN (verbose, costs off)
-UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *; -- can be pushed down
- QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------------
- Update on public.ft2
- Output: c1, c2, c3, c4, c5, c6, c7, c8
- -> Foreign Update on public.ft2
- Remote SQL: UPDATE "S 1"."T 1" SET c2 = (c2 + 400), c3 = (c3 || '_update7') WHERE ((("C 1" % 10) = 7)) RETURNING "C 1", c2, c3, c4, c5, c6, c7, c8
-(4 rows)
+WITH t AS (UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *)
+SELECT * FROM t ORDER BY c1; -- can be pushed down
+ QUERY PLAN
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ Sort
+ Output: t.c1, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c8
+ Sort Key: t.c1
+ CTE t
+ -> Update on public.ft2
+ Output: ft2.c1, ft2.c2, ft2.c3, ft2.c4, ft2.c5, ft2.c6, ft2.c7, ft2.c8
+ -> Foreign Update on public.ft2
+ Remote SQL: UPDATE "S 1"."T 1" SET c2 = (c2 + 400), c3 = (c3 || '_update7') WHERE ((("C 1" % 10) = 7)) RETURNING "C 1", c2, c3, c4, c5, c6, c7, c8
+ -> CTE Scan on t
+ Output: t.c1, t.c2, t.c3, t.c4, t.c5, t.c6, t.c7, t.c8
+(10 rows)
-UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *;
+WITH t AS (UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *)
+SELECT * FROM t ORDER BY c1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
------+-----+--------------------+------------------------------+--------------------------+----+------------+-----
7 | 407 | 00007_update7 | Thu Jan 08 00:00:00 1970 PST | Thu Jan 08 00:00:00 1970 | 7 | 7 | foo
@@ -4576,16 +4584,24 @@ UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
EXPLAIN (verbose, costs off)
- DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4; -- can be pushed down
- QUERY PLAN
---------------------------------------------------------------------------------------------
- Delete on public.ft2
- Output: c1, c4
- -> Foreign Delete on public.ft2
- Remote SQL: DELETE FROM "S 1"."T 1" WHERE ((("C 1" % 10) = 5)) RETURNING "C 1", c4
-(4 rows)
+ WITH t AS (DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4)
+ SELECT * FROM t ORDER BY c1; -- can be pushed down
+ QUERY PLAN
+----------------------------------------------------------------------------------------------------
+ Sort
+ Output: t.c1, t.c4
+ Sort Key: t.c1
+ CTE t
+ -> Delete on public.ft2
+ Output: ft2.c1, ft2.c4
+ -> Foreign Delete on public.ft2
+ Remote SQL: DELETE FROM "S 1"."T 1" WHERE ((("C 1" % 10) = 5)) RETURNING "C 1", c4
+ -> CTE Scan on t
+ Output: t.c1, t.c4
+(10 rows)
-DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
+WITH t AS (DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4)
+SELECT * FROM t ORDER BY c1;
c1 | c4
------+------------------------------
5 | Tue Jan 06 00:00:00 1970 PST
@@ -5846,7 +5862,8 @@ INSERT INTO ft2 (c1,c2,c3,c6) VALUES (1218, 818, 'ggg', '(--;') RETURNING *;
1218 | 818 | ggg_trig_update | | | (--; | ft2 |
(1 row)
-UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *;
+WITH t AS (UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *)
+SELECT * FROM t ORDER BY c1;
c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8
------+-----+------------------------+------------------------------+--------------------------+----+------------+-----
8 | 608 | 00008_trig_update | Fri Jan 09 00:00:00 1970 PST | Fri Jan 09 00:00:00 1970 | 8 | 8 | foo
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index bf3f3d9e26e..cd81fc12c3d 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -4791,8 +4791,8 @@ apply_returning_filter(PgFdwDirectModifyState *dmstate,
* Note: no need to care about tableoid here because it will be
* initialized in ExecProcessReturning().
*/
- HeapTupleHeaderSetXmin(resultTup->t_data, InvalidTransactionId);
- HeapTupleHeaderSetXmax(resultTup->t_data, InvalidTransactionId);
+ HeapTupleSetXmin(resultTup, InvalidTransactionId);
+ HeapTupleSetXmax(resultTup, InvalidTransactionId);
HeapTupleHeaderSetCmin(resultTup->t_data, InvalidTransactionId);
}
@@ -7251,6 +7251,7 @@ make_tuple_from_result_row(PGresult *res,
*/
if (ctid)
tuple->t_self = tuple->t_data->t_ctid = *ctid;
+ HeapTupleSetZeroBase(tuple);
/*
* Stomp on the xmin, xmax, and cmin fields from the tuple created by
@@ -7260,8 +7261,8 @@ make_tuple_from_result_row(PGresult *res,
* assumption. If we don't do this then, for example, the tuple length
* ends up in the xmin field, which isn't what we want.
*/
- HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
- HeapTupleHeaderSetXmin(tuple->t_data, InvalidTransactionId);
+ HeapTupleSetXmax(tuple, InvalidTransactionId);
+ HeapTupleSetXmin(tuple, InvalidTransactionId);
HeapTupleHeaderSetCmin(tuple->t_data, InvalidTransactionId);
/* Clean up */
diff --git a/contrib/postgres_fdw/sql/postgres_fdw.sql b/contrib/postgres_fdw/sql/postgres_fdw.sql
index 9eb673e3693..d610760f35a 100644
--- a/contrib/postgres_fdw/sql/postgres_fdw.sql
+++ b/contrib/postgres_fdw/sql/postgres_fdw.sql
@@ -1243,16 +1243,20 @@ EXPLAIN (verbose, costs off)
UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3; -- can be pushed down
UPDATE ft2 SET c2 = c2 + 300, c3 = c3 || '_update3' WHERE c1 % 10 = 3;
EXPLAIN (verbose, costs off)
-UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *; -- can be pushed down
-UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *;
+WITH t AS (UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *)
+SELECT * FROM t ORDER BY c1; -- can be pushed down
+WITH t AS (UPDATE ft2 SET c2 = c2 + 400, c3 = c3 || '_update7' WHERE c1 % 10 = 7 RETURNING *)
+SELECT * FROM t ORDER BY c1;
EXPLAIN (verbose, costs off)
UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9; -- can be pushed down
UPDATE ft2 SET c2 = ft2.c2 + 500, c3 = ft2.c3 || '_update9', c7 = DEFAULT
FROM ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 9;
EXPLAIN (verbose, costs off)
- DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4; -- can be pushed down
-DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4;
+ WITH t AS (DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4)
+ SELECT * FROM t ORDER BY c1; -- can be pushed down
+WITH t AS (DELETE FROM ft2 WHERE c1 % 10 = 5 RETURNING c1, c4)
+SELECT * FROM t ORDER BY c1;
EXPLAIN (verbose, costs off)
DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2; -- can be pushed down
DELETE FROM ft2 USING ft1 WHERE ft1.c1 = ft2.c2 AND ft1.c1 % 10 = 2;
@@ -1359,7 +1363,8 @@ CREATE TRIGGER t1_br_insert BEFORE INSERT OR UPDATE
INSERT INTO ft2 (c1,c2,c3) VALUES (1208, 818, 'fff') RETURNING *;
INSERT INTO ft2 (c1,c2,c3,c6) VALUES (1218, 818, 'ggg', '(--;') RETURNING *;
-UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *;
+WITH t AS (UPDATE ft2 SET c2 = c2 + 600 WHERE c1 % 10 = 8 AND c1 < 1200 RETURNING *)
+SELECT * FROM t ORDER BY c1;
-- Test errors thrown on remote side during update
ALTER TABLE "S 1"."T 1" ADD CONSTRAINT c2positive CHECK (c2 >= 0);
diff --git a/contrib/test_decoding/test_decoding.c b/contrib/test_decoding/test_decoding.c
index 42fe91a2f9c..42d83f72c54 100644
--- a/contrib/test_decoding/test_decoding.c
+++ b/contrib/test_decoding/test_decoding.c
@@ -310,7 +310,7 @@ pg_output_begin(LogicalDecodingContext *ctx, TestDecodingData *data, ReorderBuff
{
OutputPluginPrepareWrite(ctx, last_write);
if (data->include_xids)
- appendStringInfo(ctx->out, "BEGIN %u", txn->xid);
+ appendStringInfo(ctx->out, "BEGIN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "BEGIN");
OutputPluginWrite(ctx, last_write);
@@ -333,7 +333,7 @@ pg_decode_commit_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "COMMIT %u", txn->xid);
+ appendStringInfo(ctx->out, "COMMIT " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "COMMIT");
@@ -378,7 +378,7 @@ pg_decode_prepare_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
quote_literal_cstr(txn->gid));
if (data->include_xids)
- appendStringInfo(ctx->out, ", txid %u", txn->xid);
+ appendStringInfo(ctx->out, ", txid " XID_FMT, txn->xid);
if (data->include_timestamp)
appendStringInfo(ctx->out, " (at %s)",
@@ -400,7 +400,7 @@ pg_decode_commit_prepared_txn(LogicalDecodingContext *ctx, ReorderBufferTXN *txn
quote_literal_cstr(txn->gid));
if (data->include_xids)
- appendStringInfo(ctx->out, ", txid %u", txn->xid);
+ appendStringInfo(ctx->out, ", txid " XID_FMT, txn->xid);
if (data->include_timestamp)
appendStringInfo(ctx->out, " (at %s)",
@@ -424,7 +424,7 @@ pg_decode_rollback_prepared_txn(LogicalDecodingContext *ctx,
quote_literal_cstr(txn->gid));
if (data->include_xids)
- appendStringInfo(ctx->out, ", txid %u", txn->xid);
+ appendStringInfo(ctx->out, ", txid " XID_FMT, txn->xid);
if (data->include_timestamp)
appendStringInfo(ctx->out, " (at %s)",
@@ -773,7 +773,7 @@ pg_output_stream_start(LogicalDecodingContext *ctx, TestDecodingData *data, Reor
{
OutputPluginPrepareWrite(ctx, last_write);
if (data->include_xids)
- appendStringInfo(ctx->out, "opening a streamed block for transaction TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "opening a streamed block for transaction TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "opening a streamed block for transaction");
OutputPluginWrite(ctx, last_write);
@@ -791,7 +791,7 @@ pg_decode_stream_stop(LogicalDecodingContext *ctx,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "closing a streamed block for transaction TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "closing a streamed block for transaction TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "closing a streamed block for transaction");
OutputPluginWrite(ctx, true);
@@ -825,7 +825,7 @@ pg_decode_stream_abort(LogicalDecodingContext *ctx,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "aborting streamed (sub)transaction TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "aborting streamed (sub)transaction TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "aborting streamed (sub)transaction");
OutputPluginWrite(ctx, true);
@@ -845,7 +845,7 @@ pg_decode_stream_prepare(LogicalDecodingContext *ctx,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "preparing streamed transaction TXN %s, txid %u",
+ appendStringInfo(ctx->out, "preparing streamed transaction TXN %s, txid " XID_FMT,
quote_literal_cstr(txn->gid), txn->xid);
else
appendStringInfo(ctx->out, "preparing streamed transaction %s",
@@ -876,7 +876,7 @@ pg_decode_stream_commit(LogicalDecodingContext *ctx,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "committing streamed transaction TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "committing streamed transaction TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "committing streamed transaction");
@@ -910,7 +910,7 @@ pg_decode_stream_change(LogicalDecodingContext *ctx,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "streaming change for TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "streaming change for TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "streaming change for transaction");
OutputPluginWrite(ctx, true);
@@ -963,7 +963,7 @@ pg_decode_stream_truncate(LogicalDecodingContext *ctx, ReorderBufferTXN *txn,
OutputPluginPrepareWrite(ctx, true);
if (data->include_xids)
- appendStringInfo(ctx->out, "streaming truncate for TXN %u", txn->xid);
+ appendStringInfo(ctx->out, "streaming truncate for TXN " XID_FMT, txn->xid);
else
appendStringInfoString(ctx->out, "streaming truncate for transaction");
OutputPluginWrite(ctx, true);
diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c
index 503cda46eff..a9d013d9fa5 100644
--- a/src/backend/access/common/heaptuple.c
+++ b/src/backend/access/common/heaptuple.c
@@ -640,10 +640,10 @@ heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
result = PointerGetDatum(&(tup->t_self));
break;
case MinTransactionIdAttributeNumber:
- result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmin(tup->t_data));
+ result = TransactionIdGetDatum(HeapTupleGetRawXmin(tup));
break;
case MaxTransactionIdAttributeNumber:
- result = TransactionIdGetDatum(HeapTupleHeaderGetRawXmax(tup->t_data));
+ result = TransactionIdGetDatum(HeapTupleGetRawXmax(tup));
break;
case MinCommandIdAttributeNumber:
case MaxCommandIdAttributeNumber:
@@ -688,6 +688,7 @@ heap_copytuple(HeapTuple tuple)
newTuple->t_len = tuple->t_len;
newTuple->t_self = tuple->t_self;
newTuple->t_tableOid = tuple->t_tableOid;
+ HeapTupleCopyBase(newTuple, tuple);
newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE);
memcpy((char *) newTuple->t_data, (char *) tuple->t_data, tuple->t_len);
return newTuple;
@@ -714,6 +715,7 @@ heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest)
dest->t_len = src->t_len;
dest->t_self = src->t_self;
dest->t_tableOid = src->t_tableOid;
+ HeapTupleCopyBase(dest, src);
dest->t_data = (HeapTupleHeader) palloc(src->t_len);
memcpy((char *) dest->t_data, (char *) src->t_data, src->t_len);
}
@@ -1077,6 +1079,7 @@ heap_form_tuple(TupleDesc tupleDescriptor,
tuple->t_len = len;
ItemPointerSetInvalid(&(tuple->t_self));
tuple->t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(tuple);
HeapTupleHeaderSetDatumLength(td, len);
HeapTupleHeaderSetTypeId(td, tupleDescriptor->tdtypeid);
@@ -1161,6 +1164,7 @@ heap_modify_tuple(HeapTuple tuple,
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
newTuple->t_self = tuple->t_self;
newTuple->t_tableOid = tuple->t_tableOid;
+ HeapTupleCopyBase(newTuple, tuple);
return newTuple;
}
@@ -1224,6 +1228,7 @@ heap_modify_tuple_by_cols(HeapTuple tuple,
newTuple->t_data->t_ctid = tuple->t_data->t_ctid;
newTuple->t_self = tuple->t_self;
newTuple->t_tableOid = tuple->t_tableOid;
+ HeapTupleCopyBase(newTuple, tuple);
return newTuple;
}
@@ -1464,6 +1469,7 @@ heap_tuple_from_minimal_tuple(MinimalTuple mtup)
result->t_len = len;
ItemPointerSetInvalid(&(result->t_self));
result->t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(result);
result->t_data = (HeapTupleHeader) ((char *) result + HEAPTUPLESIZE);
memcpy((char *) result->t_data + MINIMAL_TUPLE_OFFSET, mtup, mtup->t_len);
memset(result->t_data, 0, offsetof(HeapTupleHeaderData, t_infomask2));
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index e7062d4da30..4e8553b4033 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -251,58 +251,6 @@ static relopt_int intRelOpts[] =
},
-1, 1, 10000
},
- {
- {
- "autovacuum_freeze_min_age",
- "Minimum age at which VACUUM should freeze a table row, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_multixact_freeze_min_age",
- "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 0, 1000000000
- },
- {
- {
- "autovacuum_freeze_max_age",
- "Age at which to autovacuum a table to prevent transaction ID wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 100000, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_max_age",
- "Multixact age at which to autovacuum a table to prevent multixact wraparound",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- },
- -1, 10000, 2000000000
- },
- {
- {
- "autovacuum_freeze_table_age",
- "Age at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
- {
- {
- "autovacuum_multixact_freeze_table_age",
- "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
- RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
- ShareUpdateExclusiveLock
- }, -1, 0, 2000000000
- },
{
{
"log_autovacuum_min_duration",
@@ -379,6 +327,60 @@ static relopt_int intRelOpts[] =
static relopt_int64 int64RelOpts[] =
{
+ {
+ {
+ "autovacuum_freeze_min_age",
+ "Minimum age at which VACUUM should freeze a table row, for autovacuum",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(0), INT64CONST(1000000000)
+ },
+ {
+ {
+ "autovacuum_multixact_freeze_min_age",
+ "Minimum multixact age at which VACUUM should freeze a row multixact's, for autovacuum",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(0), INT64CONST(1000000000)
+ },
+ {
+ {
+ "autovacuum_freeze_max_age",
+ "Age at which to autovacuum a table to prevent transaction ID wraparound",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(100000), INT64CONST(2000000000)
+ },
+ {
+ {
+ "autovacuum_multixact_freeze_max_age",
+ "Multixact age at which to autovacuum a table to prevent multixact wraparound",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(10000), INT64CONST(2000000000)
+ },
+ {
+ {
+ "autovacuum_freeze_table_age",
+ "Age at which VACUUM should perform a full table sweep to freeze row versions",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(0), INT64CONST(2000000000)
+ },
+ {
+ {
+ "autovacuum_multixact_freeze_table_age",
+ "Age of multixact at which VACUUM should perform a full table sweep to freeze row versions",
+ RELOPT_KIND_HEAP | RELOPT_KIND_TOAST,
+ ShareUpdateExclusiveLock
+ },
+ INT64CONST(-1), INT64CONST(0), INT64CONST(2000000000)
+ },
/* list terminator */
{{NULL}}
};
@@ -1912,17 +1914,17 @@ default_reloptions(Datum reloptions, bool validate, relopt_kind kind)
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, analyze_threshold)},
{"autovacuum_vacuum_cost_limit", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, vacuum_cost_limit)},
- {"autovacuum_freeze_min_age", RELOPT_TYPE_INT,
+ {"autovacuum_freeze_min_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_min_age)},
- {"autovacuum_freeze_max_age", RELOPT_TYPE_INT,
+ {"autovacuum_freeze_max_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_max_age)},
- {"autovacuum_freeze_table_age", RELOPT_TYPE_INT,
+ {"autovacuum_freeze_table_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, freeze_table_age)},
- {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT,
+ {"autovacuum_multixact_freeze_min_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_min_age)},
- {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT,
+ {"autovacuum_multixact_freeze_max_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_max_age)},
- {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT,
+ {"autovacuum_multixact_freeze_table_age", RELOPT_TYPE_INT64,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, multixact_freeze_table_age)},
{"log_autovacuum_min_duration", RELOPT_TYPE_INT,
offsetof(StdRdOptions, autovacuum) + offsetof(AutoVacOpts, log_min_duration)},
diff --git a/src/backend/access/hash/hashvalidate.c b/src/backend/access/hash/hashvalidate.c
index 10bf26ce7c0..83e033b93cc 100644
--- a/src/backend/access/hash/hashvalidate.c
+++ b/src/backend/access/hash/hashvalidate.c
@@ -317,11 +317,10 @@ check_hash_func_signature(Oid funcid, int16 amprocnum, Oid argtype)
* INTERNAL and allowing any such function seems too scary.
*/
if ((funcid == F_HASHINT4 || funcid == F_HASHINT4EXTENDED) &&
- (argtype == DATEOID ||
- argtype == XIDOID || argtype == CIDOID))
+ (argtype == DATEOID || argtype == CIDOID))
/* okay, allowed use of hashint4() */ ;
else if ((funcid == F_HASHINT8 || funcid == F_HASHINT8EXTENDED) &&
- (argtype == XID8OID))
+ (argtype == XID8OID || argtype == XIDOID))
/* okay, allowed use of hashint8() */ ;
else if ((funcid == F_TIMESTAMP_HASH ||
funcid == F_TIMESTAMP_HASH_EXTENDED) &&
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 6ec57f3d8b2..593ccba367c 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -52,6 +52,9 @@
#include "access/xloginsert.h"
#include "access/xlogutils.h"
#include "catalog/catalog.h"
+#include "catalog/index.h"
+#include "catalog/namespace.h"
+#include "commands/vacuum.h"
#include "miscadmin.h"
#include "pgstat.h"
#include "port/atomics.h"
@@ -73,7 +76,7 @@
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
- TransactionId xid, CommandId cid, int options);
+ CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
Buffer newbuf, HeapTuple oldtup,
HeapTuple newtup, HeapTuple old_key_tuple,
@@ -214,6 +217,20 @@ static const int MultiXactStatusLock[MaxMultiXactStatus + 1] =
#define TUPLOCK_from_mxstatus(status) \
(MultiXactStatusLock[(status)])
+ShortTransactionId
+HeapPageSetPruneXidInternal(Page page, TransactionId xid)
+{
+ if (TransactionIdIsNormal(xid) &&
+ /* abuse unsigned underflow */
+ xid - HeapPageGetSpecial(page)->pd_xid_base - FirstNormalTransactionId >
+ MaxShortTransactionId)
+ elog(WARNING, "pd_prune_xid is out of range");
+ ((PageHeader) (page))->pd_prune_xid =
+ NormalTransactionIdToShort(HeapPageGetSpecial(page)->pd_xid_base, xid);
+
+ return ((PageHeader) (page))->pd_prune_xid;
+}
+
/* ----------------------------------------------------------------
* heap support routines
* ----------------------------------------------------------------
@@ -458,6 +475,7 @@ heapgetpage(TableScanDesc sscan, BlockNumber page)
loctup.t_tableOid = RelationGetRelid(scan->rs_base.rs_rd);
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
loctup.t_len = ItemIdGetLength(lpp);
+ HeapTupleCopyBaseFromPage(&loctup, dp);
ItemPointerSet(&(loctup.t_self), page, lineoff);
if (all_visible)
@@ -674,6 +692,7 @@ heapgettup(HeapScanDesc scan,
tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
tuple->t_len = ItemIdGetLength(lpp);
+ HeapTupleCopyBaseFromPage(tuple, dp);
return;
}
@@ -700,6 +719,7 @@ heapgettup(HeapScanDesc scan,
tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
tuple->t_len = ItemIdGetLength(lpp);
+ HeapTupleCopyBaseFromPage(tuple, dp);
ItemPointerSet(&(tuple->t_self), page, lineoff);
/*
@@ -999,6 +1019,7 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
tuple->t_len = ItemIdGetLength(lpp);
+ HeapTupleCopyBaseFromPage(tuple, dp);
/* check that rs_cindex is in sync */
Assert(scan->rs_cindex < scan->rs_ntuples);
@@ -1021,6 +1042,7 @@ heapgettup_pagemode(HeapScanDesc scan,
tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lpp);
tuple->t_len = ItemIdGetLength(lpp);
+ HeapTupleCopyBaseFromPage(tuple, dp);
ItemPointerSet(&(tuple->t_self), page, lineoff);
/*
@@ -1653,6 +1675,7 @@ heap_fetch(Relation relation,
tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
tuple->t_len = ItemIdGetLength(lp);
tuple->t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(tuple, page);
/*
* check tuple visibility, then release lock
@@ -1661,7 +1684,7 @@ heap_fetch(Relation relation,
if (valid)
PredicateLockTID(relation, &(tuple->t_self), snapshot,
- HeapTupleHeaderGetXmin(tuple->t_data));
+ HeapTupleGetXmin(tuple));
HeapCheckForSerializableConflictOut(valid, relation, tuple, buffer, snapshot);
@@ -1733,6 +1756,9 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
Assert(TransactionIdIsValid(RecentXmin));
Assert(BufferGetBlockNumber(buffer) == blkno);
+ heapTuple->t_self = *tid;
+ HeapTupleCopyBaseFromPage(heapTuple, dp);
+
/* Scan through possible multiple members of HOT-chain */
for (;;)
{
@@ -1768,6 +1794,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
heapTuple->t_data = (HeapTupleHeader) PageGetItem(dp, lp);
heapTuple->t_len = ItemIdGetLength(lp);
heapTuple->t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(heapTuple, dp);
ItemPointerSet(&heapTuple->t_self, blkno, offnum);
/*
@@ -1782,7 +1809,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
*/
if (TransactionIdIsValid(prev_xmax) &&
!TransactionIdEquals(prev_xmax,
- HeapTupleHeaderGetXmin(heapTuple->t_data)))
+ HeapTupleGetXmin(heapTuple)))
break;
/*
@@ -1803,7 +1830,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
{
ItemPointerSetOffsetNumber(tid, offnum);
PredicateLockTID(relation, &heapTuple->t_self, snapshot,
- HeapTupleHeaderGetXmin(heapTuple->t_data));
+ HeapTupleGetXmin(heapTuple));
if (all_dead)
*all_dead = false;
return true;
@@ -1838,7 +1865,7 @@ heap_hot_search_buffer(ItemPointer tid, Relation relation, Buffer buffer,
blkno);
offnum = ItemPointerGetOffsetNumber(&heapTuple->t_data->t_ctid);
at_chain_start = false;
- prev_xmax = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ prev_xmax = HeapTupleGetUpdateXidAny(heapTuple);
}
else
break; /* end of chain */
@@ -1925,13 +1952,14 @@ heap_get_latest_tid(TableScanDesc sscan,
tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
tp.t_len = ItemIdGetLength(lp);
tp.t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(&tp, page);
/*
* After following a t_ctid link, we might arrive at an unrelated
* tuple. Check for XMIN match.
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data)))
+ !TransactionIdEquals(priorXmax, HeapTupleGetXmin(&tp)))
{
UnlockReleaseBuffer(buffer);
break;
@@ -1950,7 +1978,7 @@ heap_get_latest_tid(TableScanDesc sscan,
* If there's a valid t_ctid link, follow it, else we're done.
*/
if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data) ||
+ HeapTupleIsOnlyLocked(&tp) ||
HeapTupleHeaderIndicatesMovedPartitions(tp.t_data) ||
ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
{
@@ -1959,7 +1987,7 @@ heap_get_latest_tid(TableScanDesc sscan,
}
ctid = tp.t_data->t_ctid;
- priorXmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ priorXmax = HeapTupleGetUpdateXidAny(&tp);
UnlockReleaseBuffer(buffer);
} /* end of loop */
}
@@ -1984,7 +2012,7 @@ heap_get_latest_tid(TableScanDesc sscan,
static void
UpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid)
{
- Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple), xid));
+ Assert(TransactionIdEquals(HeapTupleHeaderGetRawXmax(BufferGetPage(buffer), tuple), xid));
Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));
if (!(tuple->t_infomask & (HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID)))
@@ -2076,7 +2104,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* Note: below this point, heaptup is the data we actually intend to store
* into the relation; tup is the caller's original untoasted data.
*/
- heaptup = heap_prepare_insert(relation, tup, xid, cid, options);
+ heaptup = heap_prepare_insert(relation, tup, cid, options);
/*
* Find buffer to insert this tuple into. If the page is all visible,
@@ -2103,6 +2131,10 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
*/
CheckForSerializableConflictIn(relation, NULL, InvalidBlockNumber);
+ heap_page_prepare_for_xid(relation, buffer, xid, false);
+ HeapTupleCopyBaseFromPage(heaptup, BufferGetPage(buffer));
+ HeapTupleSetXmin(heaptup, xid);
+
/* NO EREPORT(ERROR) from here till changes are logged */
START_CRIT_SECTION();
@@ -2184,6 +2216,10 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
}
XLogBeginInsert();
+ if (info & XLOG_HEAP_INIT_PAGE)
+ {
+ XLogRegisterData((char *) &HeapPageGetSpecial(page)->pd_xid_base, sizeof(TransactionId));
+ }
XLogRegisterData((char *) &xlrec, SizeOfHeapInsert);
xlhdr.t_infomask2 = heaptup->t_data->t_infomask2;
@@ -2238,6 +2274,544 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
}
}
+/*
+ * Find minimum and maximum short transaction ids which occurs in the page.
+ */
+static bool
+heap_page_xid_min_max(Page page, bool multi,
+ ShortTransactionId *min,
+ ShortTransactionId *max)
+{
+ TransactionId xid_base = HeapPageGetSpecial(page)->pd_xid_base;
+ bool found = false;
+ OffsetNumber offnum,
+ maxoff;
+
+ maxoff = PageGetMaxOffsetNumber(page);
+
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum))
+ {
+ ItemId itemid;
+ HeapTupleHeader htup;
+
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsNormal(itemid))
+ continue;
+
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ if (!multi)
+ {
+ if (!HeapTupleHeaderXminFrozen(htup) &&
+ TransactionIdIsNormal(htup->t_choice.t_heap.t_xmin))
+ {
+ Assert(htup->t_choice.t_heap.t_xmin >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmin <= MaxShortTransactionId);
+
+ if (!found)
+ {
+ *min = *max = htup->t_choice.t_heap.t_xmin;
+ found = true;
+ }
+ else
+ {
+ *min = Min(*min, htup->t_choice.t_heap.t_xmin);
+ *max = Max(*max, htup->t_choice.t_heap.t_xmin);
+ }
+ }
+
+ if (htup->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ if (!(htup->t_infomask & (HEAP_XMAX_INVALID | HEAP_XMAX_LOCK_ONLY)))
+ {
+ TransactionId update_xid =
+ MultiXactIdGetUpdateXid(HeapTupleHeaderGetRawXmax(page, htup),
+ htup->t_infomask);
+ ShortTransactionId xid = NormalTransactionIdToShort(xid_base, update_xid);
+
+ Assert(xid >= FirstNormalTransactionId);
+ Assert(xid <= MaxShortTransactionId);
+
+ if (!found)
+ {
+ *min = *max = xid;
+ found = true;
+ }
+ else
+ {
+ *min = Min(*min, xid);
+ *max = Max(*max, xid);
+ }
+ }
+ }
+ else if (!(htup->t_infomask & HEAP_XMAX_INVALID) &&
+ TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax))
+ {
+ Assert(htup->t_choice.t_heap.t_xmax >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmax <= MaxShortTransactionId);
+
+ if (!found)
+ {
+ *min = *max = htup->t_choice.t_heap.t_xmax;
+ found = true;
+ }
+ else
+ {
+ *min = Min(*min, htup->t_choice.t_heap.t_xmax);
+ *max = Max(*max, htup->t_choice.t_heap.t_xmax);
+ }
+ }
+ }
+ else
+ {
+ if (TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax) &&
+ (htup->t_infomask & HEAP_XMAX_IS_MULTI))
+ {
+ Assert(htup->t_choice.t_heap.t_xmax >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmax <= MaxShortTransactionId);
+
+ if (!found)
+ {
+ *min = *max = htup->t_choice.t_heap.t_xmax;
+ found = true;
+ }
+ else
+ {
+ *min = Min(*min, htup->t_choice.t_heap.t_xmax);
+ *max = Max(*max, htup->t_choice.t_heap.t_xmax);
+ }
+ }
+ }
+ }
+ return found;
+}
+
+/*
+ * Shift xid base in the page. WAL-logged if buffer is specified.
+ */
+static void
+heap_page_shift_base(Relation relation, Buffer buffer, Page page, bool multi, int64 delta)
+{
+ HeapPageSpecial pageSpecial = HeapPageGetSpecial(page);
+ OffsetNumber offnum,
+ maxoff;
+
+ START_CRIT_SECTION();
+
+ /* Iterate over page items */
+ maxoff = PageGetMaxOffsetNumber(page);
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum))
+ {
+ ItemId itemid;
+ HeapTupleHeader htup;
+
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsNormal(itemid))
+ continue;
+
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ /* Apply xid shift to heap tuple */
+ if (!multi)
+ {
+ if (!HeapTupleHeaderXminFrozen(htup) &&
+ TransactionIdIsNormal(htup->t_choice.t_heap.t_xmin))
+ {
+ Assert(htup->t_choice.t_heap.t_xmin - delta >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmin - delta <= MaxShortTransactionId);
+ htup->t_choice.t_heap.t_xmin -= delta;
+ }
+
+ if (TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax) &&
+ !(htup->t_infomask & HEAP_XMAX_IS_MULTI))
+ {
+ Assert(htup->t_choice.t_heap.t_xmax - delta >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmax - delta <= MaxShortTransactionId);
+ htup->t_choice.t_heap.t_xmax -= delta;
+ }
+ }
+ else
+ {
+ if (TransactionIdIsNormal(htup->t_choice.t_heap.t_xmax) &&
+ (htup->t_infomask & HEAP_XMAX_IS_MULTI))
+ {
+ Assert(htup->t_choice.t_heap.t_xmax - delta >= FirstNormalTransactionId);
+ Assert(htup->t_choice.t_heap.t_xmax - delta <= MaxShortTransactionId);
+ htup->t_choice.t_heap.t_xmax -= delta;
+ }
+ }
+ }
+
+ /* Apply xid shift to base as well */
+ if (!multi)
+ pageSpecial->pd_xid_base += delta;
+ else
+ pageSpecial->pd_multi_base += delta;
+
+ if (BufferIsValid(buffer))
+ MarkBufferDirty(buffer);
+
+ /* Write WAL record if needed */
+ if (relation && RelationNeedsWAL(relation))
+ {
+ XLogRecPtr recptr;
+ xl_heap_base_shift xlrec;
+
+ xlrec.multi = multi;
+ xlrec.delta = delta;
+
+ XLogBeginInsert();
+ XLogRegisterData((char *) &xlrec, SizeOfHeapBaseShift);
+
+ XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
+
+ recptr = XLogInsert(RM_HEAP3_ID, XLOG_HEAP3_BASE_SHIFT);
+
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+}
+
+/*
+ * Freeze xids in the single heap page. Useful when we can't fit new xid even
+ * with base shift.
+ */
+static void
+freeze_single_heap_page(Relation relation, Buffer buffer)
+{
+ Page page = BufferGetPage(buffer);
+ OffsetNumber offnum,
+ maxoff;
+ HeapTupleData tuple;
+ int nfrozen = 0;
+ xl_heap_freeze_tuple *frozen;
+ TransactionId OldestXmin,
+ FreezeXid;
+ MultiXactId MultiXactCutoff;
+ GlobalVisState *vistest;
+
+ vacuum_set_xid_limits(relation, 0, 0, 0, 0,
+ &OldestXmin, &FreezeXid, NULL, &MultiXactCutoff,
+ NULL);
+
+ vistest = GlobalVisTestFor(relation);
+
+ heap_page_prune(relation, buffer, vistest,
+ InvalidTransactionId, 0,
+ false, &offnum, false);
+
+ /*
+ * Now scan the page to collect vacuumable items and check for tuples
+ * requiring freezing.
+ */
+ maxoff = PageGetMaxOffsetNumber(page);
+ frozen = palloc(sizeof(xl_heap_freeze_tuple) * MaxHeapTuplesPerPage);
+
+ /*
+ * Note: If you change anything in the loop below, also look at
+ * heap_page_is_all_visible to see if that needs to be changed.
+ */
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum))
+ {
+ ItemId itemid;
+ bool tuple_totally_frozen;
+
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsNormal(itemid))
+ continue;
+
+ Assert(ItemIdIsNormal(itemid));
+
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
+ tuple.t_len = ItemIdGetLength(itemid);
+ tuple.t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(&tuple, page);
+
+ /*
+ * Each non-removable tuple must be checked to see if it needs
+ * freezing. Note we already have exclusive buffer lock.
+ */
+ if (heap_prepare_freeze_tuple(&tuple,
+ relation->rd_rel->relfrozenxid,
+ relation->rd_rel->relminmxid,
+ FreezeXid, MultiXactCutoff,
+ &frozen[nfrozen], &tuple_totally_frozen))
+ frozen[nfrozen++].offset = offnum;
+ } /* scan along page */
+
+ /*
+ * If we froze any tuples, mark the buffer dirty, and write a WAL
+ * record recording the changes. We must log the changes to be
+ * crash-safe against future truncation of CLOG.
+ */
+ if (nfrozen > 0)
+ {
+ int i;
+
+ START_CRIT_SECTION();
+
+ MarkBufferDirty(buffer);
+
+ /* execute collected freezes */
+ for (i = 0; i < nfrozen; i++)
+ {
+ ItemId itemid;
+ HeapTupleHeader htup;
+
+ itemid = PageGetItemId(page, frozen[i].offset);
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ heap_execute_freeze_tuple_page(page, htup, &frozen[i]);
+ }
+
+ /* Now WAL-log freezing if necessary */
+ if (RelationNeedsWAL(relation))
+ {
+ XLogRecPtr recptr;
+
+ recptr = log_heap_freeze(relation, buffer, FreezeXid,
+ frozen, nfrozen);
+ PageSetLSN(page, recptr);
+ }
+
+ END_CRIT_SECTION();
+ }
+
+ return;
+}
+
+/*
+ * Ensure that given xid fits base of given page.
+ */
+bool
+heap_page_prepare_for_xid(Relation relation, Buffer buffer,
+ TransactionId xid, bool multi)
+{
+ Page page = BufferGetPage(buffer);
+ HeapPageSpecial pageSpecial = HeapPageGetSpecial(page);
+ TransactionId base;
+ bool found;
+ ShortTransactionId min,
+ max;
+ int i;
+
+ /* "Double xmax" page format doesn't require any preparation */
+ if (HeapPageIsDoubleXmax(page))
+ return false;
+
+ if (!TransactionIdIsNormal(xid))
+ return false;
+
+ for (i = 0; i < 2; i++)
+ {
+ base = multi ? pageSpecial->pd_multi_base : pageSpecial->pd_xid_base;
+
+ /* Can we already store this xid? */
+ if (xid >= base + FirstNormalTransactionId && xid <= base + MaxShortTransactionId)
+ return false;
+
+ /* Find minimum and maximum xids in the page */
+ found = heap_page_xid_min_max(page, multi, &min, &max);
+
+ /* No items on the page? */
+ if (!found)
+ {
+ int64 delta;
+
+ delta = (int64) (xid - FirstNormalTransactionId) - (int64) base;
+
+ if (xid < base + delta + FirstNormalTransactionId ||
+ xid > base + delta + MaxShortTransactionId)
+ {
+ elog(FATAL, "Fatal xid base calculation error: "
+ "xid = " XID_FMT ", "
+ "base = " XID_FMT ", "
+ "min = %u, "
+ "max = %u, "
+ "delta = " INT64_FORMAT,
+ xid, base, min, max, delta
+ );
+ }
+
+ Assert(xid >= base + delta + FirstNormalTransactionId);
+ Assert(xid <= base + delta + MaxShortTransactionId);
+
+ heap_page_shift_base(relation, buffer, page, multi, delta);
+
+ base = multi ? pageSpecial->pd_multi_base : pageSpecial->pd_xid_base;
+ Assert(xid >= base + FirstNormalTransactionId);
+ Assert(xid <= base + MaxShortTransactionId);
+ return false;
+ }
+
+ /* Can we just shift base on the page */
+ if (xid < base + FirstNormalTransactionId)
+ {
+ int64 freeDelta = MaxShortTransactionId - max,
+ requiredDelta = (base + FirstNormalTransactionId) - xid;
+
+ /* Shouldn't consider setting base less than 0 */
+ freeDelta = Min(freeDelta, base);
+
+ if (requiredDelta <= freeDelta)
+ {
+ int64 delta = - (freeDelta + requiredDelta) / 2;
+
+ if (xid < base + delta + FirstNormalTransactionId ||
+ xid > base + delta + MaxShortTransactionId)
+ {
+ elog(FATAL, "Fatal xid base calculation error: "
+ "xid = " XID_FMT ", "
+ "base = " XID_FMT ", "
+ "min = %u, "
+ "max = %u, "
+ "freeDelta = " INT64_FORMAT ", "
+ "requiredDelta = " INT64_FORMAT ", "
+ "delta = " INT64_FORMAT,
+ xid, base, min, max, freeDelta, requiredDelta, delta
+ );
+ }
+
+ Assert(xid >= base + delta + FirstNormalTransactionId);
+ Assert(xid <= base + delta + MaxShortTransactionId);
+
+ heap_page_shift_base(relation, buffer, page, multi, delta);
+
+ base = multi ? pageSpecial->pd_multi_base : pageSpecial->pd_xid_base;
+ Assert(xid >= base + FirstNormalTransactionId);
+ Assert(xid <= base + MaxShortTransactionId);
+ return true;
+ }
+ }
+ else
+ {
+ int64 freeDelta = min - FirstNormalTransactionId,
+ requiredDelta = xid - (base + MaxShortTransactionId);
+
+ Assert(xid > base + MaxShortTransactionId);
+
+ if (requiredDelta <= freeDelta)
+ {
+ int64 delta = (freeDelta + requiredDelta) / 2;
+
+ if (xid < base + delta + FirstNormalTransactionId ||
+ xid > base + delta + MaxShortTransactionId)
+ {
+ elog(FATAL, "Fatal xid base calculation error: "
+ "xid = " XID_FMT ", "
+ "base = " XID_FMT ", "
+ "min = %u, "
+ "max = %u, "
+ "freeDelta = " INT64_FORMAT ", "
+ "requiredDelta = " INT64_FORMAT ", "
+ "delta = " INT64_FORMAT,
+ xid, base, min, max, freeDelta, requiredDelta, delta
+ );
+ }
+
+ Assert(xid >= base + delta + FirstNormalTransactionId);
+ Assert(xid <= base + delta + MaxShortTransactionId);
+
+ heap_page_shift_base(relation, buffer, page, multi, delta);
+
+ base = multi ? pageSpecial->pd_multi_base : pageSpecial->pd_xid_base;
+ Assert(xid >= base + FirstNormalTransactionId);
+ Assert(xid <= base + MaxShortTransactionId);
+ return true;
+ }
+ }
+
+ if (i == 1)
+ {
+ break;
+ }
+
+ /* Have to try freeing the page... */
+ freeze_single_heap_page(relation, buffer);
+ }
+
+ elog(ERROR, "Can't fit xid into page.");
+ return false;
+}
+
+/*
+ * Ensure that given xid fits base of given page.
+ */
+bool
+rewrite_page_prepare_for_xid(Page page, TransactionId xid, bool multi)
+{
+ HeapPageSpecial pageSpecial = HeapPageGetSpecial(page);
+ TransactionId base;
+ bool found;
+ ShortTransactionId min,
+ max;
+
+ if (!TransactionIdIsNormal(xid))
+ return false;
+
+ if (!multi)
+ base = pageSpecial->pd_xid_base;
+ else
+ base = pageSpecial->pd_multi_base;
+
+ /* Can we already store this xid? */
+ if (xid >= base + FirstNormalTransactionId && xid <= base + MaxShortTransactionId)
+ return false;
+
+ /* Find minimum and maximum xids in the page */
+ found = heap_page_xid_min_max(page, multi, &min, &max);
+
+ /* No items on the page? */
+ if (!found)
+ {
+ if (!multi)
+ pageSpecial->pd_xid_base = xid - FirstNormalTransactionId;
+ else
+ pageSpecial->pd_multi_base = xid - FirstNormalTransactionId;
+ return false;
+ }
+
+ /* Can we just shift base on the page */
+ if (xid < base + FirstNormalTransactionId)
+ {
+ int64 freeDelta = MaxShortTransactionId - max,
+ requiredDelta = (base + FirstNormalTransactionId) - xid;
+
+ if (requiredDelta <= freeDelta)
+ {
+ heap_page_shift_base(NULL, InvalidBuffer,
+ page, multi, - (freeDelta + requiredDelta) / 2);
+ return true;
+ }
+ }
+ else
+ {
+ int64 freeDelta = min - FirstNormalTransactionId,
+ requiredDelta = xid - (base + MaxShortTransactionId);
+
+ if (requiredDelta <= freeDelta)
+ {
+ heap_page_shift_base(NULL, InvalidBuffer,
+ page, multi, (freeDelta + requiredDelta) / 2);
+ return true;
+ }
+ }
+
+ elog(ERROR, "Can't fit xid into page.");
+ return false;
+}
+
+
/*
* Subroutine for heap_insert(). Prepares a tuple for insertion. This sets the
* tuple header fields and toasts the tuple if necessary. Returns a toasted
@@ -2245,7 +2819,7 @@ heap_insert(Relation relation, HeapTuple tup, CommandId cid,
* that in any case, the header fields are also set in the original tuple.
*/
static HeapTuple
-heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
+heap_prepare_insert(Relation relation, HeapTuple tup,
CommandId cid, int options)
{
/*
@@ -2262,12 +2836,12 @@ heap_prepare_insert(Relation relation, HeapTuple tup, TransactionId xid,
tup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
tup->t_data->t_infomask |= HEAP_XMAX_INVALID;
- HeapTupleHeaderSetXmin(tup->t_data, xid);
+ HeapTupleSetXmin(tup, InvalidTransactionId);
if (options & HEAP_INSERT_FROZEN)
HeapTupleHeaderSetXminFrozen(tup->t_data);
HeapTupleHeaderSetCmin(tup->t_data, cid);
- HeapTupleHeaderSetXmax(tup->t_data, 0); /* for cleanliness */
+ HeapTupleSetXmax(tup, 0); /* for cleanliness */
tup->t_tableOid = RelationGetRelid(relation);
/*
@@ -2330,8 +2904,7 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
tuple = ExecFetchSlotHeapTuple(slots[i], true, NULL);
slots[i]->tts_tableOid = RelationGetRelid(relation);
tuple->t_tableOid = slots[i]->tts_tableOid;
- heaptuples[i] = heap_prepare_insert(relation, tuple, xid, cid,
- options);
+ heaptuples[i] = heap_prepare_insert(relation, tuple, cid, options);
}
/*
@@ -2387,6 +2960,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
if (starting_with_empty_page && (options & HEAP_INSERT_FROZEN))
all_frozen_set = true;
+ heap_page_prepare_for_xid(relation, buffer, xid, false);
+
/* NO EREPORT(ERROR) from here till changes are logged */
START_CRIT_SECTION();
@@ -2394,6 +2969,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
* RelationGetBufferForTuple has ensured that the first tuple fits.
* Put that on the page, and then as many other tuples as fit.
*/
+ HeapTupleCopyBaseFromPage(heaptuples[ndone], BufferGetPage(buffer));
+ HeapTupleSetXmin(heaptuples[ndone], xid);
RelationPutHeapTuple(relation, buffer, heaptuples[ndone], false);
/*
@@ -2410,6 +2987,8 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
if (PageGetHeapFreeSpace(page) < MAXALIGN(heaptup->t_len) + saveFreeSpace)
break;
+ HeapTupleCopyBaseFromPage(heaptup, BufferGetPage(buffer));
+ HeapTupleSetXmin(heaptup, xid);
RelationPutHeapTuple(relation, buffer, heaptup, false);
/*
@@ -2545,6 +3124,10 @@ heap_multi_insert(Relation relation, TupleTableSlot **slots, int ntuples,
bufflags |= REGBUF_KEEP_DATA;
XLogBeginInsert();
+ if (info & XLOG_HEAP_INIT_PAGE)
+ {
+ XLogRegisterData((char *) &HeapPageGetSpecial(page)->pd_xid_base, sizeof(TransactionId));
+ }
XLogRegisterData((char *) xlrec, tupledata - scratch.data);
XLogRegisterBuffer(0, buffer, REGBUF_STANDARD | bufflags);
@@ -2765,6 +3348,7 @@ heap_delete(Relation relation, ItemPointer tid,
tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
tp.t_len = ItemIdGetLength(lp);
tp.t_self = *tid;
+ HeapTupleCopyBaseFromPage(&tp, page);
l1:
result = HeapTupleSatisfiesUpdate(&tp, cid, buffer);
@@ -2782,7 +3366,7 @@ l1:
uint16 infomask;
/* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tp.t_data);
+ xwait = HeapTupleGetRawXmax(&tp);
infomask = tp.t_data->t_infomask;
/*
@@ -2821,13 +3405,16 @@ l1:
NULL);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ /* Copy possibly updated xid base after relocking */
+ HeapTupleCopyBaseFromPage(&tp, page);
+
/*
* If xwait had just locked the tuple then some other xact
* could update this tuple before we get to this point. Check
* for xmax change, and start over if so.
*/
if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(&tp),
xwait))
goto l1;
}
@@ -2854,13 +3441,16 @@ l1:
XactLockTableWait(xwait, relation, &(tp.t_self), XLTW_Delete);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ /* Copy possibly updated xid base after relocking */
+ HeapTupleCopyBaseFromPage(&tp, page);
+
/*
* xwait is done, but if xwait had just locked the tuple then some
* other xact could update this tuple before we get to this point.
* Check for xmax change, and start over if so.
*/
if (xmax_infomask_changed(tp.t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tp.t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(&tp),
xwait))
goto l1;
@@ -2874,7 +3464,7 @@ l1:
*/
if ((tp.t_data->t_infomask & HEAP_XMAX_INVALID) ||
HEAP_XMAX_IS_LOCKED_ONLY(tp.t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tp.t_data))
+ HeapTupleIsOnlyLocked(&tp))
result = TM_Ok;
else if (!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid))
result = TM_Updated;
@@ -2899,9 +3489,9 @@ l1:
Assert(result != TM_Updated ||
!ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid));
tmfd->ctid = tp.t_data->t_ctid;
- tmfd->xmax = HeapTupleHeaderGetUpdateXid(tp.t_data);
+ tmfd->xmax = HeapTupleGetUpdateXidAny(&tp);
if (result == TM_SelfModified)
- tmfd->cmax = HeapTupleHeaderGetCmax(tp.t_data);
+ tmfd->cmax = HeapTupleGetCmax(&tp);
else
tmfd->cmax = InvalidCommandId;
UnlockReleaseBuffer(buffer);
@@ -2924,7 +3514,7 @@ l1:
CheckForSerializableConflictIn(relation, tid, BufferGetBlockNumber(buffer));
/* replace cid with a combo CID if necessary */
- HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo);
+ HeapTupleHeaderAdjustCmax(&tp, &cid, &iscombo);
/*
* Compute replica identity tuple before entering the critical section so
@@ -2942,11 +3532,15 @@ l1:
*/
MultiXactIdSetOldestMember();
- compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(tp.t_data),
+ compute_new_xmax_infomask(HeapTupleGetRawXmax(&tp),
tp.t_data->t_infomask, tp.t_data->t_infomask2,
xid, LockTupleExclusive, true,
&new_xmax, &new_infomask, &new_infomask2);
+ heap_page_prepare_for_xid(relation, buffer, new_xmax,
+ (new_infomask & HEAP_XMAX_IS_MULTI) ? true : false);
+ HeapTupleCopyBaseFromPage(&tp, page);
+
START_CRIT_SECTION();
/*
@@ -2972,7 +3566,7 @@ l1:
tp.t_data->t_infomask |= new_infomask;
tp.t_data->t_infomask2 |= new_infomask2;
HeapTupleHeaderClearHotUpdated(tp.t_data);
- HeapTupleHeaderSetXmax(tp.t_data, new_xmax);
+ HeapTupleSetXmax(&tp, new_xmax);
HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo);
/* Make sure there is no forward chain link in t_ctid */
tp.t_data->t_ctid = tp.t_self;
@@ -3166,7 +3760,8 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
HeapTuple heaptup;
HeapTuple old_key_tuple = NULL;
bool old_key_copied = false;
- Page page;
+ Page page,
+ newpage;
BlockNumber block;
MultiXactStatus mxact_status;
Buffer buffer,
@@ -3258,9 +3853,11 @@ heap_update(Relation relation, ItemPointer otid, HeapTuple newtup,
oldtup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
oldtup.t_len = ItemIdGetLength(lp);
oldtup.t_self = *otid;
+ HeapTupleCopyBaseFromPage(&oldtup, page);
/* the new tuple is ready, except for this: */
newtup->t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(newtup, page);
/* Determine columns modified by the update. */
modified_attrs = HeapDetermineModifiedColumns(relation, interesting_attrs,
@@ -3343,7 +3940,7 @@ l2:
*/
/* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(oldtup.t_data);
+ xwait = HeapTupleGetRawXmax(&oldtup);
infomask = oldtup.t_data->t_infomask;
/*
@@ -3394,6 +3991,7 @@ l2:
checked_lockers = true;
locker_remains = remain != 0;
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(&oldtup, page);
/*
* If xwait had just locked the tuple then some other xact
@@ -3402,7 +4000,7 @@ l2:
*/
if (xmax_infomask_changed(oldtup.t_data->t_infomask,
infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(oldtup.t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(&oldtup),
xwait))
goto l2;
}
@@ -3428,7 +4026,7 @@ l2:
* subxact aborts.
*/
if (!HEAP_XMAX_IS_LOCKED_ONLY(oldtup.t_data->t_infomask))
- update_xact = HeapTupleGetUpdateXid(oldtup.t_data);
+ update_xact = HeapTupleGetUpdateXid(&oldtup);
else
update_xact = InvalidTransactionId;
@@ -3476,6 +4074,8 @@ l2:
checked_lockers = true;
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(&oldtup, page);
+
/*
* xwait is done, but if xwait had just locked the tuple then some
* other xact could update this tuple before we get to this point.
@@ -3483,7 +4083,7 @@ l2:
*/
if (xmax_infomask_changed(oldtup.t_data->t_infomask, infomask) ||
!TransactionIdEquals(xwait,
- HeapTupleHeaderGetRawXmax(oldtup.t_data)))
+ HeapTupleGetRawXmax(&oldtup)))
goto l2;
/* Otherwise check if it committed or aborted */
@@ -3520,9 +4120,9 @@ l2:
Assert(result != TM_Updated ||
!ItemPointerEquals(&oldtup.t_self, &oldtup.t_data->t_ctid));
tmfd->ctid = oldtup.t_data->t_ctid;
- tmfd->xmax = HeapTupleHeaderGetUpdateXid(oldtup.t_data);
+ tmfd->xmax = HeapTupleGetUpdateXidAny(&oldtup);
if (result == TM_SelfModified)
- tmfd->cmax = HeapTupleHeaderGetCmax(oldtup.t_data);
+ tmfd->cmax = HeapTupleGetCmax(&oldtup);
else
tmfd->cmax = InvalidCommandId;
UnlockReleaseBuffer(buffer);
@@ -3552,6 +4152,7 @@ l2:
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
visibilitymap_pin(relation, block, &vmbuffer);
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(&oldtup, page);
goto l2;
}
@@ -3561,7 +4162,7 @@ l2:
* If the tuple we're updating is locked, we need to preserve the locking
* info in the old tuple's Xmax. Prepare a new Xmax value for this.
*/
- compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data),
+ compute_new_xmax_infomask(HeapTupleGetRawXmax(&oldtup),
oldtup.t_data->t_infomask,
oldtup.t_data->t_infomask2,
xid, *lockmode, true,
@@ -3580,7 +4181,7 @@ l2:
(checked_lockers && !locker_remains))
xmax_new_tuple = InvalidTransactionId;
else
- xmax_new_tuple = HeapTupleHeaderGetRawXmax(oldtup.t_data);
+ xmax_new_tuple = HeapTupleGetRawXmax(&oldtup);
if (!TransactionIdIsValid(xmax_new_tuple))
{
@@ -3613,17 +4214,15 @@ l2:
*/
newtup->t_data->t_infomask &= ~(HEAP_XACT_MASK);
newtup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK);
- HeapTupleHeaderSetXmin(newtup->t_data, xid);
HeapTupleHeaderSetCmin(newtup->t_data, cid);
newtup->t_data->t_infomask |= HEAP_UPDATED | infomask_new_tuple;
newtup->t_data->t_infomask2 |= infomask2_new_tuple;
- HeapTupleHeaderSetXmax(newtup->t_data, xmax_new_tuple);
/*
* Replace cid with a combo CID if necessary. Note that we already put
* the plain cid into the new tuple.
*/
- HeapTupleHeaderAdjustCmax(oldtup.t_data, &cid, &iscombo);
+ HeapTupleHeaderAdjustCmax(&oldtup, &cid, &iscombo);
/*
* If the toaster needs to be activated, OR if the new tuple will not fit
@@ -3653,7 +4252,7 @@ l2:
newtupsize = MAXALIGN(newtup->t_len);
- if (need_toast || newtupsize > pagefree)
+ if (need_toast || newtupsize > pagefree || HeapPageIsDoubleXmax(page))
{
TransactionId xmax_lock_old_tuple;
uint16 infomask_lock_old_tuple,
@@ -3678,7 +4277,7 @@ l2:
* updating, because the potentially created multixact would otherwise
* be wrong.
*/
- compute_new_xmax_infomask(HeapTupleHeaderGetRawXmax(oldtup.t_data),
+ compute_new_xmax_infomask(HeapTupleGetRawXmax(&oldtup),
oldtup.t_data->t_infomask,
oldtup.t_data->t_infomask2,
xid, *lockmode, false,
@@ -3687,6 +4286,10 @@ l2:
Assert(HEAP_XMAX_IS_LOCKED_ONLY(infomask_lock_old_tuple));
+ heap_page_prepare_for_xid(relation, buffer, xmax_lock_old_tuple,
+ (infomask_lock_old_tuple & HEAP_XMAX_IS_MULTI) ? true : false);
+ HeapTupleCopyBaseFromPage(&oldtup, page);
+
START_CRIT_SECTION();
/* Clear obsolete visibility flags ... */
@@ -3695,9 +4298,9 @@ l2:
HeapTupleClearHotUpdated(&oldtup);
/* ... and store info about transaction updating this tuple */
Assert(TransactionIdIsValid(xmax_lock_old_tuple));
- HeapTupleHeaderSetXmax(oldtup.t_data, xmax_lock_old_tuple);
oldtup.t_data->t_infomask |= infomask_lock_old_tuple;
oldtup.t_data->t_infomask2 |= infomask2_lock_old_tuple;
+ HeapTupleSetXmax(&oldtup, xmax_lock_old_tuple);
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* temporarily make it look not-updated, but locked */
@@ -3780,7 +4383,11 @@ l2:
*/
for (;;)
{
- if (newtupsize > pagefree)
+ /*
+ * We can't fit new tuple to "double xmax" page, since it's impossible to
+ * set xmin there.
+ */
+ if (newtupsize > pagefree || HeapPageIsDoubleXmax(page))
{
/* It doesn't fit, must use RelationGetBufferForTuple. */
newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,
@@ -3813,6 +4420,9 @@ l2:
break;
}
}
+
+ /* Copy possibly updated xid base to old tuple after relocking */
+ HeapTupleCopyBaseFromPage(&oldtup, page);
}
else
{
@@ -3870,6 +4480,33 @@ l2:
bms_overlap(modified_attrs, id_attrs),
&old_key_copied);
+ newpage = BufferGetPage(newbuf);
+
+ /*
+ * Prepare pages for the current xid, that witten to the new tuple's
+ * Xmax and old page's pd_prune_xid.
+ */
+ heap_page_prepare_for_xid(relation, buffer, xid, false);
+ if (newbuf != buffer)
+ heap_page_prepare_for_xid(relation, newbuf, xid, false);
+
+ /* Prepare pages for tuple's Xmax */
+ heap_page_prepare_for_xid(relation, buffer, xmax_old_tuple,
+ (infomask_old_tuple & HEAP_XMAX_IS_MULTI) ? true : false);
+ heap_page_prepare_for_xid(relation, newbuf, xmax_new_tuple,
+ (heaptup->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? true : false);
+
+ /* Copy possibly updated Xid bases to the both tuples. */
+ HeapTupleCopyBaseFromPage(&oldtup, page);
+ HeapTupleCopyBaseFromPage(heaptup, newpage);
+
+ /*
+ * Set new tuple's Xmin/Xmax, old tuple's Xmin/Xmax were already
+ * shifted.
+ */
+ HeapTupleSetXmin(heaptup, xid);
+ HeapTupleSetXmax(heaptup, xmax_new_tuple);
+
/* NO EREPORT(ERROR) from here till changes are logged */
START_CRIT_SECTION();
@@ -3912,9 +4549,9 @@ l2:
oldtup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
/* ... and store info about transaction updating this tuple */
Assert(TransactionIdIsValid(xmax_old_tuple));
- HeapTupleHeaderSetXmax(oldtup.t_data, xmax_old_tuple);
oldtup.t_data->t_infomask |= infomask_old_tuple;
oldtup.t_data->t_infomask2 |= infomask2_old_tuple;
+ HeapTupleSetXmax(&oldtup, xmax_old_tuple);
HeapTupleHeaderSetCmax(oldtup.t_data, cid, iscombo);
/* record address of new tuple in t_ctid of old one */
@@ -3969,6 +4606,18 @@ l2:
END_CRIT_SECTION();
+ if (newtup != heaptup)
+ {
+ /*
+ * Set new tuple's Xmin/Xmax only after both xid base preparations.
+ * Old tuple's Xmin/Xmax were already shifted because old tuple is
+ * on the page.
+ */
+ HeapTupleCopyBase(newtup, heaptup);
+ HeapTupleSetXmin(newtup, xid);
+ HeapTupleSetXmax(newtup, xmax_new_tuple);
+ }
+
if (newbuf != buffer)
LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);
LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
@@ -4267,6 +4916,7 @@ heap_lock_tuple(Relation relation, HeapTuple tuple,
tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
tuple->t_len = ItemIdGetLength(lp);
tuple->t_tableOid = RelationGetRelid(relation);
+ HeapTupleCopyBaseFromPage(tuple, page);
l3:
result = HeapTupleSatisfiesUpdate(tuple, cid, *buffer);
@@ -4293,7 +4943,7 @@ l3:
ItemPointerData t_ctid;
/* must copy state data before unlocking buffer */
- xwait = HeapTupleHeaderGetRawXmax(tuple->t_data);
+ xwait = HeapTupleGetRawXmax(tuple);
infomask = tuple->t_data->t_infomask;
infomask2 = tuple->t_data->t_infomask2;
ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid);
@@ -4451,11 +5101,13 @@ l3:
result = res;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
}
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
/*
* Make sure it's still an appropriate lock, else start over.
@@ -4464,7 +5116,7 @@ l3:
* now need to follow the update chain to lock the new
* versions.
*/
- if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) &&
+ if (!HeapTupleIsOnlyLocked(tuple) &&
((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) ||
!updated))
goto l3;
@@ -4491,6 +5143,7 @@ l3:
!HEAP_XMAX_IS_EXCL_LOCKED(infomask))
{
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
/*
* Make sure it's still an appropriate lock, else start over.
@@ -4519,8 +5172,10 @@ l3:
* meantime, start over.
*/
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
+
if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(tuple),
xwait))
goto l3;
@@ -4531,10 +5186,11 @@ l3:
else if (HEAP_XMAX_IS_KEYSHR_LOCKED(infomask))
{
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
/* if the xmax changed in the meantime, start over */
if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(tuple),
xwait))
goto l3;
/* otherwise, we're good */
@@ -4559,8 +5215,10 @@ l3:
{
/* ... but if the xmax changed in the meantime, start over */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
+
if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(tuple),
xwait))
goto l3;
Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask));
@@ -4581,6 +5239,7 @@ l3:
if (require_sleep && (result == TM_Updated || result == TM_Deleted))
{
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
else if (require_sleep)
@@ -4606,6 +5265,7 @@ l3:
result = TM_WouldBlock;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
@@ -4632,6 +5292,7 @@ l3:
result = TM_WouldBlock;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
break;
@@ -4672,6 +5333,7 @@ l3:
result = TM_WouldBlock;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
break;
@@ -4698,11 +5360,13 @@ l3:
result = res;
/* recovery code expects to have buffer lock held */
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto failed;
}
}
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
/*
* xwait is done, but if xwait had just locked the tuple then some
@@ -4710,7 +5374,7 @@ l3:
* Check for xmax change, and start over if so.
*/
if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) ||
- !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data),
+ !TransactionIdEquals(HeapTupleGetRawXmax(tuple),
xwait))
goto l3;
@@ -4738,7 +5402,7 @@ l3:
if (!require_sleep ||
(tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple->t_data))
+ HeapTupleIsOnlyLocked(tuple))
result = TM_Ok;
else if (!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid))
result = TM_Updated;
@@ -4764,9 +5428,9 @@ failed:
Assert(result != TM_Updated ||
!ItemPointerEquals(&tuple->t_self, &tuple->t_data->t_ctid));
tmfd->ctid = tuple->t_data->t_ctid;
- tmfd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+ tmfd->xmax = HeapTupleGetUpdateXidAny(tuple);
if (result == TM_SelfModified)
- tmfd->cmax = HeapTupleHeaderGetCmax(tuple->t_data);
+ tmfd->cmax = HeapTupleGetCmax(tuple);
else
tmfd->cmax = InvalidCommandId;
goto out_locked;
@@ -4786,10 +5450,11 @@ failed:
LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);
visibilitymap_pin(relation, block, &vmbuffer);
LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);
+ HeapTupleCopyBaseFromPage(tuple, page);
goto l3;
}
- xmax = HeapTupleHeaderGetRawXmax(tuple->t_data);
+ xmax = HeapTupleGetRawXmax(tuple);
old_infomask = tuple->t_data->t_infomask;
/*
@@ -4811,6 +5476,10 @@ failed:
GetCurrentTransactionId(), mode, false,
&xid, &new_infomask, &new_infomask2);
+ heap_page_prepare_for_xid(relation, *buffer, xid,
+ (new_infomask & HEAP_XMAX_IS_MULTI) ? true : false);
+ HeapTupleCopyBaseFromPage(tuple, page);
+
START_CRIT_SECTION();
/*
@@ -4829,7 +5498,7 @@ failed:
tuple->t_data->t_infomask2 |= new_infomask2;
if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask))
HeapTupleHeaderClearHotUpdated(tuple->t_data);
- HeapTupleHeaderSetXmax(tuple->t_data, xid);
+ HeapTupleSetXmax(tuple, xid);
/*
* Make sure there is no forward chain link in t_ctid. Note that in the
@@ -5145,7 +5814,7 @@ l5:
* TransactionIdIsInProgress() should have returned false. We
* assume it's no longer locked in this case.
*/
- elog(WARNING, "LOCK_ONLY found for Xid in progress %u", xmax);
+ elog(WARNING, "LOCK_ONLY found for Xid in progress " XID_FMT, xmax);
old_infomask |= HEAP_XMAX_INVALID;
old_infomask &= ~HEAP_XMAX_LOCK_ONLY;
goto l5;
@@ -5422,12 +6091,18 @@ l4:
LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
}
+ /*
+ * Copy xid base after buffer relocking, it could have changed
+ * since heap_fetch().
+ */
+ HeapTupleCopyBaseFromPage(&mytup, BufferGetPage(buf));
+
/*
* Check the tuple XMIN against prior XMAX, if any. If we reached the
* end of the chain, we're done, so return success.
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(mytup.t_data),
+ !TransactionIdEquals(HeapTupleGetXmin(&mytup),
priorXmax))
{
result = TM_Ok;
@@ -5439,7 +6114,7 @@ l4:
* (sub)transaction, then we already locked the last live one in the
* chain, thus we're done, so return success.
*/
- if (TransactionIdDidAbort(HeapTupleHeaderGetXmin(mytup.t_data)))
+ if (TransactionIdDidAbort(HeapTupleGetXmin(&mytup)))
{
result = TM_Ok;
goto out_locked;
@@ -5447,7 +6122,7 @@ l4:
old_infomask = mytup.t_data->t_infomask;
old_infomask2 = mytup.t_data->t_infomask2;
- xmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ xmax = HeapTupleGetRawXmax(&mytup);
/*
* If this tuple version has been updated or locked by some concurrent
@@ -5460,7 +6135,7 @@ l4:
TransactionId rawxmax;
bool needwait;
- rawxmax = HeapTupleHeaderGetRawXmax(mytup.t_data);
+ rawxmax = HeapTupleGetRawXmax(&mytup);
if (old_infomask & HEAP_XMAX_IS_MULTI)
{
int nmembers;
@@ -5601,14 +6276,18 @@ l4:
VISIBILITYMAP_ALL_FROZEN))
cleared_all_frozen = true;
+ heap_page_prepare_for_xid(rel, buf, new_xmax,
+ (new_infomask & HEAP_XMAX_IS_MULTI) ? true : false);
+ HeapTupleCopyBaseFromPage(&mytup, BufferGetPage(buf));
+
START_CRIT_SECTION();
/* ... and set them */
- HeapTupleHeaderSetXmax(mytup.t_data, new_xmax);
mytup.t_data->t_infomask &= ~HEAP_XMAX_BITS;
mytup.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED;
mytup.t_data->t_infomask |= new_infomask;
mytup.t_data->t_infomask2 |= new_infomask2;
+ HeapTupleSetXmax(&mytup, new_xmax);
MarkBufferDirty(buf);
@@ -5642,14 +6321,14 @@ next:
if (mytup.t_data->t_infomask & HEAP_XMAX_INVALID ||
HeapTupleHeaderIndicatesMovedPartitions(mytup.t_data) ||
ItemPointerEquals(&mytup.t_self, &mytup.t_data->t_ctid) ||
- HeapTupleHeaderIsOnlyLocked(mytup.t_data))
+ HeapTupleIsOnlyLocked(&mytup))
{
result = TM_Ok;
goto out_locked;
}
/* tail recursion */
- priorXmax = HeapTupleHeaderGetUpdateXid(mytup.t_data);
+ priorXmax = HeapTupleGetUpdateXidAny(&mytup);
ItemPointerCopy(&(mytup.t_data->t_ctid), &tupid);
UnlockReleaseBuffer(buf);
}
@@ -5856,12 +6535,13 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
tp.t_data = (HeapTupleHeader) PageGetItem(page, lp);
tp.t_len = ItemIdGetLength(lp);
tp.t_self = *tid;
+ HeapTupleCopyBaseFromPage(&tp, page);
/*
* Sanity check that the tuple really is a speculatively inserted tuple,
* inserted by us.
*/
- if (tp.t_data->t_choice.t_heap.t_xmin != xid)
+ if (HeapTupleGetRawXmin(&tp) != xid)
elog(ERROR, "attempted to kill a tuple inserted by another transaction");
if (!(IsToastRelation(relation) || HeapTupleHeaderIsSpeculative(tp.t_data)))
elog(ERROR, "attempted to kill a non-speculative tuple");
@@ -5890,6 +6570,8 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
prune_xid = relation->rd_rel->relfrozenxid;
else
prune_xid = TransactionXmin;
+ Assert(TransactionIdIsValid(prune_xid));
+ heap_page_prepare_for_xid(relation, buffer, prune_xid, false);
PageSetPrunable(page, prune_xid);
/* store transaction information of xact deleting the tuple */
@@ -5900,8 +6582,10 @@ heap_abort_speculative(Relation relation, ItemPointer tid)
* Set the tuple header xmin to InvalidTransactionId. This makes the
* tuple immediately invisible everyone. (In particular, to any
* transactions waiting on the speculative token, woken up later.)
+ * Don't need to reload xid base from page because InvalidTransactionId
+ * doesn't require xid base to be valid.
*/
- HeapTupleHeaderSetXmin(tp.t_data, InvalidTransactionId);
+ HeapTupleSetXmin(&tp, InvalidTransactionId);
/* Clear the speculative insertion token too */
tp.t_data->t_ctid = tp.t_self;
@@ -6120,7 +6804,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
else if (MultiXactIdPrecedes(multi, relminmxid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found multixact %u from before relminmxid %u",
+ errmsg_internal("found multixact " XID_FMT " from before relminmxid " XID_FMT,
multi, relminmxid)));
else if (MultiXactIdPrecedes(multi, cutoff_multi))
{
@@ -6134,7 +6818,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
HEAP_XMAX_IS_LOCKED_ONLY(t_infomask)))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("multixact %u from before cutoff %u found to be still running",
+ errmsg_internal("multixact " XID_FMT " from before cutoff " XID_FMT " found to be still running",
multi, cutoff_multi)));
if (HEAP_XMAX_IS_LOCKED_ONLY(t_infomask))
@@ -6153,7 +6837,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if (TransactionIdPrecedes(xid, relfrozenxid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found update xid %u from before relfrozenxid %u",
+ errmsg_internal("found update xid " XID_FMT " from before relfrozenxid " XID_FMT,
xid, relfrozenxid)));
/*
@@ -6165,7 +6849,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if (TransactionIdDidCommit(xid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("cannot freeze committed update xid %u", xid)));
+ errmsg_internal("cannot freeze committed update xid " XID_FMT, xid)));
*flags |= FRM_INVALIDATE_XMAX;
xid = InvalidTransactionId; /* not strictly necessary */
}
@@ -6223,7 +6907,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* to keep.
*/
nnewmembers = 0;
- newmembers = palloc(sizeof(MultiXactMember) * nmembers);
+ newmembers = palloc0(sizeof(MultiXactMember) * nmembers);
has_lockers = false;
update_xid = InvalidTransactionId;
update_committed = false;
@@ -6241,7 +6925,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
if (TransactionIdPrecedes(xid, relfrozenxid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found update xid %u from before relfrozenxid %u",
+ errmsg_internal("found update xid " XID_FMT " from before relfrozenxid " XID_FMT,
xid, relfrozenxid)));
/*
@@ -6291,7 +6975,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
TransactionIdPrecedes(update_xid, cutoff_xid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found update xid %u from before xid cutoff %u",
+ errmsg_internal("found update xid " XID_FMT " from before xid cutoff " XID_FMT,
update_xid, cutoff_xid)));
/*
@@ -6389,7 +7073,7 @@ FreezeMultiXactId(MultiXactId multi, uint16 t_infomask,
* recovery. We really need to remove old xids.
*/
bool
-heap_prepare_freeze_tuple(HeapTupleHeader tuple,
+heap_prepare_freeze_tuple(HeapTuple htup,
TransactionId relfrozenxid, TransactionId relminmxid,
TransactionId cutoff_xid, TransactionId cutoff_multi,
xl_heap_freeze_tuple *frz, bool *totally_frozen_p)
@@ -6399,11 +7083,12 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
bool xmin_frozen;
bool freeze_xmax;
TransactionId xid;
+ HeapTupleHeader tuple = htup->t_data;
frz->frzflags = 0;
frz->t_infomask2 = tuple->t_infomask2;
frz->t_infomask = tuple->t_infomask;
- frz->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ frz->xmax = HeapTupleGetRawXmax(htup);
/*
* Process xmin. xmin_frozen has two slightly different meanings: in the
@@ -6413,7 +7098,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
* otherwise). In both cases we're allowed to set totally_frozen, as far
* as xmin is concerned.
*/
- xid = HeapTupleHeaderGetXmin(tuple);
+ xid = HeapTupleGetXmin(htup);
if (!TransactionIdIsNormal(xid))
xmin_frozen = true;
else
@@ -6421,7 +7106,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
if (TransactionIdPrecedes(xid, relfrozenxid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found xmin %u from before relfrozenxid %u",
+ errmsg_internal("found xmin " XID_FMT " from before relfrozenxid " XID_FMT,
xid, relfrozenxid)));
xmin_frozen = TransactionIdPrecedes(xid, cutoff_xid);
@@ -6430,7 +7115,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
if (!TransactionIdDidCommit(xid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("uncommitted xmin %u from before xid cutoff %u needs to be frozen",
+ errmsg_internal("uncommitted xmin " XID_FMT " from before xid cutoff " XID_FMT " needs to be frozen",
xid, cutoff_xid)));
frz->t_infomask |= HEAP_XMIN_FROZEN;
@@ -6447,7 +7132,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
*
* Make sure to keep heap_tuple_needs_freeze in sync with this.
*/
- xid = HeapTupleHeaderGetRawXmax(tuple);
+ xid = HeapTupleGetRawXmax(htup);
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
@@ -6502,7 +7187,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
if (TransactionIdPrecedes(xid, relfrozenxid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found xmax %u from before relfrozenxid %u",
+ errmsg_internal("found xmax " XID_FMT " from before relfrozenxid " XID_FMT,
xid, relfrozenxid)));
if (TransactionIdPrecedes(xid, cutoff_xid))
@@ -6517,7 +7202,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
TransactionIdDidCommit(xid))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("cannot freeze committed xmax %u",
+ errmsg_internal("cannot freeze committed xmax " XID_FMT,
xid)));
freeze_xmax = true;
}
@@ -6525,7 +7210,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
freeze_xmax = false;
}
else if ((tuple->t_infomask & HEAP_XMAX_INVALID) ||
- !TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ !TransactionIdIsValid(HeapTupleGetRawXmax(htup)))
{
freeze_xmax = false;
xmax_already_frozen = true;
@@ -6533,7 +7218,7 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
else
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("found xmax %u (infomask 0x%04x) not frozen, not multi, not normal",
+ errmsg_internal("found xmax " XID_FMT " (infomask 0x%04x) not frozen, not multi, not normal",
xid, tuple->t_infomask)));
if (freeze_xmax)
@@ -6618,18 +7303,35 @@ heap_prepare_freeze_tuple(HeapTupleHeader tuple,
* NB: All code in here must be safe to execute during crash recovery!
*/
void
-heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
+heap_execute_freeze_tuple(HeapTuple htup, xl_heap_freeze_tuple *frz)
{
- HeapTupleHeaderSetXmax(tuple, frz->xmax);
+ HeapTupleHeader tuple = htup->t_data;
+
+ tuple->t_infomask = frz->t_infomask;
+ tuple->t_infomask2 = frz->t_infomask2;
+
+ HeapTupleSetXmax(htup, frz->xmax);
if (frz->frzflags & XLH_FREEZE_XVAC)
HeapTupleHeaderSetXvac(tuple, FrozenTransactionId);
if (frz->frzflags & XLH_INVALID_XVAC)
HeapTupleHeaderSetXvac(tuple, InvalidTransactionId);
+}
+void
+heap_execute_freeze_tuple_page(Page page, HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
+{
tuple->t_infomask = frz->t_infomask;
tuple->t_infomask2 = frz->t_infomask2;
+
+ HeapTupleHeaderSetXmax(page, tuple, frz->xmax);
+
+ if (frz->frzflags & XLH_FREEZE_XVAC)
+ HeapTupleHeaderSetXvac(tuple, FrozenTransactionId);
+
+ if (frz->frzflags & XLH_INVALID_XVAC)
+ HeapTupleHeaderSetXvac(tuple, InvalidTransactionId);
}
/*
@@ -6639,7 +7341,7 @@ heap_execute_freeze_tuple(HeapTupleHeader tuple, xl_heap_freeze_tuple *frz)
* Useful for callers like CLUSTER that perform their own WAL logging.
*/
bool
-heap_freeze_tuple(HeapTupleHeader tuple,
+heap_freeze_tuple(HeapTuple tuple,
TransactionId relfrozenxid, TransactionId relminmxid,
TransactionId cutoff_xid, TransactionId cutoff_multi)
{
@@ -6803,10 +7505,10 @@ MultiXactIdGetUpdateXid(TransactionId xmax, uint16 t_infomask)
* checking the hint bits.
*/
TransactionId
-HeapTupleGetUpdateXid(HeapTupleHeader tuple)
+HeapTupleGetUpdateXid(HeapTuple tuple)
{
- return MultiXactIdGetUpdateXid(HeapTupleHeaderGetRawXmax(tuple),
- tuple->t_infomask);
+ return MultiXactIdGetUpdateXid(HeapTupleGetRawXmax(tuple),
+ tuple->t_data->t_infomask);
}
/*
@@ -7037,12 +7739,16 @@ bool
heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
{
TransactionId xid;
+ HeapTupleData htup;
+
+ htup.t_data = tuple;
+ HeapTupleSetZeroBase(&htup);
/*
* If xmin is a normal transaction ID, this tuple is definitely not
* frozen.
*/
- xid = HeapTupleHeaderGetXmin(tuple);
+ xid = HeapTupleGetXmin(&htup);
if (TransactionIdIsNormal(xid))
return true;
@@ -7053,13 +7759,13 @@ heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
{
MultiXactId multi;
- multi = HeapTupleHeaderGetRawXmax(tuple);
+ multi = HeapTupleGetRawXmax(&htup);
if (MultiXactIdIsValid(multi))
return true;
}
else
{
- xid = HeapTupleHeaderGetRawXmax(tuple);
+ xid = HeapTupleGetRawXmax(&htup);
if (TransactionIdIsNormal(xid))
return true;
}
@@ -7087,12 +7793,13 @@ heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple)
* on a standby.
*/
bool
-heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+heap_tuple_needs_freeze(HeapTuple htup, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf)
{
TransactionId xid;
+ HeapTupleHeader tuple = htup->t_data;
- xid = HeapTupleHeaderGetXmin(tuple);
+ xid = HeapTupleGetXmin(htup);
if (TransactionIdIsNormal(xid) &&
TransactionIdPrecedes(xid, cutoff_xid))
return true;
@@ -7106,7 +7813,7 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
{
MultiXactId multi;
- multi = HeapTupleHeaderGetRawXmax(tuple);
+ multi = HeapTupleGetRawXmax(htup);
if (!MultiXactIdIsValid(multi))
{
/* no xmax set, ignore */
@@ -7141,7 +7848,7 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
}
else
{
- xid = HeapTupleHeaderGetRawXmax(tuple);
+ xid = HeapTupleGetRawXmax(htup);
if (TransactionIdIsNormal(xid) &&
TransactionIdPrecedes(xid, cutoff_xid))
return true;
@@ -7166,14 +7873,14 @@ heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
* with queries.
*/
void
-HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
+HeapTupleHeaderAdvanceLatestRemovedXid(HeapTuple tuple,
TransactionId *latestRemovedXid)
{
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetUpdateXid(tuple);
- TransactionId xvac = HeapTupleHeaderGetXvac(tuple);
+ TransactionId xmin = HeapTupleGetXmin(tuple);
+ TransactionId xmax = HeapTupleGetUpdateXidAny(tuple);
+ TransactionId xvac = HeapTupleHeaderGetXvac(tuple->t_data);
- if (tuple->t_infomask & HEAP_MOVED)
+ if (tuple->t_data->t_infomask & HEAP_MOVED)
{
if (TransactionIdPrecedes(*latestRemovedXid, xvac))
*latestRemovedXid = xvac;
@@ -7185,8 +7892,8 @@ HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
*
* Look for a committed hint bit, or if no xmin bit is set, check clog.
*/
- if (HeapTupleHeaderXminCommitted(tuple) ||
- (!HeapTupleHeaderXminInvalid(tuple) && TransactionIdDidCommit(xmin)))
+ if (HeapTupleHeaderXminCommitted(tuple->t_data) ||
+ (!HeapTupleHeaderXminInvalid(tuple->t_data) && TransactionIdDidCommit(xmin)))
{
if (xmax != xmin &&
TransactionIdFollows(xmax, *latestRemovedXid))
@@ -7536,7 +8243,7 @@ heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
for (;;)
{
ItemId lp;
- HeapTupleHeader htup;
+ HeapTupleData htup;
/* Sanity check (pure paranoia) */
if (offnum < FirstOffsetNumber)
@@ -7573,16 +8280,18 @@ heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
if (!ItemIdIsNormal(lp))
break;
- htup = (HeapTupleHeader) PageGetItem(page, lp);
+ htup.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ htup.t_len = ItemIdGetLength(lp);
+ HeapTupleCopyBaseFromPage(&htup, page);
/*
* Check the tuple XMIN against prior XMAX, if any
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
+ !TransactionIdEquals(HeapTupleGetXmin(&htup), priorXmax))
break;
- HeapTupleHeaderAdvanceLatestRemovedXid(htup, &latestRemovedXid);
+ HeapTupleHeaderAdvanceLatestRemovedXid(&htup, &latestRemovedXid);
/*
* If the tuple is not HOT-updated, then we are at the end of this
@@ -7590,13 +8299,13 @@ heap_index_delete_tuples(Relation rel, TM_IndexDeleteOp *delstate)
* chain (they get their own index entries) -- just move on to
* next htid from index AM caller.
*/
- if (!HeapTupleHeaderIsHotUpdated(htup))
+ if (!HeapTupleHeaderIsHotUpdated(htup.t_data))
break;
/* Advance to next HOT chain member */
- Assert(ItemPointerGetBlockNumber(&htup->t_ctid) == blkno);
- offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
- priorXmax = HeapTupleHeaderGetUpdateXid(htup);
+ Assert(ItemPointerGetBlockNumber(&htup.t_data->t_ctid) == blkno);
+ offnum = ItemPointerGetOffsetNumber(&htup.t_data->t_ctid);
+ priorXmax = HeapTupleGetUpdateXidAny(&htup);
}
/* Enable further/final shrinking of deltids for caller */
@@ -8176,13 +8885,13 @@ log_heap_update(Relation reln, Buffer oldbuf,
/* Prepare WAL data for the old page */
xlrec.old_offnum = ItemPointerGetOffsetNumber(&oldtup->t_self);
- xlrec.old_xmax = HeapTupleHeaderGetRawXmax(oldtup->t_data);
+ xlrec.old_xmax = HeapTupleGetRawXmax(oldtup);
xlrec.old_infobits_set = compute_infobits(oldtup->t_data->t_infomask,
oldtup->t_data->t_infomask2);
/* Prepare WAL data for the new page */
xlrec.new_offnum = ItemPointerGetOffsetNumber(&newtup->t_self);
- xlrec.new_xmax = HeapTupleHeaderGetRawXmax(newtup->t_data);
+ xlrec.new_xmax = HeapTupleGetRawXmax(newtup);
bufflags = REGBUF_STANDARD;
if (init)
@@ -8194,6 +8903,10 @@ log_heap_update(Relation reln, Buffer oldbuf,
if (oldbuf != newbuf)
XLogRegisterBuffer(1, oldbuf, REGBUF_STANDARD);
+ if (info & XLOG_HEAP_INIT_PAGE)
+ {
+ XLogRegisterData((char *) &HeapPageGetSpecial(page)->pd_xid_base, sizeof(TransactionId));
+ }
XLogRegisterData((char *) &xlrec, SizeOfHeapUpdate);
/*
@@ -8306,8 +9019,8 @@ log_heap_new_cid(Relation relation, HeapTuple tup)
{
Assert(!(hdr->t_infomask & HEAP_XMAX_INVALID));
Assert(!HeapTupleHeaderXminInvalid(hdr));
- xlrec.cmin = HeapTupleHeaderGetCmin(hdr);
- xlrec.cmax = HeapTupleHeaderGetCmax(hdr);
+ xlrec.cmin = HeapTupleGetCmin(tup);
+ xlrec.cmax = HeapTupleGetCmax(tup);
xlrec.combocid = HeapTupleHeaderGetRawCommandId(hdr);
}
/* No combo CID, so only cmin or cmax can be set by this TX */
@@ -8510,7 +9223,8 @@ heap_xlog_prune(XLogReaderState *record)
heap_page_prune_execute(buffer,
redirected, nredirected,
nowdead, ndead,
- nowunused, nunused);
+ nowunused, nunused,
+ true);
/*
* Note: we don't worry about updating the page's prunability hints.
@@ -8802,7 +9516,7 @@ heap_xlog_freeze_page(XLogReaderState *record)
lp = PageGetItemId(page, xlrec_tp->offset); /* offsets are one-based */
tuple = (HeapTupleHeader) PageGetItem(page, lp);
- heap_execute_freeze_tuple(tuple, xlrec_tp);
+ heap_execute_freeze_tuple_page(page, tuple, xlrec_tp);
}
PageSetLSN(page, lsn);
@@ -8889,9 +9603,9 @@ heap_xlog_delete(XLogReaderState *record)
fix_infomask_from_infobits(xlrec->infobits_set,
&htup->t_infomask, &htup->t_infomask2);
if (!(xlrec->flags & XLH_DELETE_IS_SUPER))
- HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+ HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
else
- HeapTupleHeaderSetXmin(htup, InvalidTransactionId);
+ HeapTupleHeaderSetXmin(page, htup, InvalidTransactionId);
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
/* Mark the page as a candidate for pruning */
@@ -8916,7 +9630,7 @@ static void
heap_xlog_insert(XLogReaderState *record)
{
XLogRecPtr lsn = record->EndRecPtr;
- xl_heap_insert *xlrec = (xl_heap_insert *) XLogRecGetData(record);
+ xl_heap_insert *xlrec;
Buffer buffer;
Page page;
union
@@ -8932,6 +9646,17 @@ heap_xlog_insert(XLogReaderState *record)
BlockNumber blkno;
ItemPointerData target_tid;
XLogRedoAction action;
+ bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
+ TransactionId pd_xid_base = InvalidTransactionId;
+ Pointer rec_data = (Pointer) XLogRecGetData(record);
+
+ if (isinit)
+ {
+ pd_xid_base = *((TransactionId *) rec_data);
+ rec_data += sizeof(TransactionId);
+ }
+
+ xlrec = (xl_heap_insert *) rec_data;
XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
ItemPointerSetBlockNumber(&target_tid, blkno);
@@ -8956,11 +9681,12 @@ heap_xlog_insert(XLogReaderState *record)
* If we inserted the first and only tuple on the page, re-initialize the
* page from scratch.
*/
- if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
+ if (isinit)
{
buffer = XLogInitBufferForRedo(record, 0);
page = BufferGetPage(buffer);
- PageInit(page, BufferGetPageSize(buffer), 0);
+ PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = pd_xid_base;
action = BLK_NEEDS_REDO;
}
else
@@ -8992,7 +9718,7 @@ heap_xlog_insert(XLogReaderState *record)
htup->t_infomask2 = xlhdr.t_infomask2;
htup->t_infomask = xlhdr.t_infomask;
htup->t_hoff = xlhdr.t_hoff;
- HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
+ HeapTupleHeaderSetXmin(page, htup, XLogRecGetXid(record));
HeapTupleHeaderSetCmin(htup, FirstCommandId);
htup->t_ctid = target_tid;
@@ -9052,12 +9778,19 @@ heap_xlog_multi_insert(XLogReaderState *record)
int i;
bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
XLogRedoAction action;
+ TransactionId pd_xid_base = InvalidTransactionId;
+ Pointer rec_data = (Pointer) XLogRecGetData(record);
/*
* Insertion doesn't overwrite MVCC data, so no conflict processing is
* required.
*/
- xlrec = (xl_heap_multi_insert *) XLogRecGetData(record);
+ if (isinit)
+ {
+ pd_xid_base = *((TransactionId *) rec_data);
+ rec_data += sizeof(TransactionId);
+ }
+ xlrec = (xl_heap_multi_insert *) rec_data;
XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
@@ -9084,7 +9817,8 @@ heap_xlog_multi_insert(XLogReaderState *record)
{
buffer = XLogInitBufferForRedo(record, 0);
page = BufferGetPage(buffer);
- PageInit(page, BufferGetPageSize(buffer), 0);
+ PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = pd_xid_base;
action = BLK_NEEDS_REDO;
}
else
@@ -9135,7 +9869,7 @@ heap_xlog_multi_insert(XLogReaderState *record)
htup->t_infomask2 = xlhdr->t_infomask2;
htup->t_infomask = xlhdr->t_infomask;
htup->t_hoff = xlhdr->t_hoff;
- HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
+ HeapTupleHeaderSetXmin(page, htup, XLogRecGetXid(record));
HeapTupleHeaderSetCmin(htup, FirstCommandId);
ItemPointerSetBlockNumber(&htup->t_ctid, blkno);
ItemPointerSetOffsetNumber(&htup->t_ctid, offnum);
@@ -9183,7 +9917,7 @@ static void
heap_xlog_update(XLogReaderState *record, bool hot_update)
{
XLogRecPtr lsn = record->EndRecPtr;
- xl_heap_update *xlrec = (xl_heap_update *) XLogRecGetData(record);
+ xl_heap_update *xlrec;
RelFileNode rnode;
BlockNumber oldblk;
BlockNumber newblk;
@@ -9208,6 +9942,17 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
Size freespace = 0;
XLogRedoAction oldaction;
XLogRedoAction newaction;
+ bool isinit = (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE) != 0;
+ TransactionId pd_xid_base = InvalidTransactionId;
+ Pointer rec_data = (Pointer) XLogRecGetData(record);
+
+ if (isinit)
+ {
+ pd_xid_base = *((TransactionId *) rec_data);
+ rec_data += sizeof(TransactionId);
+ }
+
+ xlrec = (xl_heap_update *) rec_data;
/* initialize to keep the compiler quiet */
oldtup.t_data = NULL;
@@ -9266,6 +10011,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
oldtup.t_data = htup;
oldtup.t_len = ItemIdGetLength(lp);
+ HeapTupleCopyBaseFromPage(&oldtup, page);
htup->t_infomask &= ~(HEAP_XMAX_BITS | HEAP_MOVED);
htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
@@ -9275,7 +10021,7 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
HeapTupleHeaderClearHotUpdated(htup);
fix_infomask_from_infobits(xlrec->old_infobits_set, &htup->t_infomask,
&htup->t_infomask2);
- HeapTupleHeaderSetXmax(htup, xlrec->old_xmax);
+ HeapTupleHeaderSetXmax(page, htup, xlrec->old_xmax);
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
/* Set forward chain link in t_ctid */
htup->t_ctid = newtid;
@@ -9298,11 +10044,12 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
nbuffer = obuffer;
newaction = oldaction;
}
- else if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
+ else if (isinit)
{
nbuffer = XLogInitBufferForRedo(record, 0);
page = (Page) BufferGetPage(nbuffer);
- PageInit(page, BufferGetPageSize(nbuffer), 0);
+ PageInit(page, BufferGetPageSize(nbuffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = pd_xid_base;
newaction = BLK_NEEDS_REDO;
}
else
@@ -9408,9 +10155,9 @@ heap_xlog_update(XLogReaderState *record, bool hot_update)
htup->t_infomask = xlhdr.t_infomask;
htup->t_hoff = xlhdr.t_hoff;
- HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
+ HeapTupleHeaderSetXmin(page, htup, XLogRecGetXid(record));
HeapTupleHeaderSetCmin(htup, FirstCommandId);
- HeapTupleHeaderSetXmax(htup, xlrec->new_xmax);
+ HeapTupleHeaderSetXmax(page, htup, xlrec->new_xmax);
/* Make sure there is no forward chain link in t_ctid */
htup->t_ctid = newtid;
@@ -9549,7 +10296,7 @@ heap_xlog_lock(XLogReaderState *record)
BufferGetBlockNumber(buffer),
offnum);
}
- HeapTupleHeaderSetXmax(htup, xlrec->locking_xid);
+ HeapTupleHeaderSetXmax(page, htup, xlrec->locking_xid);
HeapTupleHeaderSetCmax(htup, FirstCommandId, false);
PageSetLSN(page, lsn);
MarkBufferDirty(buffer);
@@ -9609,7 +10356,7 @@ heap_xlog_lock_updated(XLogReaderState *record)
htup->t_infomask2 &= ~HEAP_KEYS_UPDATED;
fix_infomask_from_infobits(xlrec->infobits_set, &htup->t_infomask,
&htup->t_infomask2);
- HeapTupleHeaderSetXmax(htup, xlrec->xmax);
+ HeapTupleHeaderSetXmax(page, htup, xlrec->xmax);
PageSetLSN(page, lsn);
MarkBufferDirty(buffer);
@@ -9659,6 +10406,31 @@ heap_xlog_inplace(XLogReaderState *record)
UnlockReleaseBuffer(buffer);
}
+static void
+heap_xlog_base_shift(XLogReaderState *record)
+{
+ XLogRecPtr lsn = record->EndRecPtr;
+ xl_heap_base_shift *xlrec = (xl_heap_base_shift *) XLogRecGetData(record);
+ Buffer buffer;
+ Page page;
+ BlockNumber blkno;
+ RelFileNode target_node;
+
+ XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
+
+ if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
+ {
+ page = BufferGetPage(buffer);
+ heap_page_shift_base(NULL, InvalidBuffer, page, xlrec->multi, xlrec->delta);
+ PageSetLSN(page, lsn);
+ MarkBufferDirty(buffer);
+ }
+
+ if (BufferIsValid(buffer))
+ UnlockReleaseBuffer(buffer);
+}
+
+
void
heap_redo(XLogReaderState *record)
{
@@ -9745,6 +10517,21 @@ heap2_redo(XLogReaderState *record)
}
}
+void
+heap3_redo(XLogReaderState *record)
+{
+ uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+ switch (info & XLOG_HEAP_OPMASK)
+ {
+ case XLOG_HEAP3_BASE_SHIFT:
+ heap_xlog_base_shift(record);
+ break;
+ default:
+ elog(PANIC, "heap3_redo: unknown op code %u", info);
+ }
+}
+
/*
* Mask a heap page before performing consistency checks on it.
*/
@@ -9757,6 +10544,10 @@ heap_mask(char *pagedata, BlockNumber blkno)
mask_page_lsn_and_checksum(page);
mask_page_hint_bits(page);
+
+ /* Ignore prune_xid (it's like a hint-bit) */
+ HeapPageSetPruneXid(page, InvalidTransactionId);
+
mask_unused_space(page);
for (off = 1; off <= PageGetMaxOffsetNumber(page); off++)
@@ -9872,14 +10663,14 @@ HeapCheckForSerializableConflictOut(bool visible, Relation relation,
case HEAPTUPLE_LIVE:
if (visible)
return;
- xid = HeapTupleHeaderGetXmin(tuple->t_data);
+ xid = HeapTupleGetXmin(tuple);
break;
case HEAPTUPLE_RECENTLY_DEAD:
case HEAPTUPLE_DELETE_IN_PROGRESS:
if (visible)
- xid = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+ xid = HeapTupleGetUpdateXidAny(tuple);
else
- xid = HeapTupleHeaderGetXmin(tuple->t_data);
+ xid = HeapTupleGetXmin(tuple);
if (TransactionIdPrecedes(xid, TransactionXmin))
{
@@ -9889,7 +10680,7 @@ HeapCheckForSerializableConflictOut(bool visible, Relation relation,
}
break;
case HEAPTUPLE_INSERT_IN_PROGRESS:
- xid = HeapTupleHeaderGetXmin(tuple->t_data);
+ xid = HeapTupleGetXmin(tuple);
break;
case HEAPTUPLE_DEAD:
Assert(!visible);
diff --git a/src/backend/access/heap/heapam_handler.c b/src/backend/access/heap/heapam_handler.c
index 39ef8a0b77d..93e153773a1 100644
--- a/src/backend/access/heap/heapam_handler.c
+++ b/src/backend/access/heap/heapam_handler.c
@@ -413,7 +413,7 @@ tuple_lock_retry:
* changes in an existing tuple, except to invalid or
* frozen, and neither of those can match priorXmax.)
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
+ if (!TransactionIdEquals(HeapTupleGetXmin(tuple),
priorXmax))
{
ReleaseBuffer(buffer);
@@ -424,7 +424,7 @@ tuple_lock_retry:
if (TransactionIdIsValid(SnapshotDirty.xmin))
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg_internal("t_xmin %u is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
+ errmsg_internal("t_xmin " XID_FMT " is uncommitted in tuple (%u,%u) to be updated in table \"%s\"",
SnapshotDirty.xmin,
ItemPointerGetBlockNumber(&tuple->t_self),
ItemPointerGetOffsetNumber(&tuple->t_self),
@@ -473,7 +473,7 @@ tuple_lock_retry:
* variable instead of doing HeapTupleHeaderGetXmin again.
*/
if (TransactionIdIsCurrentTransactionId(priorXmax) &&
- HeapTupleHeaderGetCmin(tuple->t_data) >= cid)
+ HeapTupleGetCmin(tuple) >= cid)
{
tmfd->xmax = priorXmax;
@@ -481,7 +481,7 @@ tuple_lock_retry:
* Cmin is the problematic value, so store that. See
* above.
*/
- tmfd->cmax = HeapTupleHeaderGetCmin(tuple->t_data);
+ tmfd->cmax = HeapTupleGetCmin(tuple);
ReleaseBuffer(buffer);
return TM_SelfModified;
}
@@ -506,7 +506,7 @@ tuple_lock_retry:
/*
* As above, if xmin isn't what we're expecting, do nothing.
*/
- if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple->t_data),
+ if (!TransactionIdEquals(HeapTupleGetXmin(tuple),
priorXmax))
{
if (BufferIsValid(buffer))
@@ -539,7 +539,7 @@ tuple_lock_retry:
/* updated, so look at the updated row */
*tid = tuple->t_data->t_ctid;
/* updated row should have xmin matching this xmax */
- priorXmax = HeapTupleHeaderGetUpdateXid(tuple->t_data);
+ priorXmax = HeapTupleGetUpdateXidAny(tuple);
if (BufferIsValid(buffer))
ReleaseBuffer(buffer);
/* loop back to fetch next in chain */
@@ -860,7 +860,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
* case we had better copy it.
*/
if (!is_system_catalog &&
- !TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data)))
+ !TransactionIdIsCurrentTransactionId(HeapTupleGetXmin(tuple)))
elog(WARNING, "concurrent insert in progress within table \"%s\"",
RelationGetRelationName(OldHeap));
/* treat as live */
@@ -872,7 +872,7 @@ heapam_relation_copy_for_cluster(Relation OldHeap, Relation NewHeap,
* Similar situation to INSERT_IN_PROGRESS case.
*/
if (!is_system_catalog &&
- !TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple->t_data)))
+ !TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXidAny(tuple)))
elog(WARNING, "concurrent delete in progress within table \"%s\"",
RelationGetRelationName(OldHeap));
/* treat as recently dead */
@@ -1057,6 +1057,7 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
targtuple->t_tableOid = RelationGetRelid(scan->rs_rd);
targtuple->t_data = (HeapTupleHeader) PageGetItem(targpage, itemid);
targtuple->t_len = ItemIdGetLength(itemid);
+ HeapTupleCopyBaseFromPage(targtuple, targpage);
switch (HeapTupleSatisfiesVacuum(targtuple, OldestXmin,
hscan->rs_cbuf))
@@ -1092,7 +1093,7 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
* numbers we send to the stats collector to make this come
* out right.)
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(targtuple->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetXmin(targtuple)))
{
sample_it = true;
*liverows += 1;
@@ -1123,7 +1124,7 @@ heapam_scan_analyze_next_tuple(TableScanDesc scan, TransactionId OldestXmin,
* but not the post-image. We also get sane results if the
* concurrent transaction never commits.
*/
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(targtuple->t_data)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXidAny(targtuple)))
*deadrows += 1;
else
{
@@ -1465,7 +1466,7 @@ heapam_index_build_range_scan(Relation heapRelation,
* before commit there. Give a warning if neither case
* applies.
*/
- xwait = HeapTupleHeaderGetXmin(heapTuple->t_data);
+ xwait = HeapTupleGetXmin(heapTuple);
if (!TransactionIdIsCurrentTransactionId(xwait))
{
if (!is_system_catalog)
@@ -1524,7 +1525,7 @@ heapam_index_build_range_scan(Relation heapRelation,
break;
}
- xwait = HeapTupleHeaderGetUpdateXid(heapTuple->t_data);
+ xwait = HeapTupleGetUpdateXidAny(heapTuple);
if (!TransactionIdIsCurrentTransactionId(xwait))
{
if (!is_system_catalog)
@@ -2202,13 +2203,14 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
loctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
loctup.t_len = ItemIdGetLength(lp);
loctup.t_tableOid = scan->rs_rd->rd_id;
+ HeapTupleCopyBaseFromPage(&loctup, dp);
ItemPointerSet(&loctup.t_self, page, offnum);
valid = HeapTupleSatisfiesVisibility(&loctup, snapshot, buffer);
if (valid)
{
hscan->rs_vistuples[ntup++] = offnum;
PredicateLockTID(scan->rs_rd, &loctup.t_self, snapshot,
- HeapTupleHeaderGetXmin(loctup.t_data));
+ HeapTupleGetXmin(&loctup));
}
HeapCheckForSerializableConflictOut(valid, scan->rs_rd, &loctup,
buffer, snapshot);
@@ -2247,6 +2249,7 @@ heapam_scan_bitmap_next_tuple(TableScanDesc scan,
hscan->rs_ctup.t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);
hscan->rs_ctup.t_len = ItemIdGetLength(lp);
hscan->rs_ctup.t_tableOid = scan->rs_rd->rd_id;
+ HeapTupleCopyBaseFromPage(&hscan->rs_ctup, dp);
ItemPointerSet(&hscan->rs_ctup.t_self, hscan->rs_cblock, targoffset);
pgstat_count_heap_fetch(scan->rs_rd);
@@ -2387,6 +2390,7 @@ heapam_scan_sample_next_tuple(TableScanDesc scan, SampleScanState *scanstate,
tuple->t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple->t_len = ItemIdGetLength(itemid);
+ HeapTupleCopyBaseFromPage(tuple, page);
ItemPointerSet(&(tuple->t_self), blockno, tupoffset);
diff --git a/src/backend/access/heap/heapam_visibility.c b/src/backend/access/heap/heapam_visibility.c
index ceadac70d57..5adfb9d12c9 100644
--- a/src/backend/access/heap/heapam_visibility.c
+++ b/src/backend/access/heap/heapam_visibility.c
@@ -217,7 +217,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
@@ -229,7 +229,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
{
TransactionId xmax;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -241,7 +241,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
return false;
}
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
/* deleting subtransaction must have aborted */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -251,11 +251,11 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
return false;
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleGetRawXmin(htup)))
return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmin(htup)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
+ HeapTupleGetRawXmin(htup));
else
{
/* it must have aborted or crashed */
@@ -284,7 +284,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return true;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -299,17 +299,17 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
return true;
}
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return true;
return false;
}
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsInProgress(HeapTupleGetRawXmax(htup)))
return true;
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdDidCommit(HeapTupleGetRawXmax(htup)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -327,7 +327,7 @@ HeapTupleSatisfiesSelf(HeapTuple htup, Snapshot snapshot, Buffer buffer)
}
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
return false;
}
@@ -416,7 +416,7 @@ HeapTupleSatisfiesToast(HeapTuple htup, Snapshot snapshot,
* is canceled by super-deleting the tuple. This also applies to
* TOAST tuples created during speculative insertion.
*/
- else if (!TransactionIdIsValid(HeapTupleHeaderGetXmin(tuple)))
+ else if (!TransactionIdIsValid(HeapTupleGetXmin(htup)))
return false;
}
@@ -506,9 +506,9 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup)))
{
- if (HeapTupleHeaderGetCmin(tuple) >= curcid)
+ if (HeapTupleGetCmin(htup) >= curcid)
return TM_Invisible; /* inserted after scan started */
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
@@ -518,7 +518,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
{
TransactionId xmax;
- xmax = HeapTupleHeaderGetRawXmax(tuple);
+ xmax = HeapTupleGetRawXmax(htup);
/*
* Careful here: even though this tuple was created by our own
@@ -549,7 +549,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
{
TransactionId xmax;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -557,21 +557,21 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
/* deleting subtransaction must have aborted */
if (!TransactionIdIsCurrentTransactionId(xmax))
{
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ if (MultiXactIdIsRunning(HeapTupleGetRawXmax(htup),
false))
return TM_BeingModified;
return TM_Ok;
}
else
{
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ if (HeapTupleGetCmax(htup) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
}
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
/* deleting subtransaction must have aborted */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -579,16 +579,16 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
return TM_Ok;
}
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ if (HeapTupleGetCmax(htup) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleGetRawXmin(htup)))
return TM_Invisible;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmin(htup)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
+ HeapTupleGetRawXmin(htup));
else
{
/* it must have aborted or crashed */
@@ -622,17 +622,17 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
{
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), true))
+ if (MultiXactIdIsRunning(HeapTupleGetRawXmax(htup), true))
return TM_BeingModified;
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
return TM_Ok;
}
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
if (!TransactionIdIsValid(xmax))
{
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ if (MultiXactIdIsRunning(HeapTupleGetRawXmax(htup), false))
return TM_BeingModified;
}
@@ -641,13 +641,13 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
if (TransactionIdIsCurrentTransactionId(xmax))
{
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ if (HeapTupleGetCmax(htup) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
- if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ if (MultiXactIdIsRunning(HeapTupleGetRawXmax(htup), false))
return TM_BeingModified;
if (TransactionIdDidCommit(xmax))
@@ -663,7 +663,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
* what about the other members?
*/
- if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ if (!MultiXactIdIsRunning(HeapTupleGetRawXmax(htup), false))
{
/*
* There's no member, even just a locker, alive anymore, so we can
@@ -680,20 +680,20 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
}
}
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return TM_BeingModified;
- if (HeapTupleHeaderGetCmax(tuple) >= curcid)
+ if (HeapTupleGetCmax(htup) >= curcid)
return TM_SelfModified; /* updated after scan started */
else
return TM_Invisible; /* updated before scan started */
}
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsInProgress(HeapTupleGetRawXmax(htup)))
return TM_BeingModified;
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdDidCommit(HeapTupleGetRawXmax(htup)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -711,7 +711,7 @@ HeapTupleSatisfiesUpdate(HeapTuple htup, CommandId curcid,
}
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
if (!ItemPointerEquals(&htup->t_self, &tuple->t_ctid))
return TM_Updated; /* updated by other */
else
@@ -794,7 +794,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return true;
@@ -806,7 +806,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
{
TransactionId xmax;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -818,7 +818,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
return false;
}
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
/* deleting subtransaction must have aborted */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -828,7 +828,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
return false;
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleGetRawXmin(htup)))
{
/*
* Return the speculative token to caller. Caller can worry about
@@ -844,13 +844,13 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
Assert(snapshot->speculativeToken != 0);
}
- snapshot->xmin = HeapTupleHeaderGetRawXmin(tuple);
+ snapshot->xmin = HeapTupleGetRawXmin(htup);
/* XXX shouldn't we fall through to look at xmax? */
return true; /* in insertion by other */
}
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmin(htup)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
+ HeapTupleGetRawXmin(htup));
else
{
/* it must have aborted or crashed */
@@ -879,7 +879,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return true;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -897,21 +897,21 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
return true;
}
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
return true;
return false;
}
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsInProgress(HeapTupleGetRawXmax(htup)))
{
if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask))
- snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple);
+ snapshot->xmax = HeapTupleGetRawXmax(htup);
return true;
}
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdDidCommit(HeapTupleGetRawXmax(htup)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -929,7 +929,7 @@ HeapTupleSatisfiesDirty(HeapTuple htup, Snapshot snapshot,
}
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
return false; /* updated by other */
}
@@ -1008,9 +1008,9 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
}
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup)))
{
- if (HeapTupleHeaderGetCmin(tuple) >= snapshot->curcid)
+ if (HeapTupleGetCmin(htup) >= snapshot->curcid)
return false; /* inserted after scan started */
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
@@ -1023,7 +1023,7 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
{
TransactionId xmax;
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -1031,13 +1031,13 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
/* updating subtransaction must have aborted */
if (!TransactionIdIsCurrentTransactionId(xmax))
return true;
- else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ else if (HeapTupleGetCmax(htup) >= snapshot->curcid)
return true; /* updated after scan started */
else
return false; /* updated before scan started */
}
- if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
/* deleting subtransaction must have aborted */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -1045,16 +1045,16 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
return true;
}
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ if (HeapTupleGetCmax(htup) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
}
- else if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ else if (XidInMVCCSnapshot(HeapTupleGetRawXmin(htup), snapshot))
return false;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmin(htup)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
+ HeapTupleGetRawXmin(htup));
else
{
/* it must have aborted or crashed */
@@ -1067,7 +1067,7 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
{
/* xmin is committed, but maybe not according to our snapshot */
if (!HeapTupleHeaderXminFrozen(tuple) &&
- XidInMVCCSnapshot(HeapTupleHeaderGetRawXmin(tuple), snapshot))
+ XidInMVCCSnapshot(HeapTupleGetRawXmin(htup), snapshot))
return false; /* treat as still in progress */
}
@@ -1086,14 +1086,14 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
/* already checked above */
Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
if (TransactionIdIsCurrentTransactionId(xmax))
{
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ if (HeapTupleGetCmax(htup) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
@@ -1108,18 +1108,18 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmax(htup)))
{
- if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid)
+ if (HeapTupleGetCmax(htup) >= snapshot->curcid)
return true; /* deleted after scan started */
else
return false; /* deleted before scan started */
}
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ if (XidInMVCCSnapshot(HeapTupleGetRawXmax(htup), snapshot))
return true;
- if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdDidCommit(HeapTupleGetRawXmax(htup)))
{
/* it must have aborted or crashed */
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
@@ -1129,12 +1129,12 @@ HeapTupleSatisfiesMVCC(HeapTuple htup, Snapshot snapshot,
/* xmax transaction committed */
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
}
else
{
/* xmax is committed, but maybe not according to our snapshot */
- if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot))
+ if (XidInMVCCSnapshot(HeapTupleGetRawXmax(htup), snapshot))
return true; /* treat as still in progress */
}
@@ -1249,21 +1249,21 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
return HEAPTUPLE_DEAD;
}
}
- else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(htup)))
{
if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */
return HEAPTUPLE_INSERT_IN_PROGRESS;
/* only locked? run infomask-only check first, for performance */
if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask) ||
- HeapTupleHeaderIsOnlyLocked(tuple))
+ HeapTupleIsOnlyLocked(htup))
return HEAPTUPLE_INSERT_IN_PROGRESS;
/* inserted and then deleted by same xact */
- if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tuple)))
+ if (TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXidAny(htup)))
return HEAPTUPLE_DELETE_IN_PROGRESS;
/* deleting subtransaction must have aborted */
return HEAPTUPLE_INSERT_IN_PROGRESS;
}
- else if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdIsInProgress(HeapTupleGetRawXmin(htup)))
{
/*
* It'd be possible to discern between INSERT/DELETE in progress
@@ -1275,9 +1275,9 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
*/
return HEAPTUPLE_INSERT_IN_PROGRESS;
}
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmin(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmin(htup)))
SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,
- HeapTupleHeaderGetRawXmin(tuple));
+ HeapTupleGetRawXmin(htup));
else
{
/*
@@ -1319,14 +1319,14 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
* possibly be running; otherwise have to check.
*/
if (!HEAP_LOCKED_UPGRADED(tuple->t_infomask) &&
- MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple),
+ MultiXactIdIsRunning(HeapTupleGetRawXmax(htup),
true))
return HEAPTUPLE_LIVE;
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId);
}
else
{
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsInProgress(HeapTupleGetRawXmax(htup)))
return HEAPTUPLE_LIVE;
SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,
InvalidTransactionId);
@@ -1344,7 +1344,7 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
- TransactionId xmax = HeapTupleGetUpdateXid(tuple);
+ TransactionId xmax = HeapTupleGetUpdateXid(htup);
/* already checked above */
Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask));
@@ -1367,7 +1367,7 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
*dead_after = xmax;
return HEAPTUPLE_RECENTLY_DEAD;
}
- else if (!MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple), false))
+ else if (!MultiXactIdIsRunning(HeapTupleGetRawXmax(htup), false))
{
/*
* Not in Progress, Not Committed, so either Aborted or crashed.
@@ -1381,11 +1381,11 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED))
{
- if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple)))
+ if (TransactionIdIsInProgress(HeapTupleGetRawXmax(htup)))
return HEAPTUPLE_DELETE_IN_PROGRESS;
- else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple)))
+ else if (TransactionIdDidCommit(HeapTupleGetRawXmax(htup)))
SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
else
{
/*
@@ -1407,7 +1407,7 @@ HeapTupleSatisfiesVacuumHorizon(HeapTuple htup, Buffer buffer, TransactionId *de
* Deleter committed, allow caller to check if it was recent enough that
* some open transactions could still see the tuple.
*/
- *dead_after = HeapTupleHeaderGetRawXmax(tuple);
+ *dead_after = HeapTupleGetRawXmax(htup);
return HEAPTUPLE_RECENTLY_DEAD;
}
@@ -1503,7 +1503,7 @@ HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
/* Deleter committed, so tuple is dead if the XID is old enough. */
return GlobalVisTestIsRemovableXid(vistest,
- HeapTupleHeaderGetRawXmax(tuple));
+ HeapTupleGetRawXmax(htup));
}
/*
@@ -1516,8 +1516,9 @@ HeapTupleIsSurelyDead(HeapTuple htup, GlobalVisState *vistest)
* at the top of this file.
*/
bool
-HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
+HeapTupleIsOnlyLocked(HeapTuple htup)
{
+ HeapTupleHeader tuple = htup->t_data;
TransactionId xmax;
/* if there's no valid Xmax, then there's obviously no update either */
@@ -1528,7 +1529,7 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
return true;
/* invalid xmax means no update */
- if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple)))
+ if (!TransactionIdIsValid(HeapTupleGetRawXmax(htup)))
return true;
/*
@@ -1539,7 +1540,7 @@ HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple)
return false;
/* ... but if it's a multi, then perhaps the updating Xid aborted. */
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
/* not LOCKED_ONLY, so it has to have an xmax */
Assert(TransactionIdIsValid(xmax));
@@ -1587,8 +1588,8 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
Buffer buffer)
{
HeapTupleHeader tuple = htup->t_data;
- TransactionId xmin = HeapTupleHeaderGetXmin(tuple);
- TransactionId xmax = HeapTupleHeaderGetRawXmax(tuple);
+ TransactionId xmin = HeapTupleGetXmin(htup);
+ TransactionId xmax = HeapTupleGetRawXmax(htup);
Assert(ItemPointerIsValid(&htup->t_self));
Assert(htup->t_tableOid != InvalidOid);
@@ -1688,7 +1689,7 @@ HeapTupleSatisfiesHistoricMVCC(HeapTuple htup, Snapshot snapshot,
*/
else if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)
{
- xmax = HeapTupleGetUpdateXid(tuple);
+ xmax = HeapTupleGetUpdateXid(htup);
}
/* check if it's one of our txids, toplevel is also in there */
diff --git a/src/backend/access/heap/heaptoast.c b/src/backend/access/heap/heaptoast.c
index 1575a81b01b..252e57cc1de 100644
--- a/src/backend/access/heap/heaptoast.c
+++ b/src/backend/access/heap/heaptoast.c
@@ -307,6 +307,7 @@ heap_toast_insert_or_update(Relation rel, HeapTuple newtup, HeapTuple oldtup,
result_tuple->t_len = new_tuple_len;
result_tuple->t_self = newtup->t_self;
result_tuple->t_tableOid = newtup->t_tableOid;
+ HeapTupleCopyBase(result_tuple, newtup);
new_data = (HeapTupleHeader) ((char *) result_tuple + HEAPTUPLESIZE);
result_tuple->t_data = new_data;
@@ -395,6 +396,7 @@ toast_flatten_tuple(HeapTuple tup, TupleDesc tupleDesc)
*/
new_tuple->t_self = tup->t_self;
new_tuple->t_tableOid = tup->t_tableOid;
+ HeapTupleCopyBase(new_tuple, tup);
new_tuple->t_data->t_choice = tup->t_data->t_choice;
new_tuple->t_data->t_ctid = tup->t_data->t_ctid;
@@ -467,6 +469,7 @@ toast_flatten_tuple_to_datum(HeapTupleHeader tup,
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tup;
+ HeapTupleSetZeroBase(&tmptup);
/*
* Break down the tuple into fields.
diff --git a/src/backend/access/heap/hio.c b/src/backend/access/heap/hio.c
index ae2e2ce37a5..5668004d598 100644
--- a/src/backend/access/heap/hio.c
+++ b/src/backend/access/heap/hio.c
@@ -232,6 +232,9 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
BufferGetBlockNumber(buffer),
RelationGetRelationName(relation));
+ PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = RecentXmin - FirstNormalTransactionId;
+
/*
* Add the page to the FSM without initializing. If we were to
* initialize here, the page would potentially get flushed out to disk
@@ -243,7 +246,7 @@ RelationAddExtraBlocks(Relation relation, BulkInsertState bistate)
/* we'll need this info below */
blockNum = BufferGetBlockNumber(buffer);
- freespace = BufferGetPageSize(buffer) - SizeOfPageHeaderData;
+ freespace = BufferGetPageSize(buffer) - SizeOfPageHeaderData - MAXALIGN(sizeof(HeapPageSpecialData));
UnlockReleaseBuffer(buffer);
@@ -514,6 +517,9 @@ loop:
/*
* Now we can check to see if there's enough free space here. If so,
* we're done.
+ *
+ * "Double xmax" page is not suitable for any new tuple, since xmin
+ * can't be set there.
*/
page = BufferGetPage(buffer);
@@ -525,12 +531,13 @@ loop:
*/
if (PageIsNew(page))
{
- PageInit(page, BufferGetPageSize(buffer), 0);
+ PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
MarkBufferDirty(buffer);
}
pageFreeSpace = PageGetHeapFreeSpace(page);
- if (targetFreeSpace <= pageFreeSpace)
+ if (targetFreeSpace <= pageFreeSpace &&
+ !HeapPageIsDoubleXmax(page))
{
/* use this page as future insert target, too */
RelationSetTargetBlock(relation, targetBlock);
@@ -635,7 +642,8 @@ loop:
BufferGetBlockNumber(buffer),
RelationGetRelationName(relation));
- PageInit(page, BufferGetPageSize(buffer), 0);
+ PageInit(page, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = RecentXmin - FirstNormalTransactionId;
MarkBufferDirty(buffer);
/*
diff --git a/src/backend/access/heap/pruneheap.c b/src/backend/access/heap/pruneheap.c
index b3e2eec52fa..ab84855a7c2 100644
--- a/src/backend/access/heap/pruneheap.c
+++ b/src/backend/access/heap/pruneheap.c
@@ -135,7 +135,7 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
* determining the appropriate horizon is a waste if there's no prune_xid
* (i.e. no updates/deletes left potentially dead tuples around).
*/
- prune_xid = ((PageHeader) page)->pd_prune_xid;
+ prune_xid = HeapPageGetPruneXidNoAssert(page);
if (!TransactionIdIsValid(prune_xid))
return;
@@ -206,7 +206,7 @@ heap_page_prune_opt(Relation relation, Buffer buffer)
nnewlpdead;
ndeleted = heap_page_prune(relation, buffer, vistest, limited_xmin,
- limited_ts, &nnewlpdead, NULL);
+ limited_ts, &nnewlpdead, NULL, true);
/*
* Report the number of tuples reclaimed to pgstats. This is
@@ -265,7 +265,8 @@ heap_page_prune(Relation relation, Buffer buffer,
TransactionId old_snap_xmin,
TimestampTz old_snap_ts,
int *nnewlpdead,
- OffsetNumber *off_loc)
+ OffsetNumber *off_loc,
+ bool repairFragmentation)
{
int ndeleted = 0;
Page page = BufferGetPage(buffer);
@@ -336,6 +337,7 @@ heap_page_prune(Relation relation, Buffer buffer,
htup = (HeapTupleHeader) PageGetItem(page, itemid);
tup.t_data = htup;
tup.t_len = ItemIdGetLength(itemid);
+ HeapTupleCopyBaseFromPage(&tup, page);
ItemPointerSet(&(tup.t_self), blockno, offnum);
/*
@@ -390,13 +392,15 @@ heap_page_prune(Relation relation, Buffer buffer,
heap_page_prune_execute(buffer,
prstate.redirected, prstate.nredirected,
prstate.nowdead, prstate.ndead,
- prstate.nowunused, prstate.nunused);
+ prstate.nowunused, prstate.nunused,
+ repairFragmentation);
/*
* Update the page's pd_prune_xid field to either zero, or the lowest
* XID of any soon-prunable tuple.
*/
- ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
+ if (XidFitsPage(page, prstate.new_prune_xid))
+ HeapPageSetPruneXid(page, prstate.new_prune_xid);
/*
* Also clear the "page is full" flag, since there's no point in
@@ -458,10 +462,10 @@ heap_page_prune(Relation relation, Buffer buffer,
* point in repeating the prune/defrag process until something else
* happens to the page.
*/
- if (((PageHeader) page)->pd_prune_xid != prstate.new_prune_xid ||
+ if (HeapPageGetPruneXid(page) != prstate.new_prune_xid ||
PageIsFull(page))
{
- ((PageHeader) page)->pd_prune_xid = prstate.new_prune_xid;
+ HeapPageSetPruneXid(page, prstate.new_prune_xid);
PageClearFull(page);
MarkBufferDirtyHint(buffer, true);
}
@@ -598,6 +602,10 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
OffsetNumber chainitems[MaxHeapTuplesPerPage];
int nchain = 0,
i;
+ HeapTupleData tup;
+
+ tup.t_tableOid = RelationGetRelid(prstate->rel);
+ HeapTupleCopyBaseFromPage(&tup, dp);
rootlp = PageGetItemId(dp, rootoffnum);
@@ -609,6 +617,10 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
Assert(prstate->htsv[rootoffnum] != -1);
htup = (HeapTupleHeader) PageGetItem(dp, rootlp);
+ tup.t_data = htup;
+ tup.t_len = ItemIdGetLength(rootlp);
+ ItemPointerSet(&(tup.t_self), BufferGetBlockNumber(buffer), rootoffnum);
+
if (HeapTupleHeaderIsHeapOnly(htup))
{
/*
@@ -633,7 +645,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
!HeapTupleHeaderIsHotUpdated(htup))
{
heap_prune_record_unused(prstate, rootoffnum);
- HeapTupleHeaderAdvanceLatestRemovedXid(htup,
+ HeapTupleHeaderAdvanceLatestRemovedXid(&tup,
&prstate->latestRemovedXid);
ndeleted++;
}
@@ -700,11 +712,16 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
Assert(prstate->htsv[offnum] != -1);
htup = (HeapTupleHeader) PageGetItem(dp, lp);
+ tup.t_data = htup;
+ tup.t_len = ItemIdGetLength(lp);
+ HeapTupleCopyBaseFromPage(&tup, dp);
+ ItemPointerSet(&(tup.t_self), BufferGetBlockNumber(buffer), offnum);
+
/*
* Check the tuple XMIN against prior XMAX, if any
*/
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(HeapTupleHeaderGetXmin(htup), priorXmax))
+ !TransactionIdEquals(HeapTupleGetXmin(&tup), priorXmax))
break;
/*
@@ -731,7 +748,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
* that the page is reconsidered for pruning in future.
*/
heap_prune_record_prunable(prstate,
- HeapTupleHeaderGetUpdateXid(htup));
+ HeapTupleGetUpdateXidAny(&tup));
break;
case HEAPTUPLE_DELETE_IN_PROGRESS:
@@ -741,7 +758,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
* that the page is reconsidered for pruning in future.
*/
heap_prune_record_prunable(prstate,
- HeapTupleHeaderGetUpdateXid(htup));
+ HeapTupleGetUpdateXidAny(&tup));
break;
case HEAPTUPLE_LIVE:
@@ -770,7 +787,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
if (tupdead)
{
latestdead = offnum;
- HeapTupleHeaderAdvanceLatestRemovedXid(htup,
+ HeapTupleHeaderAdvanceLatestRemovedXid(&tup,
&prstate->latestRemovedXid);
}
else if (!recent_dead)
@@ -792,7 +809,7 @@ heap_prune_chain(Buffer buffer, OffsetNumber rootoffnum, PruneState *prstate)
Assert(ItemPointerGetBlockNumber(&htup->t_ctid) ==
BufferGetBlockNumber(buffer));
offnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
- priorXmax = HeapTupleHeaderGetUpdateXid(htup);
+ priorXmax = HeapTupleGetUpdateXidAny(&tup);
}
/*
@@ -909,7 +926,8 @@ void
heap_page_prune_execute(Buffer buffer,
OffsetNumber *redirected, int nredirected,
OffsetNumber *nowdead, int ndead,
- OffsetNumber *nowunused, int nunused)
+ OffsetNumber *nowunused, int nunused,
+ bool repairFragmentation)
{
Page page = (Page) BufferGetPage(buffer);
OffsetNumber *offnum;
@@ -1033,7 +1051,8 @@ heap_page_prune_execute(Buffer buffer,
* Finally, repair any fragmentation, and update the page's hint bit about
* whether it has free pointers.
*/
- PageRepairFragmentation(page);
+ if (repairFragmentation)
+ PageRepairFragmentation(page);
/*
* Now that the page has been modified, assert that redirect items still
@@ -1120,6 +1139,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
HeapTupleHeader htup;
OffsetNumber nextoffnum;
TransactionId priorXmax;
+ HeapTupleData tup;
/* skip unused and dead items */
if (!ItemIdIsUsed(lp) || ItemIdIsDead(lp))
@@ -1128,6 +1148,8 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
if (ItemIdIsNormal(lp))
{
htup = (HeapTupleHeader) PageGetItem(page, lp);
+ tup.t_data = htup;
+ HeapTupleCopyBaseFromPage(&tup, page);
/*
* Check if this tuple is part of a HOT-chain rooted at some other
@@ -1149,7 +1171,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
/* Set up to scan the HOT-chain */
nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
- priorXmax = HeapTupleHeaderGetUpdateXid(htup);
+ priorXmax = HeapTupleGetUpdateXidAny(&tup);
}
else
{
@@ -1188,9 +1210,11 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
break;
htup = (HeapTupleHeader) PageGetItem(page, lp);
+ tup.t_data = htup;
+ HeapTupleCopyBaseFromPage(&tup, page);
if (TransactionIdIsValid(priorXmax) &&
- !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(htup)))
+ !TransactionIdEquals(priorXmax, HeapTupleGetXmin(&tup)))
break;
/* Remember the root line pointer for this item */
@@ -1204,7 +1228,7 @@ heap_get_root_tuples(Page page, OffsetNumber *root_offsets)
Assert(!HeapTupleHeaderIndicatesMovedPartitions(htup));
nextoffnum = ItemPointerGetOffsetNumber(&htup->t_ctid);
- priorXmax = HeapTupleHeaderGetUpdateXid(htup);
+ priorXmax = HeapTupleGetUpdateXidAny(&tup);
}
}
}
diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c
index aa265edf601..64e69cc3a75 100644
--- a/src/backend/access/heap/rewriteheap.c
+++ b/src/backend/access/heap/rewriteheap.c
@@ -379,6 +379,7 @@ rewrite_heap_tuple(RewriteState state,
&old_tuple->t_data->t_choice.t_heap,
sizeof(HeapTupleFields));
+ HeapTupleCopyBase(new_tuple, old_tuple);
new_tuple->t_data->t_infomask &= ~HEAP_XACT_MASK;
new_tuple->t_data->t_infomask2 &= ~HEAP2_XACT_MASK;
new_tuple->t_data->t_infomask |=
@@ -388,7 +389,7 @@ rewrite_heap_tuple(RewriteState state,
* While we have our hands on the tuple, we may as well freeze any
* eligible xmin or xmax, so that future VACUUM effort can be saved.
*/
- heap_freeze_tuple(new_tuple->t_data,
+ heap_freeze_tuple(new_tuple,
state->rs_old_rel->rd_rel->relfrozenxid,
state->rs_old_rel->rd_rel->relminmxid,
state->rs_freeze_xid,
@@ -404,7 +405,7 @@ rewrite_heap_tuple(RewriteState state,
* If the tuple has been updated, check the old-to-new mapping hash table.
*/
if (!((old_tuple->t_data->t_infomask & HEAP_XMAX_INVALID) ||
- HeapTupleHeaderIsOnlyLocked(old_tuple->t_data)) &&
+ HeapTupleIsOnlyLocked(old_tuple)) &&
!HeapTupleHeaderIndicatesMovedPartitions(old_tuple->t_data) &&
!(ItemPointerEquals(&(old_tuple->t_self),
&(old_tuple->t_data->t_ctid))))
@@ -412,7 +413,7 @@ rewrite_heap_tuple(RewriteState state,
OldToNewMapping mapping;
memset(&hashkey, 0, sizeof(hashkey));
- hashkey.xmin = HeapTupleHeaderGetUpdateXid(old_tuple->t_data);
+ hashkey.xmin = HeapTupleGetUpdateXidAny(old_tuple);
hashkey.tid = old_tuple->t_data->t_ctid;
mapping = (OldToNewMapping)
@@ -485,7 +486,7 @@ rewrite_heap_tuple(RewriteState state,
* RECENTLY_DEAD if and only if the xmin is not before OldestXmin.
*/
if ((new_tuple->t_data->t_infomask & HEAP_UPDATED) &&
- !TransactionIdPrecedes(HeapTupleHeaderGetXmin(new_tuple->t_data),
+ !TransactionIdPrecedes(HeapTupleGetXmin(new_tuple),
state->rs_oldest_xmin))
{
/*
@@ -494,7 +495,7 @@ rewrite_heap_tuple(RewriteState state,
UnresolvedTup unresolved;
memset(&hashkey, 0, sizeof(hashkey));
- hashkey.xmin = HeapTupleHeaderGetXmin(new_tuple->t_data);
+ hashkey.xmin = HeapTupleGetXmin(new_tuple);
hashkey.tid = old_tid;
unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
@@ -582,7 +583,7 @@ rewrite_heap_dead_tuple(RewriteState state, HeapTuple old_tuple)
bool found;
memset(&hashkey, 0, sizeof(hashkey));
- hashkey.xmin = HeapTupleHeaderGetXmin(old_tuple->t_data);
+ hashkey.xmin = HeapTupleGetXmin(old_tuple);
hashkey.tid = old_tuple->t_self;
unresolved = hash_search(state->rs_unresolved_tups, &hashkey,
@@ -618,6 +619,7 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
Size len;
OffsetNumber newoff;
HeapTuple heaptup;
+ TransactionId xmin, xmax;
/*
* If the new tuple is too big for storage or contains already toasted
@@ -703,10 +705,20 @@ raw_heap_insert(RewriteState state, HeapTuple tup)
if (!state->rs_buffer_valid)
{
/* Initialize a new empty page */
- PageInit(page, BLCKSZ, 0);
+ PageInit(page, BLCKSZ, sizeof(HeapPageSpecialData));
state->rs_buffer_valid = true;
}
+ rewrite_page_prepare_for_xid(page, HeapTupleGetXmin(heaptup), false);
+ rewrite_page_prepare_for_xid(page, HeapTupleGetRawXmax(heaptup),
+ (heaptup->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? true : false);
+
+ xmin = HeapTupleGetXmin(heaptup);
+ xmax = HeapTupleGetRawXmax(heaptup);
+ HeapTupleCopyBaseFromPage(heaptup, page);
+ HeapTupleSetXmin(heaptup, xmin);
+ HeapTupleSetXmax(heaptup, xmax);
+
/* And now we can insert the tuple into the page */
newoff = PageAddItem(page, (Item) heaptup->t_data, heaptup->t_len,
InvalidOffsetNumber, false, true);
@@ -999,7 +1011,10 @@ logical_rewrite_log_mapping(RewriteState state, TransactionId xid,
"pg_logical/mappings/" LOGICAL_REWRITE_FORMAT,
dboid, relid,
LSN_FORMAT_ARGS(state->rs_begin_lsn),
- xid, GetCurrentTransactionId());
+ (uint32) (xid >> 32),
+ (uint32) xid,
+ (uint32) (GetCurrentTransactionId() >> 32),
+ (uint32) GetCurrentTransactionId());
dlist_init(&src->mappings);
src->num_mappings = 0;
@@ -1048,9 +1063,9 @@ logical_rewrite_heap_tuple(RewriteState state, ItemPointerData old_tid,
if (!state->rs_logical_rewrite)
return;
- xmin = HeapTupleHeaderGetXmin(new_tuple->t_data);
+ xmin = HeapTupleGetXmin(new_tuple);
/* use *GetUpdateXid to correctly deal with multixacts */
- xmax = HeapTupleHeaderGetUpdateXid(new_tuple->t_data);
+ xmax = HeapTupleGetUpdateXidAny(new_tuple);
/*
* Log the mapping iff the tuple has been created recently.
@@ -1121,7 +1136,10 @@ heap_xlog_logical_rewrite(XLogReaderState *r)
"pg_logical/mappings/" LOGICAL_REWRITE_FORMAT,
xlrec->mapped_db, xlrec->mapped_rel,
LSN_FORMAT_ARGS(xlrec->start_lsn),
- xlrec->mapped_xid, XLogRecGetXid(r));
+ (uint32) (xlrec->mapped_xid >> 32),
+ (uint32) xlrec->mapped_xid,
+ (uint32) (XLogRecGetXid(r) >> 32),
+ (uint32) XLogRecGetXid(r));
fd = OpenTransientFile(path,
O_CREAT | O_WRONLY | PG_BINARY);
@@ -1217,10 +1235,12 @@ CheckPointLogicalRewriteHeap(void)
Oid dboid;
Oid relid;
XLogRecPtr lsn;
- TransactionId rewrite_xid;
- TransactionId create_xid;
- uint32 hi,
- lo;
+ uint32 lsn_hi,
+ lsn_lo,
+ rewrite_xid_hi,
+ rewrite_xid_lo,
+ create_xid_hi,
+ create_xid_lo;
if (strcmp(mapping_de->d_name, ".") == 0 ||
strcmp(mapping_de->d_name, "..") == 0)
@@ -1235,10 +1255,12 @@ CheckPointLogicalRewriteHeap(void)
continue;
if (sscanf(mapping_de->d_name, LOGICAL_REWRITE_FORMAT,
- &dboid, &relid, &hi, &lo, &rewrite_xid, &create_xid) != 6)
+ &dboid, &relid, &lsn_hi, &lsn_lo,
+ &rewrite_xid_hi, &rewrite_xid_lo,
+ &create_xid_hi, &create_xid_lo) != 8)
elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
- lsn = ((uint64) hi) << 32 | lo;
+ lsn = ((uint64) lsn_hi) << 32 | lsn_lo;
if (lsn < cutoff || cutoff == InvalidXLogRecPtr)
{
diff --git a/src/backend/access/heap/vacuumlazy.c b/src/backend/access/heap/vacuumlazy.c
index 1749cc2a476..797866dcfdb 100644
--- a/src/backend/access/heap/vacuumlazy.c
+++ b/src/backend/access/heap/vacuumlazy.c
@@ -253,7 +253,6 @@ static int lazy_vacuum_heap_page(LVRelState *vacrel, BlockNumber blkno,
Buffer buffer, int index, Buffer *vmbuffer);
static bool lazy_check_needs_freeze(Buffer buf, bool *hastup,
LVRelState *vacrel);
-static bool lazy_check_wraparound_failsafe(LVRelState *vacrel);
static void lazy_cleanup_all_indexes(LVRelState *vacrel);
static IndexBulkDeleteResult *lazy_vacuum_one_index(Relation indrel,
IndexBulkDeleteResult *istat,
@@ -615,11 +614,11 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,
vacrel->pinskipped_pages,
vacrel->frozenskipped_pages);
appendStringInfo(&buf,
- _("tuples: %lld removed, %lld remain, %lld are dead but not yet removable, oldest xmin: %u\n"),
+ _("tuples: %lld removed, %lld remain, %lld are dead but not yet removable, oldest xmin: "),
(long long) vacrel->tuples_deleted,
(long long) vacrel->new_rel_tuples,
- (long long) vacrel->new_dead_tuples,
- OldestXmin);
+ (long long) vacrel->new_dead_tuples);
+ appendStringInfo(&buf, XID_FMT "\n", OldestXmin);
orig_rel_pages = vacrel->rel_pages + vacrel->pages_removed;
if (orig_rel_pages > 0)
{
@@ -781,13 +780,6 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
vacrel->indstats = (IndexBulkDeleteResult **)
palloc0(vacrel->nindexes * sizeof(IndexBulkDeleteResult *));
- /*
- * Do failsafe precheck before calling dead_items_alloc. This ensures
- * that parallel VACUUM won't be attempted when relfrozenxid is already
- * dangerously old.
- */
- lazy_check_wraparound_failsafe(vacrel);
-
/*
* Allocate the space for dead_items. Note that this handles parallel
* VACUUM initialization as part of allocating shared memory space used
@@ -980,7 +972,6 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
*/
if (blkno - next_failsafe_block >= FAILSAFE_EVERY_PAGES)
{
- lazy_check_wraparound_failsafe(vacrel);
next_failsafe_block = blkno;
}
@@ -1141,7 +1132,9 @@ lazy_scan_heap(LVRelState *vacrel, VacuumParams *params, bool aggressive)
if (GetRecordedFreeSpace(vacrel->rel, blkno) == 0)
{
- Size freespace = BLCKSZ - SizeOfPageHeaderData;
+ Size freespace = BufferGetPageSize(buf)
+ - SizeOfPageHeaderData
+ - sizeof(HeapPageSpecialData);
RecordPageWithFreeSpace(vacrel->rel, blkno, freespace);
}
@@ -1513,7 +1506,7 @@ retry:
*/
tuples_deleted = heap_page_prune(rel, buf, vistest,
InvalidTransactionId, 0, &nnewlpdead,
- &vacrel->offnum);
+ &vacrel->offnum, true);
/*
* Now scan the page to collect LP_DEAD items and check for tuples
@@ -1580,6 +1573,7 @@ retry:
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(rel);
+ HeapTupleCopyBaseFromPage(&tuple, page);
/*
* DEAD tuples are almost always pruned into LP_DEAD line pointers by
@@ -1643,7 +1637,7 @@ retry:
* The inserter definitely committed. But is it old enough
* that everyone sees it as committed?
*/
- xmin = HeapTupleHeaderGetXmin(tuple.t_data);
+ xmin = HeapTupleGetXmin(&tuple);
if (!TransactionIdPrecedes(xmin, vacrel->OldestXmin))
{
prunestate->all_visible = false;
@@ -1700,7 +1694,7 @@ retry:
*/
num_tuples++;
prunestate->hastup = true;
- if (heap_prepare_freeze_tuple(tuple.t_data,
+ if (heap_prepare_freeze_tuple(&tuple,
vacrel->relfrozenxid,
vacrel->relminmxid,
vacrel->FreezeLimit,
@@ -1748,15 +1742,19 @@ retry:
MarkBufferDirty(buf);
+ tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+ HeapTupleCopyBaseFromPage(&tuple, page);
+
/* execute collected freezes */
for (int i = 0; i < nfrozen; i++)
{
- HeapTupleHeader htup;
-
itemid = PageGetItemId(page, frozen[i].offset);
- htup = (HeapTupleHeader) PageGetItem(page, itemid);
- heap_execute_freeze_tuple(htup, &frozen[i]);
+ ItemPointerSet(&tuple.t_self, blkno, frozen[i].offset);
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
+ tuple.t_len = ItemIdGetLength(itemid);
+
+ heap_execute_freeze_tuple(&tuple, &frozen[i]);
}
/* Now WAL-log freezing if necessary */
@@ -1992,13 +1990,6 @@ lazy_vacuum_all_indexes(LVRelState *vacrel)
Assert(TransactionIdIsNormal(vacrel->relfrozenxid));
Assert(MultiXactIdIsValid(vacrel->relminmxid));
- /* Precheck for XID wraparound emergencies */
- if (lazy_check_wraparound_failsafe(vacrel))
- {
- /* Wraparound emergency -- don't even start an index scan */
- return false;
- }
-
/* Report that we are now vacuuming indexes */
pgstat_progress_update_param(PROGRESS_VACUUM_PHASE,
PROGRESS_VACUUM_PHASE_VACUUM_INDEX);
@@ -2013,13 +2004,6 @@ lazy_vacuum_all_indexes(LVRelState *vacrel)
vacrel->indstats[idx] =
lazy_vacuum_one_index(indrel, istat, vacrel->old_live_tuples,
vacrel);
-
- if (lazy_check_wraparound_failsafe(vacrel))
- {
- /* Wraparound emergency -- end current index scan */
- allindexes = false;
- break;
- }
}
}
else
@@ -2027,13 +2011,6 @@ lazy_vacuum_all_indexes(LVRelState *vacrel)
/* Outsource everything to parallel variant */
parallel_vacuum_bulkdel_all_indexes(vacrel->pvs, vacrel->old_live_tuples,
vacrel->num_index_scans);
-
- /*
- * Do a postcheck to consider applying wraparound failsafe now. Note
- * that parallel VACUUM only gets the precheck and this postcheck.
- */
- if (lazy_check_wraparound_failsafe(vacrel))
- allindexes = false;
}
/*
@@ -2318,6 +2295,7 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup, LVRelState *vacrel)
offnum = OffsetNumberNext(offnum))
{
ItemId itemid;
+ HeapTupleData htup;
/*
* Set the offset number so that we can display it along with any
@@ -2335,8 +2313,10 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup, LVRelState *vacrel)
continue;
tupleheader = (HeapTupleHeader) PageGetItem(page, itemid);
+ htup.t_data = tupleheader;
+ HeapTupleCopyBaseFromPage(&htup, page);
- if (heap_tuple_needs_freeze(tupleheader, vacrel->FreezeLimit,
+ if (heap_tuple_needs_freeze(&htup, vacrel->FreezeLimit,
vacrel->MultiXactCutoff, buf))
break;
} /* scan along page */
@@ -2347,55 +2327,6 @@ lazy_check_needs_freeze(Buffer buf, bool *hastup, LVRelState *vacrel)
return (offnum <= maxoff);
}
-/*
- * Trigger the failsafe to avoid wraparound failure when vacrel table has a
- * relfrozenxid and/or relminmxid that is dangerously far in the past.
- * Triggering the failsafe makes the ongoing VACUUM bypass any further index
- * vacuuming and heap vacuuming. Truncating the heap is also bypassed.
- *
- * Any remaining work (work that VACUUM cannot just bypass) is typically sped
- * up when the failsafe triggers. VACUUM stops applying any cost-based delay
- * that it started out with.
- *
- * Returns true when failsafe has been triggered.
- */
-static bool
-lazy_check_wraparound_failsafe(LVRelState *vacrel)
-{
- /* Don't warn more than once per VACUUM */
- if (vacrel->failsafe_active)
- return true;
-
- if (unlikely(vacuum_xid_failsafe_check(vacrel->relfrozenxid,
- vacrel->relminmxid)))
- {
- vacrel->failsafe_active = true;
-
- /* Disable index vacuuming, index cleanup, and heap rel truncation */
- vacrel->do_index_vacuuming = false;
- vacrel->do_index_cleanup = false;
- vacrel->do_rel_truncate = false;
-
- ereport(WARNING,
- (errmsg("bypassing nonessential maintenance of table \"%s.%s.%s\" as a failsafe after %d index scans",
- get_database_name(MyDatabaseId),
- vacrel->relnamespace,
- vacrel->relname,
- vacrel->num_index_scans),
- errdetail("The table's relfrozenxid or relminmxid is too far in the past."),
- errhint("Consider increasing configuration parameter \"maintenance_work_mem\" or \"autovacuum_work_mem\".\n"
- "You might also need to consider other ways for VACUUM to keep up with the allocation of transaction IDs.")));
-
- /* Stop applying cost limits from this point on */
- VacuumCostActive = false;
- VacuumCostBalance = 0;
-
- return true;
- }
-
- return false;
-}
-
/*
* lazy_cleanup_all_indexes() -- cleanup all indexes of relation.
*/
@@ -3016,6 +2947,7 @@ heap_page_is_all_visible(LVRelState *vacrel, Buffer buf,
tuple.t_data = (HeapTupleHeader) PageGetItem(page, itemid);
tuple.t_len = ItemIdGetLength(itemid);
tuple.t_tableOid = RelationGetRelid(vacrel->rel);
+ HeapTupleCopyBaseFromPage(&tuple, page);
switch (HeapTupleSatisfiesVacuum(&tuple, vacrel->OldestXmin, buf))
{
@@ -3035,7 +2967,7 @@ heap_page_is_all_visible(LVRelState *vacrel, Buffer buf,
* The inserter definitely committed. But is it old enough
* that everyone sees it as committed?
*/
- xmin = HeapTupleHeaderGetXmin(tuple.t_data);
+ xmin = HeapTupleGetXmin(&tuple);
if (!TransactionIdPrecedes(xmin, vacrel->OldestXmin))
{
all_visible = false;
diff --git a/src/backend/access/nbtree/nbtpage.c b/src/backend/access/nbtree/nbtpage.c
index 6b5f01e1d07..75840325bf3 100644
--- a/src/backend/access/nbtree/nbtpage.c
+++ b/src/backend/access/nbtree/nbtpage.c
@@ -546,6 +546,7 @@ _bt_getroot(Relation rel, int access)
rootblkno = rootopaque->btpo_next;
}
+ /* Note: can't check btpo_level on deleted pages */
if (rootopaque->btpo_level != rootlevel)
elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
rootblkno, RelationGetRelationName(rel),
@@ -649,6 +650,7 @@ _bt_gettrueroot(Relation rel)
rootblkno = rootopaque->btpo_next;
}
+ /* Note: can't check btpo_level on deleted pages */
if (rootopaque->btpo_level != rootlevel)
elog(ERROR, "root page %u of index \"%s\" has level %u, expected %u",
rootblkno, RelationGetRelationName(rel),
diff --git a/src/backend/access/nbtree/nbtsplitloc.c b/src/backend/access/nbtree/nbtsplitloc.c
index c46594e1a20..c0bcff5755f 100644
--- a/src/backend/access/nbtree/nbtsplitloc.c
+++ b/src/backend/access/nbtree/nbtsplitloc.c
@@ -141,6 +141,7 @@ _bt_findsplitloc(Relation rel,
olddataitemstoleft,
perfectpenalty,
leaffillfactor;
+ int maxTupleEnd PG_USED_FOR_ASSERTS_ONLY;
FindSplitData state;
FindSplitStrat strategy;
ItemId itemid;
@@ -154,6 +155,7 @@ _bt_findsplitloc(Relation rel,
opaque = (BTPageOpaque) PageGetSpecialPointer(origpage);
maxoff = PageGetMaxOffsetNumber(origpage);
+ maxTupleEnd = ItemIdGetTupleEnd(PageGetItemId(origpage, P_HIKEY));
/* Total free space available on a btree page, after fixed overhead */
leftspace = rightspace =
@@ -215,6 +217,15 @@ _bt_findsplitloc(Relation rel,
itemid = PageGetItemId(origpage, offnum);
itemsz = MAXALIGN(ItemIdGetLength(itemid)) + sizeof(ItemIdData);
+#ifdef USE_ASSERT_CHECKING
+ /* Ending of rightmost tuple on a page can be shifted relative to
+ * left boundary of BTPageOpaqueData due to conversion from EE96,
+ * which used different BTPageOpaqueData layout. It is only checked
+ * in the assert below.
+ */
+ if (maxTupleEnd < ItemIdGetTupleEnd(itemid))
+ maxTupleEnd = ItemIdGetTupleEnd(itemid);
+#endif
/*
* When item offset number is not newitemoff, neither side of the
* split can be newitem. Record a split after the previous data item
@@ -249,7 +260,7 @@ _bt_findsplitloc(Relation rel,
* (Though only when it's possible that newitem will end up alone on new
* right page.)
*/
- Assert(olddataitemstoleft == olddataitemstotal);
+ Assert(olddataitemstoleft + ((PageHeader)origpage)->pd_special - maxTupleEnd == olddataitemstotal);
if (newitemoff > maxoff)
_bt_recsplitloc(&state, newitemoff, false, olddataitemstotal, 0);
diff --git a/src/backend/access/nbtree/nbtxlog.c b/src/backend/access/nbtree/nbtxlog.c
index 611f412ba8a..d8294b429b0 100644
--- a/src/backend/access/nbtree/nbtxlog.c
+++ b/src/backend/access/nbtree/nbtxlog.c
@@ -15,6 +15,8 @@
#include "postgres.h"
#include "access/bufmask.h"
+#include "access/heapam_xlog.h"
+#include "access/htup_details.h"
#include "access/nbtree.h"
#include "access/nbtxlog.h"
#include "access/transam.h"
diff --git a/src/backend/access/rmgrdesc/clogdesc.c b/src/backend/access/rmgrdesc/clogdesc.c
index 87513732be6..e950dd0bdc4 100644
--- a/src/backend/access/rmgrdesc/clogdesc.c
+++ b/src/backend/access/rmgrdesc/clogdesc.c
@@ -25,17 +25,17 @@ clog_desc(StringInfo buf, XLogReaderState *record)
if (info == CLOG_ZEROPAGE)
{
- int pageno;
+ int64 pageno;
- memcpy(&pageno, rec, sizeof(int));
- appendStringInfo(buf, "page %d", pageno);
+ memcpy(&pageno, rec, sizeof(int64));
+ appendStringInfo(buf, "page " INT64_FORMAT, pageno);
}
else if (info == CLOG_TRUNCATE)
{
xl_clog_truncate xlrec;
memcpy(&xlrec, rec, sizeof(xl_clog_truncate));
- appendStringInfo(buf, "page %d; oldestXact %u",
+ appendStringInfo(buf, "page " INT64_FORMAT "; oldestXact " XID_FMT,
xlrec.pageno, xlrec.oldestXact);
}
}
diff --git a/src/backend/access/rmgrdesc/committsdesc.c b/src/backend/access/rmgrdesc/committsdesc.c
index 3a65538bb0a..904e13a882f 100644
--- a/src/backend/access/rmgrdesc/committsdesc.c
+++ b/src/backend/access/rmgrdesc/committsdesc.c
@@ -26,16 +26,16 @@ commit_ts_desc(StringInfo buf, XLogReaderState *record)
if (info == COMMIT_TS_ZEROPAGE)
{
- int pageno;
+ int64 pageno;
- memcpy(&pageno, rec, sizeof(int));
- appendStringInfo(buf, "%d", pageno);
+ memcpy(&pageno, rec, sizeof(int64));
+ appendStringInfo(buf, INT64_FORMAT, pageno);
}
else if (info == COMMIT_TS_TRUNCATE)
{
xl_commit_ts_truncate *trunc = (xl_commit_ts_truncate *) rec;
- appendStringInfo(buf, "pageno %d, oldestXid %u",
+ appendStringInfo(buf, "pageno " INT64_FORMAT ", oldestXid " XID_FMT,
trunc->pageno, trunc->oldestXid);
}
}
diff --git a/src/backend/access/rmgrdesc/gistdesc.c b/src/backend/access/rmgrdesc/gistdesc.c
index 9cab4fa1580..0b1a7c6ad8b 100644
--- a/src/backend/access/rmgrdesc/gistdesc.c
+++ b/src/backend/access/rmgrdesc/gistdesc.c
@@ -26,17 +26,16 @@ out_gistxlogPageUpdate(StringInfo buf, gistxlogPageUpdate *xlrec)
static void
out_gistxlogPageReuse(StringInfo buf, gistxlogPageReuse *xlrec)
{
- appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid %u:%u",
+ appendStringInfo(buf, "rel %u/%u/%u; blk %u; latestRemovedXid " XID_FMT,
xlrec->node.spcNode, xlrec->node.dbNode,
xlrec->node.relNode, xlrec->block,
- EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
XidFromFullTransactionId(xlrec->latestRemovedFullXid));
}
static void
out_gistxlogDelete(StringInfo buf, gistxlogDelete *xlrec)
{
- appendStringInfo(buf, "delete: latestRemovedXid %u, nitems: %u",
+ appendStringInfo(buf, "delete: latestRemovedXid " XID_FMT ", nitems: %u",
xlrec->latestRemovedXid, xlrec->ntodelete);
}
@@ -51,8 +50,7 @@ out_gistxlogPageSplit(StringInfo buf, gistxlogPageSplit *xlrec)
static void
out_gistxlogPageDelete(StringInfo buf, gistxlogPageDelete *xlrec)
{
- appendStringInfo(buf, "deleteXid %u:%u; downlink %u",
- EpochFromFullTransactionId(xlrec->deleteXid),
+ appendStringInfo(buf, "deleteXid " XID_FMT "; downlink %u",
XidFromFullTransactionId(xlrec->deleteXid),
xlrec->downlinkOffset);
}
diff --git a/src/backend/access/rmgrdesc/hashdesc.c b/src/backend/access/rmgrdesc/hashdesc.c
index ef443bdb16c..d9f0c40fbfe 100644
--- a/src/backend/access/rmgrdesc/hashdesc.c
+++ b/src/backend/access/rmgrdesc/hashdesc.c
@@ -113,7 +113,7 @@ hash_desc(StringInfo buf, XLogReaderState *record)
{
xl_hash_vacuum_one_page *xlrec = (xl_hash_vacuum_one_page *) rec;
- appendStringInfo(buf, "ntuples %d, latestRemovedXid %u",
+ appendStringInfo(buf, "ntuples %d, latestRemovedXid " XID_FMT,
xlrec->ntuples,
xlrec->latestRemovedXid);
break;
diff --git a/src/backend/access/rmgrdesc/heapdesc.c b/src/backend/access/rmgrdesc/heapdesc.c
index 6238085d65e..12140ba3702 100644
--- a/src/backend/access/rmgrdesc/heapdesc.c
+++ b/src/backend/access/rmgrdesc/heapdesc.c
@@ -58,12 +58,12 @@ heap_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_update *xlrec = (xl_heap_update *) rec;
- appendStringInfo(buf, "off %u xmax %u flags 0x%02X ",
+ appendStringInfo(buf, "off %u xmax " XID_FMT " flags 0x%02X ",
xlrec->old_offnum,
xlrec->old_xmax,
xlrec->flags);
out_infobits(buf, xlrec->old_infobits_set);
- appendStringInfo(buf, "; new off %u xmax %u",
+ appendStringInfo(buf, "; new off %u xmax " XID_FMT,
xlrec->new_offnum,
xlrec->new_xmax);
}
@@ -71,12 +71,12 @@ heap_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_update *xlrec = (xl_heap_update *) rec;
- appendStringInfo(buf, "off %u xmax %u flags 0x%02X ",
+ appendStringInfo(buf, "off %u xmax " XID_FMT " flags 0x%02X ",
xlrec->old_offnum,
xlrec->old_xmax,
xlrec->flags);
out_infobits(buf, xlrec->old_infobits_set);
- appendStringInfo(buf, "; new off %u xmax %u",
+ appendStringInfo(buf, "; new off %u xmax " XID_FMT,
xlrec->new_offnum,
xlrec->new_xmax);
}
@@ -103,7 +103,7 @@ heap_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_lock *xlrec = (xl_heap_lock *) rec;
- appendStringInfo(buf, "off %u: xid %u: flags 0x%02X ",
+ appendStringInfo(buf, "off %u: xid " XID_FMT ": flags 0x%02X ",
xlrec->offnum, xlrec->locking_xid, xlrec->flags);
out_infobits(buf, xlrec->infobits_set);
}
@@ -125,7 +125,7 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_prune *xlrec = (xl_heap_prune *) rec;
- appendStringInfo(buf, "latestRemovedXid %u nredirected %u ndead %u",
+ appendStringInfo(buf, "latestRemovedXid " XID_FMT " nredirected %u ndead %u",
xlrec->latestRemovedXid,
xlrec->nredirected,
xlrec->ndead);
@@ -140,14 +140,14 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_freeze_page *xlrec = (xl_heap_freeze_page *) rec;
- appendStringInfo(buf, "cutoff xid %u ntuples %u",
+ appendStringInfo(buf, "cutoff xid " XID_FMT " ntuples %u",
xlrec->cutoff_xid, xlrec->ntuples);
}
else if (info == XLOG_HEAP2_VISIBLE)
{
xl_heap_visible *xlrec = (xl_heap_visible *) rec;
- appendStringInfo(buf, "cutoff xid %u flags 0x%02X",
+ appendStringInfo(buf, "cutoff xid " XID_FMT " flags 0x%02X",
xlrec->cutoff_xid, xlrec->flags);
}
else if (info == XLOG_HEAP2_MULTI_INSERT)
@@ -161,7 +161,7 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
{
xl_heap_lock_updated *xlrec = (xl_heap_lock_updated *) rec;
- appendStringInfo(buf, "off %u: xmax %u: flags 0x%02X ",
+ appendStringInfo(buf, "off %u: xmax " XID_FMT ": flags 0x%02X ",
xlrec->offnum, xlrec->xmax, xlrec->flags);
out_infobits(buf, xlrec->infobits_set);
}
@@ -180,6 +180,23 @@ heap2_desc(StringInfo buf, XLogReaderState *record)
}
}
+void
+heap3_desc(StringInfo buf, XLogReaderState *record)
+{
+ char *rec = XLogRecGetData(record);
+ uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
+
+ info &= XLOG_HEAP_OPMASK;
+ if (info == XLOG_HEAP3_BASE_SHIFT)
+ {
+ xl_heap_base_shift *xlrec = (xl_heap_base_shift *) rec;
+
+ appendStringInfo(buf, "%s delta " INT64_FORMAT " ",
+ xlrec->multi ? "MultiXactId" : "XactId",
+ xlrec->delta);
+ }
+}
+
const char *
heap_identify(uint8 info)
{
@@ -263,3 +280,18 @@ heap2_identify(uint8 info)
return id;
}
+
+const char *
+heap3_identify(uint8 info)
+{
+ const char *id = NULL;
+
+ switch (info & ~XLR_INFO_MASK)
+ {
+ case XLOG_HEAP3_BASE_SHIFT:
+ id = "BASE_SHIFT";
+ break;
+ }
+
+ return id;
+}
diff --git a/src/backend/access/rmgrdesc/mxactdesc.c b/src/backend/access/rmgrdesc/mxactdesc.c
index 7076be2b3f2..fba52ac9f45 100644
--- a/src/backend/access/rmgrdesc/mxactdesc.c
+++ b/src/backend/access/rmgrdesc/mxactdesc.c
@@ -19,7 +19,7 @@
static void
out_member(StringInfo buf, MultiXactMember *member)
{
- appendStringInfo(buf, "%u ", member->xid);
+ appendStringInfo(buf, XID_FMT " ", member->xid);
switch (member->status)
{
case MultiXactStatusForKeyShare:
@@ -55,18 +55,18 @@ multixact_desc(StringInfo buf, XLogReaderState *record)
if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE ||
info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
{
- int pageno;
+ int64 pageno;
memcpy(&pageno, rec, sizeof(int));
- appendStringInfo(buf, "%d", pageno);
+ appendStringInfo(buf, INT64_FORMAT, pageno);
}
else if (info == XLOG_MULTIXACT_CREATE_ID)
{
xl_multixact_create *xlrec = (xl_multixact_create *) rec;
int i;
- appendStringInfo(buf, "%u offset %u nmembers %d: ", xlrec->mid,
- xlrec->moff, xlrec->nmembers);
+ appendStringInfo(buf, XID_FMT " offset " INT64_FORMAT " nmembers %d: ",
+ xlrec->mid, xlrec->moff, xlrec->nmembers);
for (i = 0; i < xlrec->nmembers; i++)
out_member(buf, &xlrec->members[i]);
}
@@ -74,7 +74,7 @@ multixact_desc(StringInfo buf, XLogReaderState *record)
{
xl_multixact_truncate *xlrec = (xl_multixact_truncate *) rec;
- appendStringInfo(buf, "offsets [%u, %u), members [%u, %u)",
+ appendStringInfo(buf, "offsets [" XID_FMT ", " XID_FMT "), members [" INT64_FORMAT ", " INT64_FORMAT ")",
xlrec->startTruncOff, xlrec->endTruncOff,
xlrec->startTruncMemb, xlrec->endTruncMemb);
}
diff --git a/src/backend/access/rmgrdesc/nbtdesc.c b/src/backend/access/rmgrdesc/nbtdesc.c
index dfbbf4ebaf8..8aea94b8731 100644
--- a/src/backend/access/rmgrdesc/nbtdesc.c
+++ b/src/backend/access/rmgrdesc/nbtdesc.c
@@ -63,7 +63,7 @@ btree_desc(StringInfo buf, XLogReaderState *record)
{
xl_btree_delete *xlrec = (xl_btree_delete *) rec;
- appendStringInfo(buf, "latestRemovedXid %u; ndeleted %u; nupdated %u",
+ appendStringInfo(buf, "latestRemovedXid " XID_FMT "; ndeleted %u; nupdated %u",
xlrec->latestRemovedXid, xlrec->ndeleted, xlrec->nupdated);
break;
}
@@ -80,9 +80,8 @@ btree_desc(StringInfo buf, XLogReaderState *record)
{
xl_btree_unlink_page *xlrec = (xl_btree_unlink_page *) rec;
- appendStringInfo(buf, "left %u; right %u; level %u; safexid %u:%u; ",
+ appendStringInfo(buf, "left %u; right %u; level %u; safexid " XID_FMT "; ",
xlrec->leftsib, xlrec->rightsib, xlrec->level,
- EpochFromFullTransactionId(xlrec->safexid),
XidFromFullTransactionId(xlrec->safexid));
appendStringInfo(buf, "leafleft %u; leafright %u; leaftopparent %u",
xlrec->leafleftsib, xlrec->leafrightsib,
@@ -100,10 +99,9 @@ btree_desc(StringInfo buf, XLogReaderState *record)
{
xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) rec;
- appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid %u:%u",
+ appendStringInfo(buf, "rel %u/%u/%u; latestRemovedXid " XID_FMT,
xlrec->node.spcNode, xlrec->node.dbNode,
xlrec->node.relNode,
- EpochFromFullTransactionId(xlrec->latestRemovedFullXid),
XidFromFullTransactionId(xlrec->latestRemovedFullXid));
break;
}
diff --git a/src/backend/access/rmgrdesc/spgdesc.c b/src/backend/access/rmgrdesc/spgdesc.c
index d5d921a42a1..a094ac738b4 100644
--- a/src/backend/access/rmgrdesc/spgdesc.c
+++ b/src/backend/access/rmgrdesc/spgdesc.c
@@ -118,7 +118,7 @@ spg_desc(StringInfo buf, XLogReaderState *record)
{
spgxlogVacuumRedirect *xlrec = (spgxlogVacuumRedirect *) rec;
- appendStringInfo(buf, "ntoplaceholder: %u, firstplaceholder: %u, newestredirectxid: %u",
+ appendStringInfo(buf, "ntoplaceholder: %u, firstplaceholder: %u, newestredirectxid: " XID_FMT,
xlrec->nToPlaceholder,
xlrec->firstPlaceholder,
xlrec->newestRedirectXid);
diff --git a/src/backend/access/rmgrdesc/standbydesc.c b/src/backend/access/rmgrdesc/standbydesc.c
index 2dba39e349c..57fa4b729ed 100644
--- a/src/backend/access/rmgrdesc/standbydesc.c
+++ b/src/backend/access/rmgrdesc/standbydesc.c
@@ -21,7 +21,7 @@ standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
{
int i;
- appendStringInfo(buf, "nextXid %u latestCompletedXid %u oldestRunningXid %u",
+ appendStringInfo(buf, "nextXid " XID_FMT " latestCompletedXid " XID_FMT " oldestRunningXid " XID_FMT,
xlrec->nextXid,
xlrec->latestCompletedXid,
xlrec->oldestRunningXid);
@@ -29,7 +29,7 @@ standby_desc_running_xacts(StringInfo buf, xl_running_xacts *xlrec)
{
appendStringInfo(buf, "; %d xacts:", xlrec->xcnt);
for (i = 0; i < xlrec->xcnt; i++)
- appendStringInfo(buf, " %u", xlrec->xids[i]);
+ appendStringInfo(buf, " " XID_FMT, xlrec->xids[i]);
}
if (xlrec->subxid_overflow)
@@ -48,7 +48,7 @@ standby_desc(StringInfo buf, XLogReaderState *record)
int i;
for (i = 0; i < xlrec->nlocks; i++)
- appendStringInfo(buf, "xid %u db %u rel %u ",
+ appendStringInfo(buf, "xid " XID_FMT " db %u rel %u ",
xlrec->locks[i].xid, xlrec->locks[i].dbOid,
xlrec->locks[i].relOid);
}
diff --git a/src/backend/access/rmgrdesc/xactdesc.c b/src/backend/access/rmgrdesc/xactdesc.c
index 025d556f6ce..d15de291850 100644
--- a/src/backend/access/rmgrdesc/xactdesc.c
+++ b/src/backend/access/rmgrdesc/xactdesc.c
@@ -99,7 +99,8 @@ ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *pars
{
xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
- parsed->twophase_xid = xl_twophase->xid;
+ parsed->twophase_xid =
+ ((uint64) xl_twophase->xid_hi << 32) | xl_twophase->xid_lo;
data += sizeof(xl_xact_twophase);
@@ -183,7 +184,8 @@ ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed)
{
xl_xact_twophase *xl_twophase = (xl_xact_twophase *) data;
- parsed->twophase_xid = xl_twophase->xid;
+ parsed->twophase_xid =
+ ((uint64) xl_twophase->xid_hi << 32) | xl_twophase->xid_lo;
data += sizeof(xl_xact_twophase);
@@ -276,7 +278,7 @@ xact_desc_subxacts(StringInfo buf, int nsubxacts, TransactionId *subxacts)
{
appendStringInfoString(buf, "; subxacts:");
for (i = 0; i < nsubxacts; i++)
- appendStringInfo(buf, " %u", subxacts[i]);
+ appendStringInfo(buf, " " XID_FMT, subxacts[i]);
}
}
@@ -289,7 +291,7 @@ xact_desc_commit(StringInfo buf, uint8 info, xl_xact_commit *xlrec, RepOriginId
/* If this is a prepared xact, show the xid of the original xact */
if (TransactionIdIsValid(parsed.twophase_xid))
- appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+ appendStringInfo(buf, XID_FMT ": ", parsed.twophase_xid);
appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
@@ -324,7 +326,7 @@ xact_desc_abort(StringInfo buf, uint8 info, xl_xact_abort *xlrec, RepOriginId or
/* If this is a prepared xact, show the xid of the original xact */
if (TransactionIdIsValid(parsed.twophase_xid))
- appendStringInfo(buf, "%u: ", parsed.twophase_xid);
+ appendStringInfo(buf, XID_FMT ": ", parsed.twophase_xid);
appendStringInfoString(buf, timestamptz_to_str(xlrec->xact_time));
@@ -377,7 +379,7 @@ xact_desc_assignment(StringInfo buf, xl_xact_assignment *xlrec)
appendStringInfoString(buf, "subxacts:");
for (i = 0; i < xlrec->nsubxacts; i++)
- appendStringInfo(buf, " %u", xlrec->xsub[i]);
+ appendStringInfo(buf, " " XID_FMT, xlrec->xsub[i]);
}
void
@@ -416,7 +418,7 @@ xact_desc(StringInfo buf, XLogReaderState *record)
* interested in the top-level xid that issued the record and which
* xids are being reported here.
*/
- appendStringInfo(buf, "xtop %u: ", xlrec->xtop);
+ appendStringInfo(buf, "xtop " XID_FMT ": ", xlrec->xtop);
xact_desc_assignment(buf, xlrec);
}
else if (info == XLOG_XACT_INVALIDATIONS)
diff --git a/src/backend/access/rmgrdesc/xlogdesc.c b/src/backend/access/rmgrdesc/xlogdesc.c
index e7452af6790..ee897a96c75 100644
--- a/src/backend/access/rmgrdesc/xlogdesc.c
+++ b/src/backend/access/rmgrdesc/xlogdesc.c
@@ -45,15 +45,14 @@ xlog_desc(StringInfo buf, XLogReaderState *record)
CheckPoint *checkpoint = (CheckPoint *) rec;
appendStringInfo(buf, "redo %X/%X; "
- "tli %u; prev tli %u; fpw %s; xid %u:%u; oid %u; multi %u; offset %u; "
- "oldest xid %u in DB %u; oldest multi %u in DB %u; "
- "oldest/newest commit timestamp xid: %u/%u; "
- "oldest running xid %u; %s",
+ "tli %u; prev tli %u; fpw %s; xid " XID_FMT "; oid %u; multi " XID_FMT "; offset " INT64_FORMAT "; "
+ "oldest xid " XID_FMT " in DB %u; oldest multi " XID_FMT " in DB %u; "
+ "oldest/newest commit timestamp xid: " XID_FMT "/" XID_FMT "; "
+ "oldest running xid " XID_FMT "; %s",
LSN_FORMAT_ARGS(checkpoint->redo),
checkpoint->ThisTimeLineID,
checkpoint->PrevTimeLineID,
checkpoint->fullPageWrites ? "true" : "false",
- EpochFromFullTransactionId(checkpoint->nextXid),
XidFromFullTransactionId(checkpoint->nextXid),
checkpoint->nextOid,
checkpoint->nextMulti,
diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c
index de787c3d375..6a3dee57385 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -89,24 +89,24 @@ static SlruCtlData XactCtlData;
#define XactCtl (&XactCtlData)
-static int ZeroCLOGPage(int pageno, bool writeXlog);
-static bool CLOGPagePrecedes(int page1, int page2);
-static void WriteZeroPageXlogRec(int pageno);
-static void WriteTruncateXlogRec(int pageno, TransactionId oldestXact,
+static int ZeroCLOGPage(int64 pageno, bool writeXlog);
+static bool CLOGPagePrecedes(int64 page1, int64 page2);
+static void WriteZeroPageXlogRec(int64 pageno);
+static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact,
Oid oldestXactDb);
static void TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
TransactionId *subxids, XidStatus status,
- XLogRecPtr lsn, int pageno,
+ XLogRecPtr lsn, int64 pageno,
bool all_xact_same_page);
static void TransactionIdSetStatusBit(TransactionId xid, XidStatus status,
XLogRecPtr lsn, int slotno);
static void set_status_by_pages(int nsubxids, TransactionId *subxids,
XidStatus status, XLogRecPtr lsn);
static bool TransactionGroupUpdateXidStatus(TransactionId xid,
- XidStatus status, XLogRecPtr lsn, int pageno);
+ XidStatus status, XLogRecPtr lsn, int64 pageno);
static void TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
TransactionId *subxids, XidStatus status,
- XLogRecPtr lsn, int pageno);
+ XLogRecPtr lsn, int64 pageno);
/*
@@ -164,7 +164,7 @@ void
TransactionIdSetTreeStatus(TransactionId xid, int nsubxids,
TransactionId *subxids, XidStatus status, XLogRecPtr lsn)
{
- int pageno = TransactionIdToPage(xid); /* get page of parent */
+ int64 pageno = TransactionIdToPage(xid); /* get page of parent */
int i;
Assert(status == TRANSACTION_STATUS_COMMITTED ||
@@ -238,7 +238,7 @@ static void
set_status_by_pages(int nsubxids, TransactionId *subxids,
XidStatus status, XLogRecPtr lsn)
{
- int pageno = TransactionIdToPage(subxids[0]);
+ int64 pageno = TransactionIdToPage(subxids[0]);
int offset = 0;
int i = 0;
@@ -247,7 +247,7 @@ set_status_by_pages(int nsubxids, TransactionId *subxids,
while (i < nsubxids)
{
int num_on_page = 0;
- int nextpageno;
+ int64 nextpageno;
do
{
@@ -273,7 +273,7 @@ set_status_by_pages(int nsubxids, TransactionId *subxids,
static void
TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
TransactionId *subxids, XidStatus status,
- XLogRecPtr lsn, int pageno,
+ XLogRecPtr lsn, int64 pageno,
bool all_xact_same_page)
{
/* Can't use group update when PGPROC overflows. */
@@ -338,7 +338,7 @@ TransactionIdSetPageStatus(TransactionId xid, int nsubxids,
static void
TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
TransactionId *subxids, XidStatus status,
- XLogRecPtr lsn, int pageno)
+ XLogRecPtr lsn, int64 pageno)
{
int slotno;
int i;
@@ -412,7 +412,7 @@ TransactionIdSetPageStatusInternal(TransactionId xid, int nsubxids,
*/
static bool
TransactionGroupUpdateXidStatus(TransactionId xid, XidStatus status,
- XLogRecPtr lsn, int pageno)
+ XLogRecPtr lsn, int64 pageno)
{
volatile PROC_HDR *procglobal = ProcGlobal;
PGPROC *proc = MyProc;
@@ -638,7 +638,7 @@ TransactionIdSetStatusBit(TransactionId xid, XidStatus status, XLogRecPtr lsn, i
XidStatus
TransactionIdGetStatus(TransactionId xid, XLogRecPtr *lsn)
{
- int pageno = TransactionIdToPage(xid);
+ int64 pageno = TransactionIdToPage(xid);
int byteno = TransactionIdToByte(xid);
int bshift = TransactionIdToBIndex(xid) * CLOG_BITS_PER_XACT;
int slotno;
@@ -712,6 +712,9 @@ void
BootStrapCLOG(void)
{
int slotno;
+ int64 pageno;
+
+ pageno = TransactionIdToPage(XidFromFullTransactionId(ShmemVariableCache->nextXid));
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
@@ -722,6 +725,16 @@ BootStrapCLOG(void)
SimpleLruWritePage(XactCtl, slotno);
Assert(!XactCtl->shared->page_dirty[slotno]);
+ if (pageno != 0)
+ {
+ /* Create and zero the first page of the commit log */
+ slotno = ZeroCLOGPage(pageno, false);
+
+ /* Make sure it's written out */
+ SimpleLruWritePage(XactCtl, slotno);
+ Assert(!XactCtl->shared->page_dirty[slotno]);
+ }
+
LWLockRelease(XactSLRULock);
}
@@ -735,7 +748,7 @@ BootStrapCLOG(void)
* Control lock must be held at entry, and will be held at exit.
*/
static int
-ZeroCLOGPage(int pageno, bool writeXlog)
+ZeroCLOGPage(int64 pageno, bool writeXlog)
{
int slotno;
@@ -755,7 +768,7 @@ void
StartupCLOG(void)
{
TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
- int pageno = TransactionIdToPage(xid);
+ int64 pageno = TransactionIdToPage(xid);
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
@@ -774,7 +787,7 @@ void
TrimCLOG(void)
{
TransactionId xid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
- int pageno = TransactionIdToPage(xid);
+ int64 pageno = TransactionIdToPage(xid);
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
@@ -839,7 +852,7 @@ CheckPointCLOG(void)
void
ExtendCLOG(TransactionId newestXact)
{
- int pageno;
+ int64 pageno;
/*
* No work except at first XID of a page. But beware: just after
@@ -878,7 +891,7 @@ ExtendCLOG(TransactionId newestXact)
void
TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
{
- int cutoffPage;
+ int64 cutoffPage;
/*
* The cutoff point is the start of the segment containing oldestXact. We
@@ -911,49 +924,26 @@ TruncateCLOG(TransactionId oldestXact, Oid oldestxid_datoid)
SimpleLruTruncate(XactCtl, cutoffPage);
}
-
/*
* Decide whether a CLOG page number is "older" for truncation purposes.
*
- * We need to use comparison of TransactionIds here in order to do the right
- * thing with wraparound XID arithmetic. However, TransactionIdPrecedes()
- * would get weird about permanent xact IDs. So, offset both such that xid1,
- * xid2, and xid2 + CLOG_XACTS_PER_PAGE - 1 are all normal XIDs; this offset
- * is relevant to page 0 and to the page preceding page 0.
- *
- * The page containing oldestXact-2^31 is the important edge case. The
- * portion of that page equaling or following oldestXact-2^31 is expendable,
- * but the portion preceding oldestXact-2^31 is not. When oldestXact-2^31 is
- * the first XID of a page and segment, the entire page and segment is
- * expendable, and we could truncate the segment. Recognizing that case would
- * require making oldestXact, not just the page containing oldestXact,
- * available to this callback. The benefit would be rare and small, so we
- * don't optimize that edge case.
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
static bool
-CLOGPagePrecedes(int page1, int page2)
+CLOGPagePrecedes(int64 page1, int64 page2)
{
- TransactionId xid1;
- TransactionId xid2;
-
- xid1 = ((TransactionId) page1) * CLOG_XACTS_PER_PAGE;
- xid1 += FirstNormalTransactionId + 1;
- xid2 = ((TransactionId) page2) * CLOG_XACTS_PER_PAGE;
- xid2 += FirstNormalTransactionId + 1;
-
- return (TransactionIdPrecedes(xid1, xid2) &&
- TransactionIdPrecedes(xid1, xid2 + CLOG_XACTS_PER_PAGE - 1));
+ return page1 < page2;
}
-
/*
* Write a ZEROPAGE xlog record
*/
static void
-WriteZeroPageXlogRec(int pageno)
+WriteZeroPageXlogRec(int64 pageno)
{
XLogBeginInsert();
- XLogRegisterData((char *) (&pageno), sizeof(int));
+ XLogRegisterData((char *) (&pageno), sizeof(int64));
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE);
}
@@ -964,7 +954,7 @@ WriteZeroPageXlogRec(int pageno)
* in TruncateCLOG().
*/
static void
-WriteTruncateXlogRec(int pageno, TransactionId oldestXact, Oid oldestXactDb)
+WriteTruncateXlogRec(int64 pageno, TransactionId oldestXact, Oid oldestXactDb)
{
XLogRecPtr recptr;
xl_clog_truncate xlrec;
@@ -992,10 +982,10 @@ clog_redo(XLogReaderState *record)
if (info == CLOG_ZEROPAGE)
{
- int pageno;
+ int64 pageno;
int slotno;
- memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+ memcpy(&pageno, XLogRecGetData(record), sizeof(int64));
LWLockAcquire(XactSLRULock, LW_EXCLUSIVE);
diff --git a/src/backend/access/transam/commit_ts.c b/src/backend/access/transam/commit_ts.c
index 9419a5ca416..f1d551577c7 100644
--- a/src/backend/access/transam/commit_ts.c
+++ b/src/backend/access/transam/commit_ts.c
@@ -105,16 +105,16 @@ bool track_commit_timestamp;
static void SetXidCommitTsInPage(TransactionId xid, int nsubxids,
TransactionId *subxids, TimestampTz ts,
- RepOriginId nodeid, int pageno);
+ RepOriginId nodeid, int64 pageno);
static void TransactionIdSetCommitTs(TransactionId xid, TimestampTz ts,
RepOriginId nodeid, int slotno);
static void error_commit_ts_disabled(void);
-static int ZeroCommitTsPage(int pageno, bool writeXlog);
-static bool CommitTsPagePrecedes(int page1, int page2);
+static int ZeroCommitTsPage(int64 pageno, bool writeXlog);
+static bool CommitTsPagePrecedes(int64 page1, int64 page2);
static void ActivateCommitTs(void);
static void DeactivateCommitTs(void);
-static void WriteZeroPageXlogRec(int pageno);
-static void WriteTruncateXlogRec(int pageno, TransactionId oldestXid);
+static void WriteZeroPageXlogRec(int64 pageno);
+static void WriteTruncateXlogRec(int64 pageno, TransactionId oldestXid);
/*
* TransactionTreeSetCommitTsData
@@ -172,7 +172,7 @@ TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
i = 0;
for (;;)
{
- int pageno = TransactionIdToCTsPage(headxid);
+ int64 pageno = TransactionIdToCTsPage(headxid);
int j;
for (j = i; j < nsubxids; j++)
@@ -216,7 +216,7 @@ TransactionTreeSetCommitTsData(TransactionId xid, int nsubxids,
static void
SetXidCommitTsInPage(TransactionId xid, int nsubxids,
TransactionId *subxids, TimestampTz ts,
- RepOriginId nodeid, int pageno)
+ RepOriginId nodeid, int64 pageno)
{
int slotno;
int i;
@@ -268,7 +268,7 @@ bool
TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
RepOriginId *nodeid)
{
- int pageno = TransactionIdToCTsPage(xid);
+ int64 pageno = TransactionIdToCTsPage(xid);
int entryno = TransactionIdToCTsEntry(xid);
int slotno;
CommitTimestampEntry entry;
@@ -278,7 +278,7 @@ TransactionIdGetCommitTsData(TransactionId xid, TimestampTz *ts,
if (!TransactionIdIsValid(xid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("cannot retrieve commit timestamp for transaction %u", xid)));
+ errmsg("cannot retrieve commit timestamp for transaction %" PRIu64, xid)));
else if (!TransactionIdIsNormal(xid))
{
/* frozen and bootstrap xids are always committed far in the past */
@@ -589,7 +589,7 @@ BootStrapCommitTs(void)
* Control lock must be held at entry, and will be held at exit.
*/
static int
-ZeroCommitTsPage(int pageno, bool writeXlog)
+ZeroCommitTsPage(int64 pageno, bool writeXlog)
{
int slotno;
@@ -682,7 +682,7 @@ static void
ActivateCommitTs(void)
{
TransactionId xid;
- int pageno;
+ int64 pageno;
/* If we've done this already, there's nothing to do */
LWLockAcquire(CommitTsLock, LW_EXCLUSIVE);
@@ -815,7 +815,7 @@ CheckPointCommitTs(void)
void
ExtendCommitTs(TransactionId newestXact)
{
- int pageno;
+ int64 pageno;
/*
* Nothing to do if module not enabled. Note we do an unlocked read of
@@ -853,7 +853,7 @@ ExtendCommitTs(TransactionId newestXact)
void
TruncateCommitTs(TransactionId oldestXact)
{
- int cutoffPage;
+ int64 cutoffPage;
/*
* The cutoff point is the start of the segment containing oldestXact. We
@@ -918,49 +918,23 @@ AdvanceOldestCommitTsXid(TransactionId oldestXact)
* Decide whether a commitTS page number is "older" for truncation purposes.
* Analogous to CLOGPagePrecedes().
*
- * At default BLCKSZ, (1 << 31) % COMMIT_TS_XACTS_PER_PAGE == 128. This
- * introduces differences compared to CLOG and the other SLRUs having (1 <<
- * 31) % per_page == 0. This function never tests exactly
- * TransactionIdPrecedes(x-2^31, x). When the system reaches xidStopLimit,
- * there are two possible counts of page boundaries between oldestXact and the
- * latest XID assigned, depending on whether oldestXact is within the first
- * 128 entries of its page. Since this function doesn't know the location of
- * oldestXact within page2, it returns false for one page that actually is
- * expendable. This is a wider (yet still negligible) version of the
- * truncation opportunity that CLOGPagePrecedes() cannot recognize.
- *
- * For the sake of a worked example, number entries with decimal values such
- * that page1==1 entries range from 1.0 to 1.999. Let N+0.15 be the number of
- * pages that 2^31 entries will span (N is an integer). If oldestXact=N+2.1,
- * then the final safe XID assignment leaves newestXact=1.95. We keep page 2,
- * because entry=2.85 is the border that toggles whether entries precede the
- * last entry of the oldestXact page. While page 2 is expendable at
- * oldestXact=N+2.1, it would be precious at oldestXact=N+2.9.
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
static bool
-CommitTsPagePrecedes(int page1, int page2)
+CommitTsPagePrecedes(int64 page1, int64 page2)
{
- TransactionId xid1;
- TransactionId xid2;
-
- xid1 = ((TransactionId) page1) * COMMIT_TS_XACTS_PER_PAGE;
- xid1 += FirstNormalTransactionId + 1;
- xid2 = ((TransactionId) page2) * COMMIT_TS_XACTS_PER_PAGE;
- xid2 += FirstNormalTransactionId + 1;
-
- return (TransactionIdPrecedes(xid1, xid2) &&
- TransactionIdPrecedes(xid1, xid2 + COMMIT_TS_XACTS_PER_PAGE - 1));
+ return page1 < page2;
}
-
/*
* Write a ZEROPAGE xlog record
*/
static void
-WriteZeroPageXlogRec(int pageno)
+WriteZeroPageXlogRec(int64 pageno)
{
XLogBeginInsert();
- XLogRegisterData((char *) (&pageno), sizeof(int));
+ XLogRegisterData((char *) (&pageno), sizeof(int64));
(void) XLogInsert(RM_COMMIT_TS_ID, COMMIT_TS_ZEROPAGE);
}
@@ -968,7 +942,7 @@ WriteZeroPageXlogRec(int pageno)
* Write a TRUNCATE xlog record
*/
static void
-WriteTruncateXlogRec(int pageno, TransactionId oldestXid)
+WriteTruncateXlogRec(int64 pageno, TransactionId oldestXid)
{
xl_commit_ts_truncate xlrec;
@@ -993,10 +967,10 @@ commit_ts_redo(XLogReaderState *record)
if (info == COMMIT_TS_ZEROPAGE)
{
- int pageno;
+ int64 pageno;
int slotno;
- memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+ memcpy(&pageno, XLogRecGetData(record), sizeof(int64));
LWLockAcquire(CommitTsSLRULock, LW_EXCLUSIVE);
diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c
index 806f2e43ba9..e44a8b85453 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -112,15 +112,15 @@
((xid) / (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
#define MultiXactIdToOffsetEntry(xid) \
((xid) % (MultiXactOffset) MULTIXACT_OFFSETS_PER_PAGE)
-#define MultiXactIdToOffsetSegment(xid) (MultiXactIdToOffsetPage(xid) / SLRU_PAGES_PER_SEGMENT)
+#define MultiXactIdToOffsetSegment(xid) ((uint64)(MultiXactIdToOffsetPage(xid) / SLRU_PAGES_PER_SEGMENT))
/*
* The situation for members is a bit more complex: we store one byte of
* additional flag bits for each TransactionId. To do this without getting
- * into alignment issues, we store four bytes of flags, and then the
- * corresponding 4 Xids. Each such 5-word (20-byte) set we call a "group", and
- * are stored as a whole in pages. Thus, with 8kB BLCKSZ, we keep 409 groups
- * per page. This wastes 12 bytes per page, but that's OK -- simplicity (and
+ * into alignment issues, we store eight bytes of flags, and then the
+ * corresponding 8 Xids. Each such 9-word (72-byte) set we call a "group", and
+ * are stored as a whole in pages. Thus, with 8kB BLCKSZ, we keep 113 groups
+ * per page. This wastes 56 bytes per page, but that's OK -- simplicity (and
* performance) trumps space efficiency here.
*
* Note that the "offset" macros work with byte offset, not array indexes, so
@@ -132,7 +132,7 @@
#define MXACT_MEMBER_XACT_BITMASK ((1 << MXACT_MEMBER_BITS_PER_XACT) - 1)
/* how many full bytes of flags are there in a group? */
-#define MULTIXACT_FLAGBYTES_PER_GROUP 4
+#define MULTIXACT_FLAGBYTES_PER_GROUP 8
#define MULTIXACT_MEMBERS_PER_MEMBERGROUP \
(MULTIXACT_FLAGBYTES_PER_GROUP * MXACT_MEMBER_FLAGS_PER_BYTE)
/* size in bytes of a complete group */
@@ -142,22 +142,9 @@
#define MULTIXACT_MEMBERS_PER_PAGE \
(MULTIXACT_MEMBERGROUPS_PER_PAGE * MULTIXACT_MEMBERS_PER_MEMBERGROUP)
-/*
- * Because the number of items per page is not a divisor of the last item
- * number (member 0xFFFFFFFF), the last segment does not use the maximum number
- * of pages, and moreover the last used page therein does not use the same
- * number of items as previous pages. (Another way to say it is that the
- * 0xFFFFFFFF member is somewhere in the middle of the last page, so the page
- * has some empty space after that item.)
- *
- * This constant is the number of members in the last page of the last segment.
- */
-#define MAX_MEMBERS_IN_LAST_MEMBERS_PAGE \
- ((uint32) ((0xFFFFFFFF % MULTIXACT_MEMBERS_PER_PAGE) + 1))
-
/* page in which a member is to be found */
#define MXOffsetToMemberPage(xid) ((xid) / (TransactionId) MULTIXACT_MEMBERS_PER_PAGE)
-#define MXOffsetToMemberSegment(xid) (MXOffsetToMemberPage(xid) / SLRU_PAGES_PER_SEGMENT)
+#define MXOffsetToMemberSegment(xid) ((uint64)(MXOffsetToMemberPage(xid) / SLRU_PAGES_PER_SEGMENT))
/* Location (byte offset within page) of flag word for a given member */
#define MXOffsetToFlagsOffset(xid) \
@@ -216,22 +203,8 @@ typedef struct MultiXactStateData
MultiXactId oldestMultiXactId;
Oid oldestMultiXactDB;
- /*
- * Oldest multixact offset that is potentially referenced by a multixact
- * referenced by a relation. We don't always know this value, so there's
- * a flag here to indicate whether or not we currently do.
- */
- MultiXactOffset oldestOffset;
- bool oldestOffsetKnown;
-
/* support for anti-wraparound measures */
MultiXactId multiVacLimit;
- MultiXactId multiWarnLimit;
- MultiXactId multiStopLimit;
- MultiXactId multiWrapLimit;
-
- /* support for members anti-wraparound measures */
- MultiXactOffset offsetStopLimit; /* known if oldestOffsetKnown */
/*
* Per-backend data starts here. We have two arrays stored in the area
@@ -353,19 +326,16 @@ static void mXactCachePut(MultiXactId multi, int nmembers,
static char *mxstatus_to_string(MultiXactStatus status);
/* management of SLRU infrastructure */
-static int ZeroMultiXactOffsetPage(int pageno, bool writeXlog);
-static int ZeroMultiXactMemberPage(int pageno, bool writeXlog);
-static bool MultiXactOffsetPagePrecedes(int page1, int page2);
-static bool MultiXactMemberPagePrecedes(int page1, int page2);
+static int ZeroMultiXactOffsetPage(int64 pageno, bool writeXlog);
+static int ZeroMultiXactMemberPage(int64 pageno, bool writeXlog);
+static bool MultiXactOffsetPagePrecedes(int64 page1, int64 page2);
+static bool MultiXactMemberPagePrecedes(int64 page1, int64 page2);
static bool MultiXactOffsetPrecedes(MultiXactOffset offset1,
MultiXactOffset offset2);
static void ExtendMultiXactOffset(MultiXactId multi);
static void ExtendMultiXactMember(MultiXactOffset offset, int nmembers);
-static bool MultiXactOffsetWouldWrap(MultiXactOffset boundary,
- MultiXactOffset start, uint32 distance);
-static bool SetOffsetVacuumLimit(bool is_startup);
static bool find_multixact_start(MultiXactId multi, MultiXactOffset *result);
-static void WriteMZeroPageXlogRec(int pageno, uint8 info);
+static void WriteMZeroPageXlogRec(int64 pageno, uint8 info);
static void WriteMTruncateXlogRec(Oid oldestMultiDB,
MultiXactId startTruncOff,
MultiXactId endTruncOff,
@@ -397,6 +367,9 @@ MultiXactIdCreate(TransactionId xid1, MultiXactStatus status1,
/* MultiXactIdSetOldestMember() must have been called already. */
Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));
+ /* memset members array because with 64-bit xids it has a padding hole */
+ MemSet(members, 0, sizeof(members));
+
/*
* Note: unlike MultiXactIdExpand, we don't bother to check that both XIDs
* are still running. In typical usage, xid2 will be our own XID and the
@@ -451,7 +424,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
/* MultiXactIdSetOldestMember() must have been called already. */
Assert(MultiXactIdIsValid(OldestMemberMXactId[MyBackendId]));
- debug_elog5(DEBUG2, "Expand: received multi %u, xid %u status %s",
+ debug_elog5(DEBUG2, "Expand: received multi " XID_FMT ", xid " XID_FMT " status %s",
multi, xid, mxstatus_to_string(status));
/*
@@ -476,7 +449,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
member.status = status;
newMulti = MultiXactIdCreateFromMembers(1, &member);
- debug_elog4(DEBUG2, "Expand: %u has no members, create singleton %u",
+ debug_elog4(DEBUG2, "Expand: " XID_FMT " has no members, create singleton " XID_FMT,
multi, newMulti);
return newMulti;
}
@@ -490,7 +463,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
if (TransactionIdEquals(members[i].xid, xid) &&
(members[i].status == status))
{
- debug_elog4(DEBUG2, "Expand: %u is already a member of %u",
+ debug_elog4(DEBUG2, "Expand: " XID_FMT " is already a member of " XID_FMT,
xid, multi);
pfree(members);
return multi;
@@ -511,7 +484,7 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
* end of the loop.
*/
newMembers = (MultiXactMember *)
- palloc(sizeof(MultiXactMember) * (nmembers + 1));
+ palloc0(sizeof(MultiXactMember) * (nmembers + 1));
for (i = 0, j = 0; i < nmembers; i++)
{
@@ -526,12 +499,13 @@ MultiXactIdExpand(MultiXactId multi, TransactionId xid, MultiXactStatus status)
newMembers[j].xid = xid;
newMembers[j++].status = status;
+
newMulti = MultiXactIdCreateFromMembers(j, newMembers);
pfree(members);
pfree(newMembers);
- debug_elog3(DEBUG2, "Expand: returning new multi %u", newMulti);
+ debug_elog3(DEBUG2, "Expand: returning new multi " XID_FMT, newMulti);
return newMulti;
}
@@ -554,7 +528,7 @@ MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
int nmembers;
int i;
- debug_elog3(DEBUG2, "IsRunning %u?", multi);
+ debug_elog3(DEBUG2, "IsRunning " XID_FMT "?", multi);
/*
* "false" here means we assume our callers have checked that the given
@@ -594,7 +568,7 @@ MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
{
if (TransactionIdIsInProgress(members[i].xid))
{
- debug_elog4(DEBUG2, "IsRunning: member %d (%u) is running",
+ debug_elog4(DEBUG2, "IsRunning: member %d (" XID_FMT ") is running",
i, members[i].xid);
pfree(members);
return true;
@@ -603,7 +577,7 @@ MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly)
pfree(members);
- debug_elog3(DEBUG2, "IsRunning: %u is not running", multi);
+ debug_elog3(DEBUG2, "IsRunning: " XID_FMT " is not running", multi);
return false;
}
@@ -657,7 +631,7 @@ MultiXactIdSetOldestMember(void)
LWLockRelease(MultiXactGenLock);
- debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = %u",
+ debug_elog4(DEBUG2, "MultiXact: setting OldestMember[%d] = " XID_FMT,
MyBackendId, nextMXact);
}
}
@@ -710,7 +684,7 @@ MultiXactIdSetOldestVisible(void)
LWLockRelease(MultiXactGenLock);
- debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = %u",
+ debug_elog4(DEBUG2, "MultiXact: setting OldestVisible[%d] = " XID_FMT,
MyBackendId, oldestMXact);
}
}
@@ -729,9 +703,6 @@ ReadNextMultiXactId(void)
mxid = MultiXactState->nextMXact;
LWLockRelease(MultiXactGenLock);
- if (mxid < FirstMultiXactId)
- mxid = FirstMultiXactId;
-
return mxid;
}
@@ -862,8 +833,8 @@ static void
RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
int nmembers, MultiXactMember *members)
{
- int pageno;
- int prev_pageno;
+ int64 pageno;
+ int64 prev_pageno;
int entryno;
int slotno;
MultiXactOffset *offptr;
@@ -899,8 +870,8 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
for (i = 0; i < nmembers; i++, offset++)
{
TransactionId *memberptr;
- uint32 *flagsptr;
- uint32 flagsval;
+ uint64 *flagsptr;
+ uint64 flagsval;
int bshift;
int flagsoff;
int memberoff;
@@ -921,14 +892,15 @@ RecordNewMultiXact(MultiXactId multi, MultiXactOffset offset,
memberptr = (TransactionId *)
(MultiXactMemberCtl->shared->page_buffer[slotno] + memberoff);
+
*memberptr = members[i].xid;
- flagsptr = (uint32 *)
+ flagsptr = (uint64 *)
(MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
flagsval = *flagsptr;
- flagsval &= ~(((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
- flagsval |= (members[i].status << bshift);
+ flagsval &= ~((uint64)((1 << MXACT_MEMBER_BITS_PER_XACT) - 1) << bshift);
+ flagsval |= ((uint64)members[i].status << bshift);
*flagsptr = flagsval;
MultiXactMemberCtl->shared->page_dirty[slotno] = true;
@@ -981,8 +953,6 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
* If we're past multiVacLimit or the safe threshold for member storage
* space, or we don't know what the safe threshold for member storage is,
* start trying to force autovacuum cycles.
- * If we're past multiWarnLimit, start issuing warnings.
- * If we're past multiStopLimit, refuse to create new MultiXactIds.
*
* Note these are pretty much the same protections in GetNewTransactionId.
*----------
@@ -996,41 +966,9 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
* possibility of deadlock while doing get_database_name(). First,
* copy all the shared values we'll need in this path.
*/
- MultiXactId multiWarnLimit = MultiXactState->multiWarnLimit;
- MultiXactId multiStopLimit = MultiXactState->multiStopLimit;
- MultiXactId multiWrapLimit = MultiXactState->multiWrapLimit;
- Oid oldest_datoid = MultiXactState->oldestMultiXactDB;
LWLockRelease(MultiXactGenLock);
- if (IsUnderPostmaster &&
- !MultiXactIdPrecedes(result, multiStopLimit))
- {
- char *oldest_datname = get_database_name(oldest_datoid);
-
- /*
- * Immediately kick autovacuum into action as we're already in
- * ERROR territory.
- */
- SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
-
- /* complain even if that DB has disappeared */
- if (oldest_datname)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database \"%s\"",
- oldest_datname),
- errhint("Execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("database is not accepting commands that generate new MultiXactIds to avoid wraparound data loss in database with OID %u",
- oldest_datoid),
- errhint("Execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
-
/*
* To avoid swamping the postmaster with signals, we issue the autovac
* request only once per 64K multis generated. This still gives
@@ -1039,36 +977,9 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
if (IsUnderPostmaster && (result % 65536) == 0)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
- if (!MultiXactIdPrecedes(result, multiWarnLimit))
- {
- char *oldest_datname = get_database_name(oldest_datoid);
-
- /* complain even if that DB has disappeared */
- if (oldest_datname)
- ereport(WARNING,
- (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used",
- "database \"%s\" must be vacuumed before %u more MultiXactIds are used",
- multiWrapLimit - result,
- oldest_datname,
- multiWrapLimit - result),
- errhint("Execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(WARNING,
- (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
- "database with OID %u must be vacuumed before %u more MultiXactIds are used",
- multiWrapLimit - result,
- oldest_datoid,
- multiWrapLimit - result),
- errhint("Execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
-
/* Re-acquire lock and start over */
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
result = MultiXactState->nextMXact;
- if (result < FirstMultiXactId)
- result = FirstMultiXactId;
}
/* Make sure there is room for the MXID in the file. */
@@ -1088,78 +999,6 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
else
*offset = nextOffset;
- /*----------
- * Protect against overrun of the members space as well, with the
- * following rules:
- *
- * If we're past offsetStopLimit, refuse to generate more multis.
- * If we're close to offsetStopLimit, emit a warning.
- *
- * Arbitrarily, we start emitting warnings when we're 20 segments or less
- * from offsetStopLimit.
- *
- * Note we haven't updated the shared state yet, so if we fail at this
- * point, the multixact ID we grabbed can still be used by the next guy.
- *
- * Note that there is no point in forcing autovacuum runs here: the
- * multixact freeze settings would have to be reduced for that to have any
- * effect.
- *----------
- */
-#define OFFSET_WARN_SEGMENTS 20
- if (MultiXactState->oldestOffsetKnown &&
- MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit, nextOffset,
- nmembers))
- {
- /* see comment in the corresponding offsets wraparound case */
- SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
-
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("multixact \"members\" limit exceeded"),
- errdetail_plural("This command would create a multixact with %u members, but the remaining space is only enough for %u member.",
- "This command would create a multixact with %u members, but the remaining space is only enough for %u members.",
- MultiXactState->offsetStopLimit - nextOffset - 1,
- nmembers,
- MultiXactState->offsetStopLimit - nextOffset - 1),
- errhint("Execute a database-wide VACUUM in database with OID %u with reduced vacuum_multixact_freeze_min_age and vacuum_multixact_freeze_table_age settings.",
- MultiXactState->oldestMultiXactDB)));
- }
-
- /*
- * Check whether we should kick autovacuum into action, to prevent members
- * wraparound. NB we use a much larger window to trigger autovacuum than
- * just the warning limit. The warning is just a measure of last resort -
- * this is in line with GetNewTransactionId's behaviour.
- */
- if (!MultiXactState->oldestOffsetKnown ||
- (MultiXactState->nextOffset - MultiXactState->oldestOffset
- > MULTIXACT_MEMBER_SAFE_THRESHOLD))
- {
- /*
- * To avoid swamping the postmaster with signals, we issue the autovac
- * request only when crossing a segment boundary. With default
- * compilation settings that's roughly after 50k members. This still
- * gives plenty of chances before we get into real trouble.
- */
- if ((MXOffsetToMemberPage(nextOffset) / SLRU_PAGES_PER_SEGMENT) !=
- (MXOffsetToMemberPage(nextOffset + nmembers) / SLRU_PAGES_PER_SEGMENT))
- SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
- }
-
- if (MultiXactState->oldestOffsetKnown &&
- MultiXactOffsetWouldWrap(MultiXactState->offsetStopLimit,
- nextOffset,
- nmembers + MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT * OFFSET_WARN_SEGMENTS))
- ereport(WARNING,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg_plural("database with OID %u must be vacuumed before %d more multixact member is used",
- "database with OID %u must be vacuumed before %d more multixact members are used",
- MultiXactState->offsetStopLimit - nextOffset + nmembers,
- MultiXactState->oldestMultiXactDB,
- MultiXactState->offsetStopLimit - nextOffset + nmembers),
- errhint("Execute a database-wide VACUUM in that database with reduced vacuum_multixact_freeze_min_age and vacuum_multixact_freeze_table_age settings.")));
-
ExtendMultiXactMember(nextOffset, nmembers);
/*
@@ -1188,7 +1027,7 @@ GetNewMultiXactId(int nmembers, MultiXactOffset *offset)
LWLockRelease(MultiXactGenLock);
- debug_elog4(DEBUG2, "GetNew: returning %u offset %u", result, *offset);
+ debug_elog4(DEBUG2, "GetNew: returning " XID_FMT " offset " INT64_FORMAT, result, *offset);
return result;
}
@@ -1223,8 +1062,8 @@ int
GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
bool from_pgupgrade, bool onlyLock)
{
- int pageno;
- int prev_pageno;
+ int64 pageno;
+ int64 prev_pageno;
int entryno;
int slotno;
MultiXactOffset *offptr;
@@ -1232,13 +1071,12 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
int length;
int truelength;
int i;
- MultiXactId oldestMXact;
MultiXactId nextMXact;
MultiXactId tmpMXact;
MultiXactOffset nextOffset;
MultiXactMember *ptr;
- debug_elog3(DEBUG2, "GetMembers: asked for %u", multi);
+ debug_elog3(DEBUG2, "GetMembers: asked for " XID_FMT, multi);
if (!MultiXactIdIsValid(multi) || from_pgupgrade)
{
@@ -1288,24 +1126,11 @@ GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **members,
*/
LWLockAcquire(MultiXactGenLock, LW_SHARED);
- oldestMXact = MultiXactState->oldestMultiXactId;
nextMXact = MultiXactState->nextMXact;
nextOffset = MultiXactState->nextOffset;
LWLockRelease(MultiXactGenLock);
- if (MultiXactIdPrecedes(multi, oldestMXact))
- ereport(ERROR,
- (errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("MultiXactId %u does no longer exist -- apparent wraparound",
- multi)));
-
- if (!MultiXactIdPrecedes(multi, nextMXact))
- ereport(ERROR,
- (errcode(ERRCODE_INTERNAL_ERROR),
- errmsg("MultiXactId %u has not been created yet -- apparent wraparound",
- multi)));
-
/*
* Find out the offset at which we need to start reading MultiXactMembers
* and the number of members in the multixact. We determine the latter as
@@ -1350,7 +1175,9 @@ retry:
offptr += entryno;
offset = *offptr;
- Assert(offset != 0);
+ if (offset == 0)
+ ereport(ERROR,
+ (errmsg("found invalid zero offset in multixact " XID_FMT, multi)));
/*
* Use the same increment rule as GetNewMultiXactId(), that is, don't
@@ -1367,10 +1194,6 @@ retry:
{
MultiXactOffset nextMXOffset;
- /* handle wraparound if needed */
- if (tmpMXact < FirstMultiXactId)
- tmpMXact = FirstMultiXactId;
-
prev_pageno = pageno;
pageno = MultiXactIdToOffsetPage(tmpMXact);
@@ -1397,7 +1220,7 @@ retry:
LWLockRelease(MultiXactOffsetSLRULock);
- ptr = (MultiXactMember *) palloc(length * sizeof(MultiXactMember));
+ ptr = (MultiXactMember *) palloc0(length * sizeof(MultiXactMember));
/* Now get the members themselves. */
LWLockAcquire(MultiXactMemberSLRULock, LW_EXCLUSIVE);
@@ -1407,7 +1230,7 @@ retry:
for (i = 0; i < length; i++, offset++)
{
TransactionId *xactptr;
- uint32 *flagsptr;
+ uint64 *flagsptr;
int flagsoff;
int bshift;
int memberoff;
@@ -1433,7 +1256,7 @@ retry:
flagsoff = MXOffsetToFlagsOffset(offset);
bshift = MXOffsetToFlagsBitShift(offset);
- flagsptr = (uint32 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
+ flagsptr = (uint64 *) (MultiXactMemberCtl->shared->page_buffer[slotno] + flagsoff);
ptr[truelength].xid = *xactptr;
ptr[truelength].status = (*flagsptr >> bshift) & MXACT_MEMBER_XACT_BITMASK;
@@ -1517,7 +1340,7 @@ mXactCacheGetBySet(int nmembers, MultiXactMember *members)
*/
if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
{
- debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
+ debug_elog3(DEBUG2, "CacheGet: found " XID_FMT, entry->multi);
dlist_move_head(&MXactCache, iter.cur);
return entry->multi;
}
@@ -1540,7 +1363,7 @@ mXactCacheGetById(MultiXactId multi, MultiXactMember **members)
{
dlist_iter iter;
- debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
+ debug_elog3(DEBUG2, "CacheGet: looking for " XID_FMT, multi);
dlist_foreach(iter, &MXactCache)
{
@@ -1621,7 +1444,7 @@ mXactCachePut(MultiXactId multi, int nmembers, MultiXactMember *members)
MXactCacheMembers--;
entry = dlist_container(mXactCacheEnt, node, node);
- debug_elog3(DEBUG2, "CachePut: pruning cached multi %u",
+ debug_elog3(DEBUG2, "CachePut: pruning cached multi " XID_FMT,
entry->multi);
pfree(entry);
@@ -1663,11 +1486,11 @@ mxid_to_string(MultiXactId multi, int nmembers, MultiXactMember *members)
initStringInfo(&buf);
- appendStringInfo(&buf, "%u %d[%u (%s)", multi, nmembers, members[0].xid,
+ appendStringInfo(&buf, XID_FMT " %d[" XID_FMT " (%s)", multi, nmembers, members[0].xid,
mxstatus_to_string(members[0].status));
for (i = 1; i < nmembers; i++)
- appendStringInfo(&buf, ", %u (%s)", members[i].xid,
+ appendStringInfo(&buf, ", " XID_FMT " (%s)", members[i].xid,
mxstatus_to_string(members[i].status));
appendStringInfoChar(&buf, ']');
@@ -1894,6 +1717,8 @@ void
BootStrapMultiXact(void)
{
int slotno;
+ int64 multiOffsetPageno;
+ int64 multiMemberPageno;
LWLockAcquire(MultiXactOffsetSLRULock, LW_EXCLUSIVE);
@@ -1904,6 +1729,17 @@ BootStrapMultiXact(void)
SimpleLruWritePage(MultiXactOffsetCtl, slotno);
Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
+ multiOffsetPageno = MultiXactIdToOffsetPage(MultiXactState->nextMXact - 1);
+ if (multiOffsetPageno != 0)
+ {
+ /* Create and zero the first page of the offsets log */
+ slotno = ZeroMultiXactOffsetPage(multiOffsetPageno, false);
+
+ /* Make sure it's written out */
+ SimpleLruWritePage(MultiXactOffsetCtl, slotno);
+ Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
+ }
+
LWLockRelease(MultiXactOffsetSLRULock);
LWLockAcquire(MultiXactMemberSLRULock, LW_EXCLUSIVE);
@@ -1915,7 +1751,32 @@ BootStrapMultiXact(void)
SimpleLruWritePage(MultiXactMemberCtl, slotno);
Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
+ multiMemberPageno = MXOffsetToMemberPage(MultiXactState->nextOffset - 1);
+ if (multiMemberPageno != 0)
+ {
+ /* Create and zero the first page of the members log */
+ slotno = ZeroMultiXactMemberPage(multiMemberPageno, false);
+
+ /* Make sure it's written out */
+ SimpleLruWritePage(MultiXactMemberCtl, slotno);
+ Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
+ }
+
LWLockRelease(MultiXactMemberSLRULock);
+
+ /*
+ * If we're starting not from zero offset, initilize dummy multixact to
+ * evade too long loop in PerformMembersTruncation().
+ */
+ if (MultiXactState->nextOffset > 0 && MultiXactState->nextMXact > 0)
+ {
+ RecordNewMultiXact(FirstMultiXactId,
+ MultiXactState->nextOffset,
+ 0, NULL);
+ RecordNewMultiXact(MultiXactState->nextMXact - 1,
+ MultiXactState->nextOffset,
+ 0, NULL);
+ }
}
/*
@@ -1928,7 +1789,7 @@ BootStrapMultiXact(void)
* Control lock must be held at entry, and will be held at exit.
*/
static int
-ZeroMultiXactOffsetPage(int pageno, bool writeXlog)
+ZeroMultiXactOffsetPage(int64 pageno, bool writeXlog)
{
int slotno;
@@ -1944,7 +1805,7 @@ ZeroMultiXactOffsetPage(int pageno, bool writeXlog)
* Ditto, for MultiXactMember
*/
static int
-ZeroMultiXactMemberPage(int pageno, bool writeXlog)
+ZeroMultiXactMemberPage(int64 pageno, bool writeXlog)
{
int slotno;
@@ -1974,7 +1835,7 @@ ZeroMultiXactMemberPage(int pageno, bool writeXlog)
static void
MaybeExtendOffsetSlru(void)
{
- int pageno;
+ int64 pageno;
pageno = MultiXactIdToOffsetPage(MultiXactState->nextMXact);
@@ -2009,7 +1870,7 @@ StartupMultiXact(void)
{
MultiXactId multi = MultiXactState->nextMXact;
MultiXactOffset offset = MultiXactState->nextOffset;
- int pageno;
+ int64 pageno;
/*
* Initialize offset's idea of the latest page number.
@@ -2034,7 +1895,7 @@ TrimMultiXact(void)
MultiXactOffset offset;
MultiXactId oldestMXact;
Oid oldestMXactDB;
- int pageno;
+ int64 pageno;
int entryno;
int flagsoff;
@@ -2144,7 +2005,7 @@ MultiXactGetCheckptMulti(bool is_shutdown,
LWLockRelease(MultiXactGenLock);
debug_elog6(DEBUG2,
- "MultiXact: checkpoint is nextMulti %u, nextOffset %u, oldestMulti %u in DB %u",
+ "MultiXact: checkpoint is nextMulti " XID_FMT ", nextOffset " INT64_FORMAT ", oldestMulti " XID_FMT " in DB %u",
*nextMulti, *nextMultiOffset, *oldestMulti, *oldestMultiDB);
}
@@ -2179,7 +2040,7 @@ void
MultiXactSetNextMXact(MultiXactId nextMulti,
MultiXactOffset nextMultiOffset)
{
- debug_elog4(DEBUG2, "MultiXact: setting next multi to %u offset %u",
+ debug_elog4(DEBUG2, "MultiXact: setting next multi to " XID_FMT " offset " INT64_FORMAT,
nextMulti, nextMultiOffset);
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->nextMXact = nextMulti;
@@ -2214,47 +2075,9 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid,
bool is_startup)
{
MultiXactId multiVacLimit;
- MultiXactId multiWarnLimit;
- MultiXactId multiStopLimit;
- MultiXactId multiWrapLimit;
- MultiXactId curMulti;
- bool needs_offset_vacuum;
Assert(MultiXactIdIsValid(oldest_datminmxid));
- /*
- * We pretend that a wrap will happen halfway through the multixact ID
- * space, but that's not really true, because multixacts wrap differently
- * from transaction IDs. Note that, separately from any concern about
- * multixact IDs wrapping, we must ensure that multixact members do not
- * wrap. Limits for that are set in SetOffsetVacuumLimit, not here.
- */
- multiWrapLimit = oldest_datminmxid + (MaxMultiXactId >> 1);
- if (multiWrapLimit < FirstMultiXactId)
- multiWrapLimit += FirstMultiXactId;
-
- /*
- * We'll refuse to continue assigning MultiXactIds once we get within 3M
- * multi of data loss. See SetTransactionIdLimit.
- */
- multiStopLimit = multiWrapLimit - 3000000;
- if (multiStopLimit < FirstMultiXactId)
- multiStopLimit -= FirstMultiXactId;
-
- /*
- * We'll start complaining loudly when we get within 40M multis of data
- * loss. This is kind of arbitrary, but if you let your gas gauge get
- * down to 2% of full, would you be looking for the next gas station? We
- * need to be fairly liberal about this number because there are lots of
- * scenarios where most transactions are done by automatic clients that
- * won't pay attention to warnings. (No, we're not gonna make this
- * configurable. If you know enough to configure it, you know enough to
- * not get in this kind of trouble in the first place.)
- */
- multiWarnLimit = multiWrapLimit - 40000000;
- if (multiWarnLimit < FirstMultiXactId)
- multiWarnLimit -= FirstMultiXactId;
-
/*
* We'll start trying to force autovacuums when oldest_datminmxid gets to
* be more than autovacuum_multixact_freeze_max_age mxids old.
@@ -2264,25 +2087,14 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid,
* its value. See SetTransactionIdLimit.
*/
multiVacLimit = oldest_datminmxid + autovacuum_multixact_freeze_max_age;
- if (multiVacLimit < FirstMultiXactId)
- multiVacLimit += FirstMultiXactId;
/* Grab lock for just long enough to set the new limit values */
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
MultiXactState->oldestMultiXactId = oldest_datminmxid;
MultiXactState->oldestMultiXactDB = oldest_datoid;
MultiXactState->multiVacLimit = multiVacLimit;
- MultiXactState->multiWarnLimit = multiWarnLimit;
- MultiXactState->multiStopLimit = multiStopLimit;
- MultiXactState->multiWrapLimit = multiWrapLimit;
- curMulti = MultiXactState->nextMXact;
LWLockRelease(MultiXactGenLock);
- /* Log the info */
- ereport(DEBUG1,
- (errmsg_internal("MultiXactId wrap limit is %u, limited by database with OID %u",
- multiWrapLimit, oldest_datoid)));
-
/*
* Computing the actual limits is only possible once the data directory is
* in a consistent state. There's no need to compute the limits while
@@ -2294,59 +2106,6 @@ SetMultiXactIdLimit(MultiXactId oldest_datminmxid, Oid oldest_datoid,
return;
Assert(!InRecovery);
-
- /* Set limits for offset vacuum. */
- needs_offset_vacuum = SetOffsetVacuumLimit(is_startup);
-
- /*
- * If past the autovacuum force point, immediately signal an autovac
- * request. The reason for this is that autovac only processes one
- * database per invocation. Once it's finished cleaning up the oldest
- * database, it'll call here, and we'll signal the postmaster to start
- * another iteration immediately if there are still any old databases.
- */
- if ((MultiXactIdPrecedes(multiVacLimit, curMulti) ||
- needs_offset_vacuum) && IsUnderPostmaster)
- SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
-
- /* Give an immediate warning if past the wrap warn point */
- if (MultiXactIdPrecedes(multiWarnLimit, curMulti))
- {
- char *oldest_datname;
-
- /*
- * We can be called when not inside a transaction, for example during
- * StartupXLOG(). In such a case we cannot do database access, so we
- * must just report the oldest DB's OID.
- *
- * Note: it's also possible that get_database_name fails and returns
- * NULL, for example because the database just got dropped. We'll
- * still warn, even though the warning might now be unnecessary.
- */
- if (IsTransactionState())
- oldest_datname = get_database_name(oldest_datoid);
- else
- oldest_datname = NULL;
-
- if (oldest_datname)
- ereport(WARNING,
- (errmsg_plural("database \"%s\" must be vacuumed before %u more MultiXactId is used",
- "database \"%s\" must be vacuumed before %u more MultiXactIds are used",
- multiWrapLimit - curMulti,
- oldest_datname,
- multiWrapLimit - curMulti),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(WARNING,
- (errmsg_plural("database with OID %u must be vacuumed before %u more MultiXactId is used",
- "database with OID %u must be vacuumed before %u more MultiXactIds are used",
- multiWrapLimit - curMulti,
- oldest_datoid,
- multiWrapLimit - curMulti),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
}
/*
@@ -2365,12 +2124,12 @@ MultiXactAdvanceNextMXact(MultiXactId minMulti,
LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
if (MultiXactIdPrecedes(MultiXactState->nextMXact, minMulti))
{
- debug_elog3(DEBUG2, "MultiXact: setting next multi to %u", minMulti);
+ debug_elog3(DEBUG2, "MultiXact: setting next multi to " XID_FMT, minMulti);
MultiXactState->nextMXact = minMulti;
}
if (MultiXactOffsetPrecedes(MultiXactState->nextOffset, minMultiOffset))
{
- debug_elog3(DEBUG2, "MultiXact: setting next offset to %u",
+ debug_elog3(DEBUG2, "MultiXact: setting next offset to " INT64_FORMAT,
minMultiOffset);
MultiXactState->nextOffset = minMultiOffset;
}
@@ -2403,7 +2162,7 @@ MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB)
static void
ExtendMultiXactOffset(MultiXactId multi)
{
- int pageno;
+ int64 pageno;
/*
* No work except at first MultiXactId of a page. But beware: just after
@@ -2452,7 +2211,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
flagsbit = MXOffsetToFlagsBitShift(offset);
if (flagsoff == 0 && flagsbit == 0)
{
- int pageno;
+ int64 pageno;
pageno = MXOffsetToMemberPage(offset);
@@ -2464,23 +2223,7 @@ ExtendMultiXactMember(MultiXactOffset offset, int nmembers)
LWLockRelease(MultiXactMemberSLRULock);
}
- /*
- * Compute the number of items till end of current page. Careful: if
- * addition of unsigned ints wraps around, we're at the last page of
- * the last segment; since that page holds a different number of items
- * than other pages, we need to do it differently.
- */
- if (offset + MAX_MEMBERS_IN_LAST_MEMBERS_PAGE < offset)
- {
- /*
- * This is the last page of the last segment; we can compute the
- * number of items left to allocate in it without modulo
- * arithmetic.
- */
- difference = MaxMultiXactOffset - offset + 1;
- }
- else
- difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
+ difference = MULTIXACT_MEMBERS_PER_PAGE - offset % MULTIXACT_MEMBERS_PER_PAGE;
/*
* Advance to next page, taking care to properly handle the wraparound
@@ -2544,183 +2287,6 @@ GetOldestMultiXactId(void)
return oldestMXact;
}
-/*
- * Determine how aggressively we need to vacuum in order to prevent member
- * wraparound.
- *
- * To do so determine what's the oldest member offset and install the limit
- * info in MultiXactState, where it can be used to prevent overrun of old data
- * in the members SLRU area.
- *
- * The return value is true if emergency autovacuum is required and false
- * otherwise.
- */
-static bool
-SetOffsetVacuumLimit(bool is_startup)
-{
- MultiXactId oldestMultiXactId;
- MultiXactId nextMXact;
- MultiXactOffset oldestOffset = 0; /* placate compiler */
- MultiXactOffset prevOldestOffset;
- MultiXactOffset nextOffset;
- bool oldestOffsetKnown = false;
- bool prevOldestOffsetKnown;
- MultiXactOffset offsetStopLimit = 0;
- MultiXactOffset prevOffsetStopLimit;
-
- /*
- * NB: Have to prevent concurrent truncation, we might otherwise try to
- * lookup an oldestMulti that's concurrently getting truncated away.
- */
- LWLockAcquire(MultiXactTruncationLock, LW_SHARED);
-
- /* Read relevant fields from shared memory. */
- LWLockAcquire(MultiXactGenLock, LW_SHARED);
- oldestMultiXactId = MultiXactState->oldestMultiXactId;
- nextMXact = MultiXactState->nextMXact;
- nextOffset = MultiXactState->nextOffset;
- prevOldestOffsetKnown = MultiXactState->oldestOffsetKnown;
- prevOldestOffset = MultiXactState->oldestOffset;
- prevOffsetStopLimit = MultiXactState->offsetStopLimit;
- Assert(MultiXactState->finishedStartup);
- LWLockRelease(MultiXactGenLock);
-
- /*
- * Determine the offset of the oldest multixact. Normally, we can read
- * the offset from the multixact itself, but there's an important special
- * case: if there are no multixacts in existence at all, oldestMXact
- * obviously can't point to one. It will instead point to the multixact
- * ID that will be assigned the next time one is needed.
- */
- if (oldestMultiXactId == nextMXact)
- {
- /*
- * When the next multixact gets created, it will be stored at the next
- * offset.
- */
- oldestOffset = nextOffset;
- oldestOffsetKnown = true;
- }
- else
- {
- /*
- * Figure out where the oldest existing multixact's offsets are
- * stored. Due to bugs in early release of PostgreSQL 9.3.X and 9.4.X,
- * the supposedly-earliest multixact might not really exist. We are
- * careful not to fail in that case.
- */
- oldestOffsetKnown =
- find_multixact_start(oldestMultiXactId, &oldestOffset);
-
- if (oldestOffsetKnown)
- ereport(DEBUG1,
- (errmsg_internal("oldest MultiXactId member is at offset %u",
- oldestOffset)));
- else
- ereport(LOG,
- (errmsg("MultiXact member wraparound protections are disabled because oldest checkpointed MultiXact %u does not exist on disk",
- oldestMultiXactId)));
- }
-
- LWLockRelease(MultiXactTruncationLock);
-
- /*
- * If we can, compute limits (and install them MultiXactState) to prevent
- * overrun of old data in the members SLRU area. We can only do so if the
- * oldest offset is known though.
- */
- if (oldestOffsetKnown)
- {
- /* move back to start of the corresponding segment */
- offsetStopLimit = oldestOffset - (oldestOffset %
- (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT));
-
- /* always leave one segment before the wraparound point */
- offsetStopLimit -= (MULTIXACT_MEMBERS_PER_PAGE * SLRU_PAGES_PER_SEGMENT);
-
- if (!prevOldestOffsetKnown && !is_startup)
- ereport(LOG,
- (errmsg("MultiXact member wraparound protections are now enabled")));
-
- ereport(DEBUG1,
- (errmsg_internal("MultiXact member stop limit is now %u based on MultiXact %u",
- offsetStopLimit, oldestMultiXactId)));
- }
- else if (prevOldestOffsetKnown)
- {
- /*
- * If we failed to get the oldest offset this time, but we have a
- * value from a previous pass through this function, use the old
- * values rather than automatically forcing an emergency autovacuum
- * cycle again.
- */
- oldestOffset = prevOldestOffset;
- oldestOffsetKnown = true;
- offsetStopLimit = prevOffsetStopLimit;
- }
-
- /* Install the computed values */
- LWLockAcquire(MultiXactGenLock, LW_EXCLUSIVE);
- MultiXactState->oldestOffset = oldestOffset;
- MultiXactState->oldestOffsetKnown = oldestOffsetKnown;
- MultiXactState->offsetStopLimit = offsetStopLimit;
- LWLockRelease(MultiXactGenLock);
-
- /*
- * Do we need an emergency autovacuum? If we're not sure, assume yes.
- */
- return !oldestOffsetKnown ||
- (nextOffset - oldestOffset > MULTIXACT_MEMBER_SAFE_THRESHOLD);
-}
-
-/*
- * Return whether adding "distance" to "start" would move past "boundary".
- *
- * We use this to determine whether the addition is "wrapping around" the
- * boundary point, hence the name. The reason we don't want to use the regular
- * 2^31-modulo arithmetic here is that we want to be able to use the whole of
- * the 2^32-1 space here, allowing for more multixacts than would fit
- * otherwise.
- */
-static bool
-MultiXactOffsetWouldWrap(MultiXactOffset boundary, MultiXactOffset start,
- uint32 distance)
-{
- MultiXactOffset finish;
-
- /*
- * Note that offset number 0 is not used (see GetMultiXactIdMembers), so
- * if the addition wraps around the UINT_MAX boundary, skip that value.
- */
- finish = start + distance;
- if (finish < start)
- finish++;
-
- /*-----------------------------------------------------------------------
- * When the boundary is numerically greater than the starting point, any
- * value numerically between the two is not wrapped:
- *
- * <----S----B---->
- * [---) = F wrapped past B (and UINT_MAX)
- * [---) = F not wrapped
- * [----] = F wrapped past B
- *
- * When the boundary is numerically less than the starting point (i.e. the
- * UINT_MAX wraparound occurs somewhere in between) then all values in
- * between are wrapped:
- *
- * <----B----S---->
- * [---) = F not wrapped past B (but wrapped past UINT_MAX)
- * [---) = F wrapped past B (and UINT_MAX)
- * [----] = F not wrapped
- *-----------------------------------------------------------------------
- */
- if (start < boundary)
- return finish >= boundary || finish < start;
- else
- return finish >= boundary && finish < start;
-}
-
/*
* Find the starting offset of the given MultiXactId.
*
@@ -2734,7 +2300,7 @@ static bool
find_multixact_start(MultiXactId multi, MultiXactOffset *result)
{
MultiXactOffset offset;
- int pageno;
+ int64 pageno;
int entryno;
int slotno;
MultiXactOffset *offptr;
@@ -2764,97 +2330,6 @@ find_multixact_start(MultiXactId multi, MultiXactOffset *result)
return true;
}
-/*
- * Determine how many multixacts, and how many multixact members, currently
- * exist. Return false if unable to determine.
- */
-static bool
-ReadMultiXactCounts(uint32 *multixacts, MultiXactOffset *members)
-{
- MultiXactOffset nextOffset;
- MultiXactOffset oldestOffset;
- MultiXactId oldestMultiXactId;
- MultiXactId nextMultiXactId;
- bool oldestOffsetKnown;
-
- LWLockAcquire(MultiXactGenLock, LW_SHARED);
- nextOffset = MultiXactState->nextOffset;
- oldestMultiXactId = MultiXactState->oldestMultiXactId;
- nextMultiXactId = MultiXactState->nextMXact;
- oldestOffset = MultiXactState->oldestOffset;
- oldestOffsetKnown = MultiXactState->oldestOffsetKnown;
- LWLockRelease(MultiXactGenLock);
-
- if (!oldestOffsetKnown)
- return false;
-
- *members = nextOffset - oldestOffset;
- *multixacts = nextMultiXactId - oldestMultiXactId;
- return true;
-}
-
-/*
- * Multixact members can be removed once the multixacts that refer to them
- * are older than every datminmxid. autovacuum_multixact_freeze_max_age and
- * vacuum_multixact_freeze_table_age work together to make sure we never have
- * too many multixacts; we hope that, at least under normal circumstances,
- * this will also be sufficient to keep us from using too many offsets.
- * However, if the average multixact has many members, we might exhaust the
- * members space while still using few enough members that these limits fail
- * to trigger full table scans for relminmxid advancement. At that point,
- * we'd have no choice but to start failing multixact-creating operations
- * with an error.
- *
- * To prevent that, if more than a threshold portion of the members space is
- * used, we effectively reduce autovacuum_multixact_freeze_max_age and
- * to a value just less than the number of multixacts in use. We hope that
- * this will quickly trigger autovacuuming on the table or tables with the
- * oldest relminmxid, thus allowing datminmxid values to advance and removing
- * some members.
- *
- * As the fraction of the member space currently in use grows, we become
- * more aggressive in clamping this value. That not only causes autovacuum
- * to ramp up, but also makes any manual vacuums the user issues more
- * aggressive. This happens because vacuum_set_xid_limits() clamps the
- * freeze table and the minimum freeze age based on the effective
- * autovacuum_multixact_freeze_max_age this function returns. In the worst
- * case, we'll claim the freeze_max_age to zero, and every vacuum of any
- * table will try to freeze every multixact.
- *
- * It's possible that these thresholds should be user-tunable, but for now
- * we keep it simple.
- */
-int
-MultiXactMemberFreezeThreshold(void)
-{
- MultiXactOffset members;
- uint32 multixacts;
- uint32 victim_multixacts;
- double fraction;
-
- /* If we can't determine member space utilization, assume the worst. */
- if (!ReadMultiXactCounts(&multixacts, &members))
- return 0;
-
- /* If member space utilization is low, no special action is required. */
- if (members <= MULTIXACT_MEMBER_SAFE_THRESHOLD)
- return autovacuum_multixact_freeze_max_age;
-
- /*
- * Compute a target for relminmxid advancement. The number of multixacts
- * we try to eliminate from the system is based on how far we are past
- * MULTIXACT_MEMBER_SAFE_THRESHOLD.
- */
- fraction = (double) (members - MULTIXACT_MEMBER_SAFE_THRESHOLD) /
- (MULTIXACT_MEMBER_DANGER_THRESHOLD - MULTIXACT_MEMBER_SAFE_THRESHOLD);
- victim_multixacts = multixacts * fraction;
-
- /* fraction could be > 1.0, but lowest possible freeze age is zero */
- if (victim_multixacts > multixacts)
- return 0;
- return multixacts - victim_multixacts;
-}
-
typedef struct mxtruncinfo
{
int earliestExistingPage;
@@ -2865,7 +2340,7 @@ typedef struct mxtruncinfo
* This callback determines the earliest existing page number.
*/
static bool
-SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
+SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int64 segpage, void *data)
{
mxtruncinfo *trunc = (mxtruncinfo *) data;
@@ -2881,35 +2356,12 @@ SlruScanDirCbFindEarliest(SlruCtl ctl, char *filename, int segpage, void *data)
/*
* Delete members segments [oldest, newOldest)
- *
- * The members SLRU can, in contrast to the offsets one, be filled to almost
- * the full range at once. This means SimpleLruTruncate() can't trivially be
- * used - instead the to-be-deleted range is computed using the offsets
- * SLRU. C.f. TruncateMultiXact().
*/
static void
PerformMembersTruncation(MultiXactOffset oldestOffset, MultiXactOffset newOldestOffset)
{
- const int maxsegment = MXOffsetToMemberSegment(MaxMultiXactOffset);
- int startsegment = MXOffsetToMemberSegment(oldestOffset);
- int endsegment = MXOffsetToMemberSegment(newOldestOffset);
- int segment = startsegment;
-
- /*
- * Delete all the segments but the last one. The last segment can still
- * contain, possibly partially, valid data.
- */
- while (segment != endsegment)
- {
- elog(DEBUG2, "truncating multixact members segment %x", segment);
- SlruDeleteSegment(MultiXactMemberCtl, segment);
-
- /* move to next segment, handling wraparound correctly */
- if (segment == maxsegment)
- segment = 0;
- else
- segment += 1;
- }
+ SimpleLruTruncate(MultiXactMemberCtl,
+ MXOffsetToMemberPage(newOldestOffset));
}
/*
@@ -3027,7 +2479,7 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
else if (!find_multixact_start(oldestMulti, &oldestOffset))
{
ereport(LOG,
- (errmsg("oldest MultiXact %u not found, earliest MultiXact %u, skipping truncation",
+ (errmsg("oldest MultiXact %" PRIu64 " not found, earliest MultiXact %" PRIu64 ", skipping truncation",
oldestMulti, earliest)));
LWLockRelease(MultiXactTruncationLock);
return;
@@ -3045,21 +2497,27 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
else if (!find_multixact_start(newOldestMulti, &newOldestOffset))
{
ereport(LOG,
- (errmsg("cannot truncate up to MultiXact %u because it does not exist on disk, skipping truncation",
+ (errmsg("cannot truncate up to MultiXact %" PRIu64 " because it does not exist on disk, skipping truncation",
newOldestMulti)));
LWLockRelease(MultiXactTruncationLock);
return;
}
elog(DEBUG1, "performing multixact truncation: "
- "offsets [%u, %u), offsets segments [%x, %x), "
- "members [%u, %u), members segments [%x, %x)",
+ "offsets [" XID_FMT ", " XID_FMT "), "
+ "offsets segments [%04x%08x, %04x%08x), "
+ "members [" INT64_FORMAT ", " INT64_FORMAT "), "
+ "members segments [%04x%08x, %04x%08x)",
oldestMulti, newOldestMulti,
- MultiXactIdToOffsetSegment(oldestMulti),
- MultiXactIdToOffsetSegment(newOldestMulti),
+ (uint32) (MultiXactIdToOffsetSegment(oldestMulti) >> 32),
+ (uint32) MultiXactIdToOffsetSegment(oldestMulti),
+ (uint32) (MultiXactIdToOffsetSegment(newOldestMulti) >> 32),
+ (uint32) MultiXactIdToOffsetSegment(newOldestMulti),
oldestOffset, newOldestOffset,
- MXOffsetToMemberSegment(oldestOffset),
- MXOffsetToMemberSegment(newOldestOffset));
+ (uint32) (MXOffsetToMemberSegment(oldestOffset) >> 32),
+ (uint32) MXOffsetToMemberSegment(oldestOffset),
+ (uint32) (MXOffsetToMemberSegment(newOldestOffset) >> 32),
+ (uint32) MXOffsetToMemberSegment(newOldestOffset));
/*
* Do truncation, and the WAL logging of the truncation, in a critical
@@ -3112,81 +2570,40 @@ TruncateMultiXact(MultiXactId newOldestMulti, Oid newOldestMultiDB)
* Decide whether a MultiXactOffset page number is "older" for truncation
* purposes. Analogous to CLOGPagePrecedes().
*
- * Offsetting the values is optional, because MultiXactIdPrecedes() has
- * translational symmetry.
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
static bool
-MultiXactOffsetPagePrecedes(int page1, int page2)
+MultiXactOffsetPagePrecedes(int64 page1, int64 page2)
{
- MultiXactId multi1;
- MultiXactId multi2;
-
- multi1 = ((MultiXactId) page1) * MULTIXACT_OFFSETS_PER_PAGE;
- multi1 += FirstMultiXactId + 1;
- multi2 = ((MultiXactId) page2) * MULTIXACT_OFFSETS_PER_PAGE;
- multi2 += FirstMultiXactId + 1;
-
- return (MultiXactIdPrecedes(multi1, multi2) &&
- MultiXactIdPrecedes(multi1,
- multi2 + MULTIXACT_OFFSETS_PER_PAGE - 1));
+ return page1 < page2;
}
/*
- * Decide whether a MultiXactMember page number is "older" for truncation
- * purposes. There is no "invalid offset number" so use the numbers verbatim.
- */
-static bool
-MultiXactMemberPagePrecedes(int page1, int page2)
-{
- MultiXactOffset offset1;
- MultiXactOffset offset2;
-
- offset1 = ((MultiXactOffset) page1) * MULTIXACT_MEMBERS_PER_PAGE;
- offset2 = ((MultiXactOffset) page2) * MULTIXACT_MEMBERS_PER_PAGE;
-
- return (MultiXactOffsetPrecedes(offset1, offset2) &&
- MultiXactOffsetPrecedes(offset1,
- offset2 + MULTIXACT_MEMBERS_PER_PAGE - 1));
-}
-
-/*
- * Decide which of two MultiXactIds is earlier.
+ * Decide which of two offsets is earlier.
*
- * XXX do we need to do something special for InvalidMultiXactId?
- * (Doesn't look like it.)
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
-bool
-MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2)
+static bool
+MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
{
- int32 diff = (int32) (multi1 - multi2);
+ int64 diff = (int64) (offset1 - offset2);
return (diff < 0);
}
/*
- * MultiXactIdPrecedesOrEquals -- is multi1 logically <= multi2?
+ * Decide whether a MultiXactMember page number is "older" for truncation
+ * purposes. There is no "invalid offset number" so use the numbers verbatim.
*
- * XXX do we need to do something special for InvalidMultiXactId?
- * (Doesn't look like it.)
- */
-bool
-MultiXactIdPrecedesOrEquals(MultiXactId multi1, MultiXactId multi2)
-{
- int32 diff = (int32) (multi1 - multi2);
-
- return (diff <= 0);
-}
-
-
-/*
- * Decide which of two offsets is earlier.
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
static bool
-MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
+MultiXactMemberPagePrecedes(int64 page1, int64 page2)
{
- int32 diff = (int32) (offset1 - offset2);
-
- return (diff < 0);
+ return page1 < page2;
}
/*
@@ -3194,10 +2611,10 @@ MultiXactOffsetPrecedes(MultiXactOffset offset1, MultiXactOffset offset2)
* OFFSETs page (info shows which)
*/
static void
-WriteMZeroPageXlogRec(int pageno, uint8 info)
+WriteMZeroPageXlogRec(int64 pageno, uint8 info)
{
XLogBeginInsert();
- XLogRegisterData((char *) (&pageno), sizeof(int));
+ XLogRegisterData((char *) (&pageno), sizeof(int64));
(void) XLogInsert(RM_MULTIXACT_ID, info);
}
@@ -3242,10 +2659,10 @@ multixact_redo(XLogReaderState *record)
if (info == XLOG_MULTIXACT_ZERO_OFF_PAGE)
{
- int pageno;
+ int64 pageno;
int slotno;
- memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+ memcpy(&pageno, XLogRecGetData(record), sizeof(int64));
LWLockAcquire(MultiXactOffsetSLRULock, LW_EXCLUSIVE);
@@ -3257,10 +2674,10 @@ multixact_redo(XLogReaderState *record)
}
else if (info == XLOG_MULTIXACT_ZERO_MEM_PAGE)
{
- int pageno;
+ int64 pageno;
int slotno;
- memcpy(&pageno, XLogRecGetData(record), sizeof(int));
+ memcpy(&pageno, XLogRecGetData(record), sizeof(int64));
LWLockAcquire(MultiXactMemberSLRULock, LW_EXCLUSIVE);
@@ -3302,20 +2719,26 @@ multixact_redo(XLogReaderState *record)
else if (info == XLOG_MULTIXACT_TRUNCATE_ID)
{
xl_multixact_truncate xlrec;
- int pageno;
+ int64 pageno;
memcpy(&xlrec, XLogRecGetData(record),
SizeOfMultiXactTruncate);
elog(DEBUG1, "replaying multixact truncation: "
- "offsets [%u, %u), offsets segments [%x, %x), "
- "members [%u, %u), members segments [%x, %x)",
+ "offsets [" XID_FMT ", " XID_FMT "), "
+ "offsets segments [%04x%08x, %04x%08x), "
+ "members [" INT64_FORMAT ", " INT64_FORMAT "), "
+ "members segments [%04x%08x, %04x%08x)",
xlrec.startTruncOff, xlrec.endTruncOff,
- MultiXactIdToOffsetSegment(xlrec.startTruncOff),
- MultiXactIdToOffsetSegment(xlrec.endTruncOff),
+ (uint32) (MultiXactIdToOffsetSegment(xlrec.startTruncOff) >> 32),
+ (uint32) MultiXactIdToOffsetSegment(xlrec.startTruncOff),
+ (uint32) (MultiXactIdToOffsetSegment(xlrec.endTruncOff) >> 32),
+ (uint32) MultiXactIdToOffsetSegment(xlrec.endTruncOff),
xlrec.startTruncMemb, xlrec.endTruncMemb,
- MXOffsetToMemberSegment(xlrec.startTruncMemb),
- MXOffsetToMemberSegment(xlrec.endTruncMemb));
+ (uint32) (MXOffsetToMemberSegment(xlrec.startTruncMemb) >> 32),
+ (uint32) MXOffsetToMemberSegment(xlrec.startTruncMemb),
+ (uint32) (MXOffsetToMemberSegment(xlrec.endTruncMemb) >> 32),
+ (uint32) MXOffsetToMemberSegment(xlrec.endTruncMemb));
/* should not be required, but more than cheap enough */
LWLockAcquire(MultiXactTruncationLock, LW_EXCLUSIVE);
@@ -3359,7 +2782,7 @@ pg_get_multixact_members(PG_FUNCTION_ARGS)
if (mxid < FirstMultiXactId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("invalid MultiXactId: %u", mxid)));
+ errmsg("invalid MultiXactId: %" PRIu64, mxid)));
if (SRF_IS_FIRSTCALL())
{
@@ -3395,7 +2818,7 @@ pg_get_multixact_members(PG_FUNCTION_ARGS)
HeapTuple tuple;
char *values[2];
- values[0] = psprintf("%u", multi->members[multi->iter].xid);
+ values[0] = psprintf(XID_FMT, multi->members[multi->iter].xid);
values[1] = mxstatus_to_string(multi->members[multi->iter].status);
tuple = BuildTupleFromCStrings(funccxt->attinmeta, values);
diff --git a/src/backend/access/transam/slru.c b/src/backend/access/transam/slru.c
index 30a476ed5dc..b442a4dc228 100644
--- a/src/backend/access/transam/slru.c
+++ b/src/backend/access/transam/slru.c
@@ -59,9 +59,11 @@
#include "pgstat.h"
#include "storage/fd.h"
#include "storage/shmem.h"
+#include "utils/builtins.h"
#define SlruFileName(ctl, path, seg) \
- snprintf(path, MAXPGPATH, "%s/%04X", (ctl)->Dir, seg)
+ snprintf(path, MAXPGPATH, "%s/%04X%08X", (ctl)->Dir, \
+ (uint32) ((seg) >> 32), (uint32) ((seg) & (int64)0xFFFFFFFF))
/*
* During SimpleLruWriteAll(), we will usually not need to write more than one
@@ -75,7 +77,7 @@ typedef struct SlruWriteAllData
{
int num_files; /* # files actually open */
int fd[MAX_WRITEALL_BUFFERS]; /* their FD's */
- int segno[MAX_WRITEALL_BUFFERS]; /* their log seg#s */
+ int64 segno[MAX_WRITEALL_BUFFERS]; /* their log seg#s */
} SlruWriteAllData;
typedef struct SlruWriteAllData *SlruWriteAll;
@@ -138,15 +140,16 @@ static int slru_errno;
static void SimpleLruZeroLSNs(SlruCtl ctl, int slotno);
static void SimpleLruWaitIO(SlruCtl ctl, int slotno);
static void SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata);
-static bool SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno);
-static bool SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno,
+static bool SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno);
+static bool SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno,
SlruWriteAll fdata);
-static void SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid);
-static int SlruSelectLRUPage(SlruCtl ctl, int pageno);
+static void SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid);
+static int SlruSelectLRUPage(SlruCtl ctl, int64 pageno);
static bool SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename,
- int segpage, void *data);
-static void SlruInternalDeleteSegment(SlruCtl ctl, int segno);
+ int64 segpage, void *data);
+static void SlruInternalDeleteSegment(SlruCtl ctl, int64 segno);
+
/*
* Initialization of shared memory
@@ -162,7 +165,7 @@ SimpleLruShmemSize(int nslots, int nlsns)
sz += MAXALIGN(nslots * sizeof(char *)); /* page_buffer[] */
sz += MAXALIGN(nslots * sizeof(SlruPageStatus)); /* page_status[] */
sz += MAXALIGN(nslots * sizeof(bool)); /* page_dirty[] */
- sz += MAXALIGN(nslots * sizeof(int)); /* page_number[] */
+ sz += MAXALIGN(nslots * sizeof(int64)); /* page_number[] */
sz += MAXALIGN(nslots * sizeof(int)); /* page_lru_count[] */
sz += MAXALIGN(nslots * sizeof(LWLockPadded)); /* buffer_locks[] */
@@ -225,8 +228,8 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
offset += MAXALIGN(nslots * sizeof(SlruPageStatus));
shared->page_dirty = (bool *) (ptr + offset);
offset += MAXALIGN(nslots * sizeof(bool));
- shared->page_number = (int *) (ptr + offset);
- offset += MAXALIGN(nslots * sizeof(int));
+ shared->page_number = (int64 *) (ptr + offset);
+ offset += MAXALIGN(nslots * sizeof(int64));
shared->page_lru_count = (int *) (ptr + offset);
offset += MAXALIGN(nslots * sizeof(int));
@@ -277,7 +280,7 @@ SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
* Control lock must be held at entry, and will be held at exit.
*/
int
-SimpleLruZeroPage(SlruCtl ctl, int pageno)
+SimpleLruZeroPage(SlruCtl ctl, int64 pageno)
{
SlruShared shared = ctl->shared;
int slotno;
@@ -392,7 +395,7 @@ SimpleLruWaitIO(SlruCtl ctl, int slotno)
* Control lock must be held at entry, and will be held at exit.
*/
int
-SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
+SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
TransactionId xid)
{
SlruShared shared = ctl->shared;
@@ -492,7 +495,7 @@ SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
* It is unspecified whether the lock will be shared or exclusive.
*/
int
-SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno, TransactionId xid)
+SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno, TransactionId xid)
{
SlruShared shared = ctl->shared;
int slotno;
@@ -539,7 +542,7 @@ static void
SlruInternalWritePage(SlruCtl ctl, int slotno, SlruWriteAll fdata)
{
SlruShared shared = ctl->shared;
- int pageno = shared->page_number[slotno];
+ int64 pageno = shared->page_number[slotno];
bool ok;
/* If a write is in progress, wait for it to finish */
@@ -623,11 +626,11 @@ SimpleLruWritePage(SlruCtl ctl, int slotno)
* large enough to contain the given page.
*/
bool
-SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
+SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno)
{
- int segno = pageno / SLRU_PAGES_PER_SEGMENT;
- int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
- int offset = rpageno * BLCKSZ;
+ int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
+ int64 rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+ int64 offset = rpageno * BLCKSZ;
char path[MAXPGPATH];
int fd;
bool result;
@@ -681,11 +684,11 @@ SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno)
* read/write operations. We could cache one virtual file pointer ...
*/
static bool
-SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
+SlruPhysicalReadPage(SlruCtl ctl, int64 pageno, int slotno)
{
SlruShared shared = ctl->shared;
- int segno = pageno / SLRU_PAGES_PER_SEGMENT;
- int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+ int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
+ int64 rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
off_t offset = rpageno * BLCKSZ;
char path[MAXPGPATH];
int fd;
@@ -753,11 +756,11 @@ SlruPhysicalReadPage(SlruCtl ctl, int pageno, int slotno)
* SimpleLruWriteAll.
*/
static bool
-SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
+SlruPhysicalWritePage(SlruCtl ctl, int64 pageno, int slotno, SlruWriteAll fdata)
{
SlruShared shared = ctl->shared;
- int segno = pageno / SLRU_PAGES_PER_SEGMENT;
- int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+ int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
+ int64 rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
off_t offset = rpageno * BLCKSZ;
char path[MAXPGPATH];
int fd = -1;
@@ -928,11 +931,11 @@ SlruPhysicalWritePage(SlruCtl ctl, int pageno, int slotno, SlruWriteAll fdata)
* SlruPhysicalWritePage. Call this after cleaning up shared-memory state.
*/
static void
-SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
+SlruReportIOError(SlruCtl ctl, int64 pageno, TransactionId xid)
{
- int segno = pageno / SLRU_PAGES_PER_SEGMENT;
- int rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
- int offset = rpageno * BLCKSZ;
+ int64 segno = pageno / SLRU_PAGES_PER_SEGMENT;
+ int64 rpageno = pageno % SLRU_PAGES_PER_SEGMENT;
+ int64 offset = rpageno * BLCKSZ;
char path[MAXPGPATH];
SlruFileName(ctl, path, segno);
@@ -942,52 +945,52 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
case SLRU_OPEN_FAILED:
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
+ errmsg("could not access status of transaction %" PRIu64, xid),
errdetail("Could not open file \"%s\": %m.", path)));
break;
case SLRU_SEEK_FAILED:
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
- errdetail("Could not seek in file \"%s\" to offset %u: %m.",
+ errmsg("could not access status of transaction %" PRIu64, xid),
+ errdetail("Could not seek in file \"%s\" to offset %" PRIu64 ": %m.",
path, offset)));
break;
case SLRU_READ_FAILED:
if (errno)
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
- errdetail("Could not read from file \"%s\" at offset %u: %m.",
+ errmsg("could not access status of transaction %" PRIu64, xid),
+ errdetail("Could not read from file \"%s\" at offset %" PRIu64 ": %m.",
path, offset)));
else
ereport(ERROR,
- (errmsg("could not access status of transaction %u", xid),
- errdetail("Could not read from file \"%s\" at offset %u: read too few bytes.", path, offset)));
+ (errmsg("could not access status of transaction %" PRIu64, xid),
+ errdetail("Could not read from file \"%s\" at offset %" PRIu64 ": read too few bytes.", path, offset)));
break;
case SLRU_WRITE_FAILED:
if (errno)
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
- errdetail("Could not write to file \"%s\" at offset %u: %m.",
+ errmsg("could not access status of transaction %" PRIu64, xid),
+ errdetail("Could not write to file \"%s\" at offset %" PRIu64 ": %m.",
path, offset)));
else
ereport(ERROR,
- (errmsg("could not access status of transaction %u", xid),
- errdetail("Could not write to file \"%s\" at offset %u: wrote too few bytes.",
+ (errmsg("could not access status of transaction %" PRIu64, xid),
+ errdetail("Could not write to file \"%s\" at offset %" PRIu64 ": wrote too few bytes.",
path, offset)));
break;
case SLRU_FSYNC_FAILED:
ereport(data_sync_elevel(ERROR),
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
+ errmsg("could not access status of transaction %" PRIu64, xid),
errdetail("Could not fsync file \"%s\": %m.",
path)));
break;
case SLRU_CLOSE_FAILED:
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not access status of transaction %u", xid),
+ errmsg("could not access status of transaction %" PRIu64, xid),
errdetail("Could not close file \"%s\": %m.",
path)));
break;
@@ -1013,7 +1016,7 @@ SlruReportIOError(SlruCtl ctl, int pageno, TransactionId xid)
* Control lock must be held at entry, and will be held at exit.
*/
static int
-SlruSelectLRUPage(SlruCtl ctl, int pageno)
+SlruSelectLRUPage(SlruCtl ctl, int64 pageno)
{
SlruShared shared = ctl->shared;
@@ -1024,10 +1027,10 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
int cur_count;
int bestvalidslot = 0; /* keep compiler quiet */
int best_valid_delta = -1;
- int best_valid_page_number = 0; /* keep compiler quiet */
+ int64 best_valid_page_number = 0; /* keep compiler quiet */
int bestinvalidslot = 0; /* keep compiler quiet */
int best_invalid_delta = -1;
- int best_invalid_page_number = 0; /* keep compiler quiet */
+ int64 best_invalid_page_number = 0; /* keep compiler quiet */
/* See if page already has a buffer assigned */
for (slotno = 0; slotno < shared->num_slots; slotno++)
@@ -1068,7 +1071,7 @@ SlruSelectLRUPage(SlruCtl ctl, int pageno)
for (slotno = 0; slotno < shared->num_slots; slotno++)
{
int this_delta;
- int this_page_number;
+ int64 this_page_number;
if (shared->page_status[slotno] == SLRU_PAGE_EMPTY)
return slotno;
@@ -1158,7 +1161,7 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
SlruShared shared = ctl->shared;
SlruWriteAllData fdata;
int slotno;
- int pageno = 0;
+ int64 pageno = 0;
int i;
bool ok;
@@ -1223,7 +1226,7 @@ SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied)
* after it has accrued freshly-written data.
*/
void
-SimpleLruTruncate(SlruCtl ctl, int cutoffPage)
+SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage)
{
SlruShared shared = ctl->shared;
int slotno;
@@ -1301,7 +1304,7 @@ restart:;
* they either can't yet contain anything, or have already been cleaned out.
*/
static void
-SlruInternalDeleteSegment(SlruCtl ctl, int segno)
+SlruInternalDeleteSegment(SlruCtl ctl, int64 segno)
{
char path[MAXPGPATH];
@@ -1324,7 +1327,7 @@ SlruInternalDeleteSegment(SlruCtl ctl, int segno)
* Delete an individual SLRU segment, identified by the segment number.
*/
void
-SlruDeleteSegment(SlruCtl ctl, int segno)
+SlruDeleteSegment(SlruCtl ctl, int64 segno)
{
SlruShared shared = ctl->shared;
int slotno;
@@ -1399,6 +1402,7 @@ SlruMayDeleteSegment(SlruCtl ctl, int segpage, int cutoffPage)
}
#ifdef USE_ASSERT_CHECKING
+#if 0 /* XXX remove unit tests */
static void
SlruPagePrecedesTestOffset(SlruCtl ctl, int per_page, uint32 offset)
{
@@ -1471,6 +1475,7 @@ SlruPagePrecedesTestOffset(SlruCtl ctl, int per_page, uint32 offset)
newestPage % SLRU_PAGES_PER_SEGMENT),
oldestPage));
}
+#endif
/*
* Unit-test a PagePrecedes function.
@@ -1484,10 +1489,12 @@ SlruPagePrecedesTestOffset(SlruCtl ctl, int per_page, uint32 offset)
void
SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page)
{
+#if 0 /* XXX remove unit tests */
/* Test first, middle and last entries of a page. */
SlruPagePrecedesTestOffset(ctl, per_page, 0);
SlruPagePrecedesTestOffset(ctl, per_page, per_page / 2);
SlruPagePrecedesTestOffset(ctl, per_page, per_page - 1);
+#endif
}
#endif
@@ -1497,9 +1504,9 @@ SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page)
* one containing the page passed as "data".
*/
bool
-SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int segpage, void *data)
+SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int64 segpage, void *data)
{
- int cutoffPage = *(int *) data;
+ int64 cutoffPage = *(int64 *) data;
if (SlruMayDeleteSegment(ctl, segpage, cutoffPage))
return true; /* found one; don't iterate any more */
@@ -1512,9 +1519,9 @@ SlruScanDirCbReportPresence(SlruCtl ctl, char *filename, int segpage, void *data
* This callback deletes segments prior to the one passed in as "data".
*/
static bool
-SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data)
+SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int64 segpage, void *data)
{
- int cutoffPage = *(int *) data;
+ int64 cutoffPage = *(int64 *) data;
if (SlruMayDeleteSegment(ctl, segpage, cutoffPage))
SlruInternalDeleteSegment(ctl, segpage / SLRU_PAGES_PER_SEGMENT);
@@ -1527,7 +1534,7 @@ SlruScanDirCbDeleteCutoff(SlruCtl ctl, char *filename, int segpage, void *data)
* This callback deletes all segments.
*/
bool
-SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage, void *data)
+SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int64 segpage, void *data)
{
SlruInternalDeleteSegment(ctl, segpage / SLRU_PAGES_PER_SEGMENT);
@@ -1555,8 +1562,8 @@ SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
bool retval = false;
DIR *cldir;
struct dirent *clde;
- int segno;
- int segpage;
+ int64 segno;
+ int64 segpage;
cldir = AllocateDir(ctl->Dir);
while ((clde = ReadDir(cldir, ctl->Dir)) != NULL)
@@ -1565,10 +1572,10 @@ SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data)
len = strlen(clde->d_name);
- if ((len == 4 || len == 5 || len == 6) &&
+ if ((len == 12 || len == 13 || len == 14) &&
strspn(clde->d_name, "0123456789ABCDEF") == len)
{
- segno = (int) strtol(clde->d_name, NULL, 16);
+ segno = pg_strtouint64(clde->d_name, NULL, 16);
segpage = segno * SLRU_PAGES_PER_SEGMENT;
elog(DEBUG2, "SlruScanDirectory invoking callback on %s/%s",
diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c
index 66d35481552..2f60fcb79c7 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -63,8 +63,8 @@ static SlruCtlData SubTransCtlData;
#define SubTransCtl (&SubTransCtlData)
-static int ZeroSUBTRANSPage(int pageno);
-static bool SubTransPagePrecedes(int page1, int page2);
+static bool SubTransPagePrecedes(int64 page1, int64 page2);
+static int ZeroSUBTRANSPage(int64 pageno);
/*
@@ -73,7 +73,7 @@ static bool SubTransPagePrecedes(int page1, int page2);
void
SubTransSetParent(TransactionId xid, TransactionId parent)
{
- int pageno = TransactionIdToPage(xid);
+ int64 pageno = TransactionIdToPage(xid);
int entryno = TransactionIdToEntry(xid);
int slotno;
TransactionId *ptr;
@@ -108,7 +108,7 @@ SubTransSetParent(TransactionId xid, TransactionId parent)
TransactionId
SubTransGetParent(TransactionId xid)
{
- int pageno = TransactionIdToPage(xid);
+ int64 pageno = TransactionIdToPage(xid);
int entryno = TransactionIdToEntry(xid);
int slotno;
TransactionId *ptr;
@@ -168,7 +168,7 @@ SubTransGetTopmostTransaction(TransactionId xid)
* structure that could lead to an infinite loop, so exit.
*/
if (!TransactionIdPrecedes(parentXid, previousXid))
- elog(ERROR, "pg_subtrans contains invalid entry: xid %u points to parent xid %u",
+ elog(ERROR, "pg_subtrans contains invalid entry: xid " XID_FMT " points to parent xid " XID_FMT,
previousXid, parentXid);
}
@@ -211,11 +211,14 @@ void
BootStrapSUBTRANS(void)
{
int slotno;
+ int64 pageno;
+
+ pageno = TransactionIdToPage(XidFromFullTransactionId(ShmemVariableCache->nextXid));
LWLockAcquire(SubtransSLRULock, LW_EXCLUSIVE);
/* Create and zero the first page of the subtrans log */
- slotno = ZeroSUBTRANSPage(0);
+ slotno = ZeroSUBTRANSPage(pageno);
/* Make sure it's written out */
SimpleLruWritePage(SubTransCtl, slotno);
@@ -233,7 +236,7 @@ BootStrapSUBTRANS(void)
* Control lock must be held at entry, and will be held at exit.
*/
static int
-ZeroSUBTRANSPage(int pageno)
+ZeroSUBTRANSPage(int64 pageno)
{
return SimpleLruZeroPage(SubTransCtl, pageno);
}
@@ -249,8 +252,8 @@ void
StartupSUBTRANS(TransactionId oldestActiveXID)
{
FullTransactionId nextXid;
- int startPage;
- int endPage;
+ int64 startPage;
+ int64 endPage;
/*
* Since we don't expect pg_subtrans to be valid across crashes, we
@@ -268,9 +271,6 @@ StartupSUBTRANS(TransactionId oldestActiveXID)
{
(void) ZeroSUBTRANSPage(startPage);
startPage++;
- /* must account for wraparound */
- if (startPage > TransactionIdToPage(MaxTransactionId))
- startPage = 0;
}
(void) ZeroSUBTRANSPage(startPage);
@@ -307,7 +307,7 @@ CheckPointSUBTRANS(void)
void
ExtendSUBTRANS(TransactionId newestXact)
{
- int pageno;
+ int64 pageno;
/*
* No work except at first XID of a page. But beware: just after
@@ -337,7 +337,7 @@ ExtendSUBTRANS(TransactionId newestXact)
void
TruncateSUBTRANS(TransactionId oldestXact)
{
- int cutoffPage;
+ int64 cutoffPage;
/*
* The cutoff point is the start of the segment containing oldestXact. We
@@ -347,6 +347,13 @@ TruncateSUBTRANS(TransactionId oldestXact)
* a page and oldestXact == next XID. In that case, if we didn't subtract
* one, we'd trigger SimpleLruTruncate's wraparound detection.
*/
+
+ if (oldestXact <= FirstNormalTransactionId)
+ {
+ SimpleLruTruncate(SubTransCtl, 0);
+ return;
+ }
+
TransactionIdRetreat(oldestXact);
cutoffPage = TransactionIdToPage(oldestXact);
@@ -357,18 +364,12 @@ TruncateSUBTRANS(TransactionId oldestXact)
/*
* Decide whether a SUBTRANS page number is "older" for truncation purposes.
* Analogous to CLOGPagePrecedes().
+ *
+ * With 64xid this function is just "<", but we left it as a function in order
+ * for its calls remain "vanilla" like.
*/
static bool
-SubTransPagePrecedes(int page1, int page2)
+SubTransPagePrecedes(int64 page1, int64 page2)
{
- TransactionId xid1;
- TransactionId xid2;
-
- xid1 = ((TransactionId) page1) * SUBTRANS_XACTS_PER_PAGE;
- xid1 += FirstNormalTransactionId + 1;
- xid2 = ((TransactionId) page2) * SUBTRANS_XACTS_PER_PAGE;
- xid2 += FirstNormalTransactionId + 1;
-
- return (TransactionIdPrecedes(xid1, xid2) &&
- TransactionIdPrecedes(xid1, xid2 + SUBTRANS_XACTS_PER_PAGE - 1));
+ return page1 < page2;
}
diff --git a/src/backend/access/transam/transam.c b/src/backend/access/transam/transam.c
index dbc5f884e88..a588b615b06 100644
--- a/src/backend/access/transam/transam.c
+++ b/src/backend/access/transam/transam.c
@@ -157,7 +157,7 @@ TransactionIdDidCommit(TransactionId transactionId)
parentXid = SubTransGetParent(transactionId);
if (!TransactionIdIsValid(parentXid))
{
- elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
+ elog(WARNING, "no pg_subtrans entry for subcommitted XID " XID_FMT,
transactionId);
return false;
}
@@ -206,7 +206,7 @@ TransactionIdDidAbort(TransactionId transactionId)
if (!TransactionIdIsValid(parentXid))
{
/* see notes in TransactionIdDidCommit */
- elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
+ elog(WARNING, "no pg_subtrans entry for subcommitted XID " XID_FMT,
transactionId);
return true;
}
@@ -293,70 +293,6 @@ TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids)
TRANSACTION_STATUS_ABORTED, InvalidXLogRecPtr);
}
-/*
- * TransactionIdPrecedes --- is id1 logically < id2?
- */
-bool
-TransactionIdPrecedes(TransactionId id1, TransactionId id2)
-{
- /*
- * If either ID is a permanent XID then we can just do unsigned
- * comparison. If both are normal, do a modulo-2^32 comparison.
- */
- int32 diff;
-
- if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
- return (id1 < id2);
-
- diff = (int32) (id1 - id2);
- return (diff < 0);
-}
-
-/*
- * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
- */
-bool
-TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
-{
- int32 diff;
-
- if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
- return (id1 <= id2);
-
- diff = (int32) (id1 - id2);
- return (diff <= 0);
-}
-
-/*
- * TransactionIdFollows --- is id1 logically > id2?
- */
-bool
-TransactionIdFollows(TransactionId id1, TransactionId id2)
-{
- int32 diff;
-
- if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
- return (id1 > id2);
-
- diff = (int32) (id1 - id2);
- return (diff > 0);
-}
-
-/*
- * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
- */
-bool
-TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
-{
- int32 diff;
-
- if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
- return (id1 >= id2);
-
- diff = (int32) (id1 - id2);
- return (diff >= 0);
-}
-
/*
* TransactionIdLatest --- get latest XID among a main xact and its children
diff --git a/src/backend/access/transam/twophase.c b/src/backend/access/transam/twophase.c
index 271a3146dbb..0c70795eea9 100644
--- a/src/backend/access/transam/twophase.c
+++ b/src/backend/access/transam/twophase.c
@@ -848,7 +848,7 @@ TwoPhaseGetGXact(TransactionId xid, bool lock_held)
LWLockRelease(TwoPhaseStateLock);
if (result == NULL) /* should not happen */
- elog(ERROR, "failed to find GlobalTransaction for xid %u", xid);
+ elog(ERROR, "failed to find GlobalTransaction for xid " XID_FMT, xid);
cached_xid = xid;
cached_gxact = result;
@@ -940,7 +940,7 @@ TwoPhaseGetDummyProc(TransactionId xid, bool lock_held)
/************************************************************************/
#define TwoPhaseFilePath(path, xid) \
- snprintf(path, MAXPGPATH, TWOPHASE_DIR "/%08X", xid)
+ snprintf(path, MAXPGPATH, TWOPHASE_DIR "/%08X%08X", (uint32)(xid >> 32), (uint32)xid)
/*
* 2PC state file format:
@@ -1847,13 +1847,13 @@ restoreTwoPhaseData(void)
cldir = AllocateDir(TWOPHASE_DIR);
while ((clde = ReadDir(cldir, TWOPHASE_DIR)) != NULL)
{
- if (strlen(clde->d_name) == 8 &&
- strspn(clde->d_name, "0123456789ABCDEF") == 8)
+ if (strlen(clde->d_name) == 16 &&
+ strspn(clde->d_name, "0123456789ABCDEF") == 16)
{
TransactionId xid;
char *buf;
- xid = (TransactionId) strtoul(clde->d_name, NULL, 16);
+ xid = (TransactionId) pg_strtouint64(clde->d_name, NULL, 16);
buf = ProcessTwoPhaseBuffer(xid, InvalidXLogRecPtr,
true, false, false);
@@ -2055,7 +2055,7 @@ RecoverPreparedTransactions(void)
continue;
ereport(LOG,
- (errmsg("recovering prepared transaction %u from shared memory", xid)));
+ (errmsg("recovering prepared transaction %" PRIu64 " from shared memory", xid)));
hdr = (TwoPhaseFileHeader *) buf;
Assert(TransactionIdEquals(hdr->xid, xid));
@@ -2146,14 +2146,14 @@ ProcessTwoPhaseBuffer(TransactionId xid,
if (fromdisk)
{
ereport(WARNING,
- (errmsg("removing stale two-phase state file for transaction %u",
+ (errmsg("removing stale two-phase state file for transaction \"%" PRIu64 "\"",
xid)));
RemoveTwoPhaseFile(xid, true);
}
else
{
ereport(WARNING,
- (errmsg("removing stale two-phase state from memory for transaction %u",
+ (errmsg("removing stale two-phase state from memory for transaction \"%" PRIu64 "\"",
xid)));
PrepareRedoRemove(xid, true);
}
@@ -2166,14 +2166,14 @@ ProcessTwoPhaseBuffer(TransactionId xid,
if (fromdisk)
{
ereport(WARNING,
- (errmsg("removing future two-phase state file for transaction %u",
+ (errmsg("removing future two-phase state file for transaction \"%" PRIu64 "\"",
xid)));
RemoveTwoPhaseFile(xid, true);
}
else
{
ereport(WARNING,
- (errmsg("removing future two-phase state from memory for transaction %u",
+ (errmsg("removing future two-phase state from memory for transaction \"%" PRIu64 "\"",
xid)));
PrepareRedoRemove(xid, true);
}
@@ -2182,7 +2182,6 @@ ProcessTwoPhaseBuffer(TransactionId xid,
if (fromdisk)
{
- /* Read and validate file */
buf = ReadTwoPhaseFile(xid, false);
}
else
@@ -2198,12 +2197,12 @@ ProcessTwoPhaseBuffer(TransactionId xid,
if (fromdisk)
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg("corrupted two-phase state file for transaction %u",
+ errmsg("corrupted two-phase state file for transaction %" PRIu64,
xid)));
else
ereport(ERROR,
(errcode(ERRCODE_DATA_CORRUPTED),
- errmsg("corrupted two-phase state in memory for transaction %u",
+ errmsg("corrupted two-phase state in memory for transaction %" PRIu64,
xid)));
}
@@ -2359,7 +2358,7 @@ RecordTransactionAbortPrepared(TransactionId xid,
* RecordTransactionCommitPrepared ...
*/
if (TransactionIdDidCommit(xid))
- elog(PANIC, "cannot abort transaction %u, it was already committed",
+ elog(PANIC, "cannot abort transaction " XID_FMT ", it was already committed",
xid);
START_CRIT_SECTION();
@@ -2466,7 +2465,7 @@ PrepareRedoAdd(char *buf, XLogRecPtr start_lsn,
false /* backward */ , false /* WAL */ );
}
- elog(DEBUG2, "added 2PC data in shared memory for transaction %u", gxact->xid);
+ elog(DEBUG2, "added 2PC data in shared memory for transaction " XID_FMT, gxact->xid);
}
/*
@@ -2509,7 +2508,7 @@ PrepareRedoRemove(TransactionId xid, bool giveWarning)
/*
* And now we can clean up any files we may have left.
*/
- elog(DEBUG2, "removing 2PC data for transaction %u", xid);
+ elog(DEBUG2, "removing 2PC data for transaction " XID_FMT, xid);
if (gxact->ondisk)
RemoveTwoPhaseFile(xid, giveWarning);
RemoveGXact(gxact);
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index 748120a0125..0a89721b004 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -68,7 +68,7 @@ GetNewTransactionId(bool isSubXact)
Assert(!isSubXact);
MyProc->xid = BootstrapTransactionId;
ProcGlobal->xids[MyProc->pgxactoff] = BootstrapTransactionId;
- return FullTransactionIdFromEpochAndXid(0, BootstrapTransactionId);
+ return FullTransactionIdFromXid(BootstrapTransactionId);
}
/* safety check, we should never get this far in a HS standby */
@@ -102,11 +102,6 @@ GetNewTransactionId(bool isSubXact)
* possibility of deadlock while doing get_database_name(). First,
* copy all the shared values we'll need in this path.
*/
- TransactionId xidWarnLimit = ShmemVariableCache->xidWarnLimit;
- TransactionId xidStopLimit = ShmemVariableCache->xidStopLimit;
- TransactionId xidWrapLimit = ShmemVariableCache->xidWrapLimit;
- Oid oldest_datoid = ShmemVariableCache->oldestXidDB;
-
LWLockRelease(XidGenLock);
/*
@@ -117,48 +112,6 @@ GetNewTransactionId(bool isSubXact)
if (IsUnderPostmaster && (xid % 65536) == 0)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
- if (IsUnderPostmaster &&
- TransactionIdFollowsOrEquals(xid, xidStopLimit))
- {
- char *oldest_datname = get_database_name(oldest_datoid);
-
- /* complain even if that DB has disappeared */
- if (oldest_datname)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("database is not accepting commands to avoid wraparound data loss in database \"%s\"",
- oldest_datname),
- errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("database is not accepting commands to avoid wraparound data loss in database with OID %u",
- oldest_datoid),
- errhint("Stop the postmaster and vacuum that database in single-user mode.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
- else if (TransactionIdFollowsOrEquals(xid, xidWarnLimit))
- {
- char *oldest_datname = get_database_name(oldest_datoid);
-
- /* complain even if that DB has disappeared */
- if (oldest_datname)
- ereport(WARNING,
- (errmsg("database \"%s\" must be vacuumed within %u transactions",
- oldest_datname,
- xidWrapLimit - xid),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(WARNING,
- (errmsg("database with OID %u must be vacuumed within %u transactions",
- oldest_datoid,
- xidWrapLimit - xid),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
-
/* Re-acquire lock and start over */
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
full_xid = ShmemVariableCache->nextXid;
@@ -270,7 +223,7 @@ ReadNextFullTransactionId(void)
}
/*
- * Advance nextXid to the value after a given xid. The epoch is inferred.
+ * Advance nextXid to the value after a given xid.
* This must only be called during recovery or from two-phase start-up code.
*/
void
@@ -278,7 +231,6 @@ AdvanceNextFullTransactionIdPastXid(TransactionId xid)
{
FullTransactionId newNextFullXid;
TransactionId next_xid;
- uint32 epoch;
/*
* It is safe to read nextXid without a lock, because this is only called
@@ -292,19 +244,9 @@ AdvanceNextFullTransactionIdPastXid(TransactionId xid)
if (!TransactionIdFollowsOrEquals(xid, next_xid))
return;
- /*
- * Compute the FullTransactionId that comes after the given xid. To do
- * this, we preserve the existing epoch, but detect when we've wrapped
- * into a new epoch. This is necessary because WAL records and 2PC state
- * currently contain 32 bit xids. The wrap logic is safe in those cases
- * because the span of active xids cannot exceed one epoch at any given
- * point in the WAL stream.
- */
+ /* Compute the FullTransactionId that comes after the given xid. */
TransactionIdAdvance(xid);
- epoch = EpochFromFullTransactionId(ShmemVariableCache->nextXid);
- if (unlikely(xid < next_xid))
- ++epoch;
- newNextFullXid = FullTransactionIdFromEpochAndXid(epoch, xid);
+ newNextFullXid = FullTransactionIdFromXid(xid);
/*
* We still need to take a lock to modify the value when there are
@@ -345,54 +287,10 @@ void
SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
{
TransactionId xidVacLimit;
- TransactionId xidWarnLimit;
- TransactionId xidStopLimit;
- TransactionId xidWrapLimit;
TransactionId curXid;
Assert(TransactionIdIsNormal(oldest_datfrozenxid));
- /*
- * The place where we actually get into deep trouble is halfway around
- * from the oldest potentially-existing XID. (This calculation is
- * probably off by one or two counts, because the special XIDs reduce the
- * size of the loop a little bit. But we throw in plenty of slop below,
- * so it doesn't matter.)
- */
- xidWrapLimit = oldest_datfrozenxid + (MaxTransactionId >> 1);
- if (xidWrapLimit < FirstNormalTransactionId)
- xidWrapLimit += FirstNormalTransactionId;
-
- /*
- * We'll refuse to continue assigning XIDs in interactive mode once we get
- * within 3M transactions of data loss. This leaves lots of room for the
- * DBA to fool around fixing things in a standalone backend, while not
- * being significant compared to total XID space. (VACUUM requires an XID
- * if it truncates at wal_level!=minimal. "VACUUM (ANALYZE)", which a DBA
- * might do by reflex, assigns an XID. Hence, we had better be sure
- * there's lots of XIDs left...) Also, at default BLCKSZ, this leaves two
- * completely-idle segments. In the event of edge-case bugs involving
- * page or segment arithmetic, idle segments render the bugs unreachable
- * outside of single-user mode.
- */
- xidStopLimit = xidWrapLimit - 3000000;
- if (xidStopLimit < FirstNormalTransactionId)
- xidStopLimit -= FirstNormalTransactionId;
-
- /*
- * We'll start complaining loudly when we get within 40M transactions of
- * data loss. This is kind of arbitrary, but if you let your gas gauge
- * get down to 2% of full, would you be looking for the next gas station?
- * We need to be fairly liberal about this number because there are lots
- * of scenarios where most transactions are done by automatic clients that
- * won't pay attention to warnings. (No, we're not gonna make this
- * configurable. If you know enough to configure it, you know enough to
- * not get in this kind of trouble in the first place.)
- */
- xidWarnLimit = xidWrapLimit - 40000000;
- if (xidWarnLimit < FirstNormalTransactionId)
- xidWarnLimit -= FirstNormalTransactionId;
-
/*
* We'll start trying to force autovacuums when oldest_datfrozenxid gets
* to be more than autovacuum_freeze_max_age transactions old.
@@ -416,18 +314,10 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
LWLockAcquire(XidGenLock, LW_EXCLUSIVE);
ShmemVariableCache->oldestXid = oldest_datfrozenxid;
ShmemVariableCache->xidVacLimit = xidVacLimit;
- ShmemVariableCache->xidWarnLimit = xidWarnLimit;
- ShmemVariableCache->xidStopLimit = xidStopLimit;
- ShmemVariableCache->xidWrapLimit = xidWrapLimit;
ShmemVariableCache->oldestXidDB = oldest_datoid;
curXid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
LWLockRelease(XidGenLock);
- /* Log the info */
- ereport(DEBUG1,
- (errmsg_internal("transaction ID wrap limit is %u, limited by database with OID %u",
- xidWrapLimit, oldest_datoid)));
-
/*
* If past the autovacuum force point, immediately signal an autovac
* request. The reason for this is that autovac only processes one
@@ -438,41 +328,6 @@ SetTransactionIdLimit(TransactionId oldest_datfrozenxid, Oid oldest_datoid)
if (TransactionIdFollowsOrEquals(curXid, xidVacLimit) &&
IsUnderPostmaster && !InRecovery)
SendPostmasterSignal(PMSIGNAL_START_AUTOVAC_LAUNCHER);
-
- /* Give an immediate warning if past the wrap warn point */
- if (TransactionIdFollowsOrEquals(curXid, xidWarnLimit) && !InRecovery)
- {
- char *oldest_datname;
-
- /*
- * We can be called when not inside a transaction, for example during
- * StartupXLOG(). In such a case we cannot do database access, so we
- * must just report the oldest DB's OID.
- *
- * Note: it's also possible that get_database_name fails and returns
- * NULL, for example because the database just got dropped. We'll
- * still warn, even though the warning might now be unnecessary.
- */
- if (IsTransactionState())
- oldest_datname = get_database_name(oldest_datoid);
- else
- oldest_datname = NULL;
-
- if (oldest_datname)
- ereport(WARNING,
- (errmsg("database \"%s\" must be vacuumed within %u transactions",
- oldest_datname,
- xidWrapLimit - curXid),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- else
- ereport(WARNING,
- (errmsg("database with OID %u must be vacuumed within %u transactions",
- oldest_datoid,
- xidWrapLimit - curXid),
- errhint("To avoid a database shutdown, execute a database-wide VACUUM in that database.\n"
- "You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
- }
}
diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
index c9516e03fae..e271493aae2 100644
--- a/src/backend/access/transam/xact.c
+++ b/src/backend/access/transam/xact.c
@@ -1726,7 +1726,7 @@ RecordTransactionAbort(bool isSubXact)
* Check that we haven't aborted halfway through RecordTransactionCommit.
*/
if (TransactionIdDidCommit(xid))
- elog(PANIC, "cannot abort transaction %u, it was already committed",
+ elog(PANIC, "cannot abort transaction " XID_FMT ", it was already committed",
xid);
/* Fetch the data we need for the abort record */
@@ -5436,22 +5436,22 @@ ShowTransactionStateRec(const char *str, TransactionState s)
{
int i;
- appendStringInfo(&buf, ", children: %u", s->childXids[0]);
+ appendStringInfo(&buf, ", children: " XID_FMT, s->childXids[0]);
for (i = 1; i < s->nChildXids; i++)
- appendStringInfo(&buf, " %u", s->childXids[i]);
+ appendStringInfo(&buf, " " XID_FMT, s->childXids[i]);
}
if (s->parent)
ShowTransactionStateRec(str, s->parent);
ereport(DEBUG5,
- (errmsg_internal("%s(%d) name: %s; blockState: %s; state: %s, xid/subid/cid: %u/%u/%u%s%s",
+ (errmsg_internal("%s(%d) name: %s; blockState: %s; state: %s, xid/subid/cid: " XID_FMT "/" XID_FMT "/%u%s%s",
str, s->nestingLevel,
PointerIsValid(s->name) ? s->name : "unnamed",
BlockStateAsString(s->blockState),
TransStateAsString(s->state),
- (unsigned int) XidFromFullTransactionId(s->fullTransactionId),
- (unsigned int) s->subTransactionId,
+ XidFromFullTransactionId(s->fullTransactionId),
+ s->subTransactionId,
(unsigned int) currentCommandId,
currentCommandIdUsed ? " (used)" : "",
buf.data)));
@@ -5634,6 +5634,17 @@ XactLogCommitRecord(TimestampTz commit_time,
xl_subxacts.nsubxacts = nsubxacts;
}
+ if (TransactionIdIsValid(twophase_xid))
+ {
+ xl_xinfo.xinfo |= XACT_XINFO_HAS_TWOPHASE;
+ xl_twophase.xid_lo = (uint32)(twophase_xid & 0xFFFFFFFF);
+ xl_twophase.xid_hi = (uint32)(twophase_xid >> 32);
+ Assert(twophase_gid != NULL);
+
+ if (XLogLogicalInfoActive())
+ xl_xinfo.xinfo |= XACT_XINFO_HAS_GID;
+ }
+
if (nrels > 0)
{
xl_xinfo.xinfo |= XACT_XINFO_HAS_RELFILENODES;
@@ -5647,16 +5658,6 @@ XactLogCommitRecord(TimestampTz commit_time,
xl_invals.nmsgs = nmsgs;
}
- if (TransactionIdIsValid(twophase_xid))
- {
- xl_xinfo.xinfo |= XACT_XINFO_HAS_TWOPHASE;
- xl_twophase.xid = twophase_xid;
- Assert(twophase_gid != NULL);
-
- if (XLogLogicalInfoActive())
- xl_xinfo.xinfo |= XACT_XINFO_HAS_GID;
- }
-
/* dump transaction origin information */
if (replorigin_session_origin != InvalidRepOriginId)
{
@@ -5767,23 +5768,24 @@ XactLogAbortRecord(TimestampTz abort_time,
xl_subxacts.nsubxacts = nsubxacts;
}
- if (nrels > 0)
- {
- xl_xinfo.xinfo |= XACT_XINFO_HAS_RELFILENODES;
- xl_relfilenodes.nrels = nrels;
- info |= XLR_SPECIAL_REL_UPDATE;
- }
-
if (TransactionIdIsValid(twophase_xid))
{
xl_xinfo.xinfo |= XACT_XINFO_HAS_TWOPHASE;
- xl_twophase.xid = twophase_xid;
+ xl_twophase.xid_lo = (uint32)(twophase_xid & 0xFFFFFFFF);
+ xl_twophase.xid_hi = (uint32)(twophase_xid >> 32);
Assert(twophase_gid != NULL);
if (XLogLogicalInfoActive())
xl_xinfo.xinfo |= XACT_XINFO_HAS_GID;
}
+ if (nrels > 0)
+ {
+ xl_xinfo.xinfo |= XACT_XINFO_HAS_RELFILENODES;
+ xl_relfilenodes.nrels = nrels;
+ info |= XLR_SPECIAL_REL_UPDATE;
+ }
+
if (TransactionIdIsValid(twophase_xid) && XLogLogicalInfoActive())
{
xl_xinfo.xinfo |= XACT_XINFO_HAS_DBINFO;
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index c9d4cbf3ff5..e46de79d313 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -116,6 +116,10 @@ int wal_retrieve_retry_interval = 5000;
int max_slot_wal_keep_size_mb = -1;
bool track_wal_io_timing = false;
+TransactionId start_xid = 0;
+MultiXactId start_mx_id = 0;
+MultiXactOffset start_mx_offset = 0;
+
#ifdef WAL_DEBUG
bool XLOG_DEBUG = false;
#endif
@@ -5395,13 +5399,13 @@ BootStrapXLOG(void)
checkPoint.PrevTimeLineID = BootstrapTimeLineID;
checkPoint.fullPageWrites = fullPageWrites;
checkPoint.nextXid =
- FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
+ FullTransactionIdFromXid(Max(FirstNormalTransactionId + 1, start_xid));
checkPoint.nextOid = FirstGenbkiObjectId;
- checkPoint.nextMulti = FirstMultiXactId;
- checkPoint.nextMultiOffset = 0;
- checkPoint.oldestXid = FirstNormalTransactionId;
+ checkPoint.nextMulti = Max(FirstMultiXactId + 1, start_mx_id);
+ checkPoint.nextMultiOffset = start_mx_offset;
+ checkPoint.oldestXid = XidFromFullTransactionId(checkPoint.nextXid) - 1;
checkPoint.oldestXidDB = TemplateDbOid;
- checkPoint.oldestMulti = FirstMultiXactId;
+ checkPoint.oldestMulti = checkPoint.nextMulti - 1;
checkPoint.oldestMultiDB = TemplateDbOid;
checkPoint.oldestCommitTsXid = InvalidTransactionId;
checkPoint.newestCommitTsXid = InvalidTransactionId;
@@ -6048,14 +6052,14 @@ recoveryStopsBefore(XLogReaderState *record)
if (isCommit)
{
ereport(LOG,
- (errmsg("recovery stopping before commit of transaction %u, time %s",
+ (errmsg("recovery stopping before commit of transaction %" PRIu64 ", time %s",
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
else
{
ereport(LOG,
- (errmsg("recovery stopping before abort of transaction %u, time %s",
+ (errmsg("recovery stopping before abort of transaction %" PRIu64 ", time %s",
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
@@ -6193,7 +6197,7 @@ recoveryStopsAfter(XLogReaderState *record)
xact_info == XLOG_XACT_COMMIT_PREPARED)
{
ereport(LOG,
- (errmsg("recovery stopping after commit of transaction %u, time %s",
+ (errmsg("recovery stopping after commit of transaction %" PRIu64 ", time %s",
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
@@ -6201,7 +6205,7 @@ recoveryStopsAfter(XLogReaderState *record)
xact_info == XLOG_XACT_ABORT_PREPARED)
{
ereport(LOG,
- (errmsg("recovery stopping after abort of transaction %u, time %s",
+ (errmsg("recovery stopping after abort of transaction %" PRIu64 ", time %s",
recoveryStopXid,
timestamptz_to_str(recoveryStopTime))));
}
@@ -6237,7 +6241,7 @@ getRecoveryStopReason(void)
if (recoveryTarget == RECOVERY_TARGET_XID)
snprintf(reason, sizeof(reason),
- "%s transaction %u",
+ "%s transaction " XID_FMT,
recoveryStopAfter ? "after" : "before",
recoveryStopXid);
else if (recoveryTarget == RECOVERY_TARGET_TIME)
@@ -6832,7 +6836,7 @@ StartupXLOG(void)
(errmsg("entering standby mode")));
else if (recoveryTarget == RECOVERY_TARGET_XID)
ereport(LOG,
- (errmsg("starting point-in-time recovery to XID %u",
+ (errmsg("starting point-in-time recovery to XID %" PRIu64,
recoveryTargetXid)));
else if (recoveryTarget == RECOVERY_TARGET_TIME)
ereport(LOG,
@@ -7129,16 +7133,16 @@ StartupXLOG(void)
U64FromFullTransactionId(checkPoint.nextXid),
checkPoint.nextOid)));
ereport(DEBUG1,
- (errmsg_internal("next MultiXactId: %u; next MultiXactOffset: %u",
+ (errmsg_internal("next MultiXactId: " XID_FMT "; next MultiXactOffset: " INT64_FORMAT,
checkPoint.nextMulti, checkPoint.nextMultiOffset)));
ereport(DEBUG1,
- (errmsg_internal("oldest unfrozen transaction ID: %u, in database %u",
+ (errmsg_internal("oldest unfrozen transaction ID: " XID_FMT ", in database %u",
checkPoint.oldestXid, checkPoint.oldestXidDB)));
ereport(DEBUG1,
- (errmsg_internal("oldest MultiXactId: %u, in database %u",
+ (errmsg_internal("oldest MultiXactId: " XID_FMT ", in database %u",
checkPoint.oldestMulti, checkPoint.oldestMultiDB)));
ereport(DEBUG1,
- (errmsg_internal("commit timestamp Xid oldest/newest: %u/%u",
+ (errmsg_internal("commit timestamp Xid oldest/newest: " XID_FMT "/" XID_FMT,
checkPoint.oldestCommitTsXid,
checkPoint.newestCommitTsXid)));
if (!TransactionIdIsNormal(XidFromFullTransactionId(checkPoint.nextXid)))
@@ -9394,7 +9398,7 @@ CreateCheckPoint(int flags)
UpdateControlFile();
LWLockRelease(ControlFileLock);
- /* Update shared-memory copy of checkpoint XID/epoch */
+ /* Update shared-memory copy of checkpoint XID/base */
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->ckptFullXid = checkPoint.nextXid;
SpinLockRelease(&XLogCtl->info_lck);
@@ -10411,7 +10415,7 @@ xlog_redo(XLogReaderState *record)
ControlFile->checkPointCopy.nextXid = checkPoint.nextXid;
LWLockRelease(ControlFileLock);
- /* Update shared-memory copy of checkpoint XID/epoch */
+ /* Update shared-memory copy of checkpoint XID/base */
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->ckptFullXid = checkPoint.nextXid;
SpinLockRelease(&XLogCtl->info_lck);
@@ -10470,7 +10474,7 @@ xlog_redo(XLogReaderState *record)
ControlFile->checkPointCopy.nextXid = checkPoint.nextXid;
LWLockRelease(ControlFileLock);
- /* Update shared-memory copy of checkpoint XID/epoch */
+ /* Update shared-memory copy of checkpoint XID/base */
SpinLockAcquire(&XLogCtl->info_lck);
XLogCtl->ckptFullXid = checkPoint.nextXid;
SpinLockRelease(&XLogCtl->info_lck);
diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index 35029cf97d6..abf7613002a 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -1656,37 +1656,3 @@ RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
return true;
}
-
-#ifndef FRONTEND
-
-/*
- * Extract the FullTransactionId from a WAL record.
- */
-FullTransactionId
-XLogRecGetFullXid(XLogReaderState *record)
-{
- TransactionId xid,
- next_xid;
- uint32 epoch;
-
- /*
- * This function is only safe during replay, because it depends on the
- * replay state. See AdvanceNextFullTransactionIdPastXid() for more.
- */
- Assert(AmStartupProcess() || !IsUnderPostmaster);
-
- xid = XLogRecGetXid(record);
- next_xid = XidFromFullTransactionId(ShmemVariableCache->nextXid);
- epoch = EpochFromFullTransactionId(ShmemVariableCache->nextXid);
-
- /*
- * If xid is numerically greater than next_xid, it has to be from the last
- * epoch.
- */
- if (unlikely(xid > next_xid))
- --epoch;
-
- return FullTransactionIdFromEpochAndXid(epoch, xid);
-}
-
-#endif
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index 9fa8fdd4cf3..2e3efc1fc07 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -120,7 +120,7 @@ static const struct typinfo TypInfo[] = {
F_OIDIN, F_OIDOUT},
{"tid", TIDOID, 0, 6, false, TYPALIGN_SHORT, TYPSTORAGE_PLAIN, InvalidOid,
F_TIDIN, F_TIDOUT},
- {"xid", XIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
+ {"xid", XIDOID, 0, 8, FLOAT8PASSBYVAL, TYPALIGN_DOUBLE, TYPSTORAGE_PLAIN, InvalidOid,
F_XIDIN, F_XIDOUT},
{"cid", CIDOID, 0, 4, true, TYPALIGN_INT, TYPSTORAGE_PLAIN, InvalidOid,
F_CIDIN, F_CIDOUT},
@@ -221,7 +221,11 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
argv++;
argc--;
- while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1)
+ start_xid = 0;
+ start_mx_id = 0;
+ start_mx_offset = 0;
+
+ while ((flag = getopt(argc, argv, "B:c:d:D:Fkm:o:r:X:Z:-:")) != -1)
{
switch (flag)
{
@@ -250,9 +254,30 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
case 'k':
bootstrap_data_checksum_version = PG_DATA_CHECKSUM_VERSION;
break;
+ case 'm':
+ if (sscanf(optarg, XID_FMT, &start_mx_id) != 1
+ || !StartMultiXactIdIsValid(start_mx_id))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid start multixact id value")));
+ break;
+ case 'o':
+ if (sscanf(optarg, XID_FMT, &start_mx_offset) != 1
+ || !StartMultiXactOffsetIsValid(start_mx_offset))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid start multixact offset value")));
+ break;
case 'r':
strlcpy(OutputFileName, optarg, MAXPGPATH);
break;
+ case 'Z':
+ if (sscanf(optarg, XID_FMT, &start_xid) != 1
+ || !StartTransactionIdIsValid(start_xid))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid start xid value")));
+ break;
case 'X':
{
int WalSegSz = strtoul(optarg, NULL, 0);
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 7e99de88b34..10fdc5af6c6 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -173,8 +173,8 @@ static const FormData_pg_attribute a2 = {
.attnum = MinTransactionIdAttributeNumber,
.attcacheoff = -1,
.atttypmod = -1,
- .attbyval = true,
- .attalign = TYPALIGN_INT,
+ .attbyval = FLOAT8PASSBYVAL,
+ .attalign = TYPALIGN_DOUBLE,
.attstorage = TYPSTORAGE_PLAIN,
.attnotnull = true,
.attislocal = true,
@@ -201,8 +201,8 @@ static const FormData_pg_attribute a4 = {
.attnum = MaxTransactionIdAttributeNumber,
.attcacheoff = -1,
.atttypmod = -1,
- .attbyval = true,
- .attalign = TYPALIGN_INT,
+ .attbyval = FLOAT8PASSBYVAL,
+ .attalign = TYPALIGN_DOUBLE,
.attstorage = TYPSTORAGE_PLAIN,
.attnotnull = true,
.attislocal = true,
diff --git a/src/backend/catalog/pg_inherits.c b/src/backend/catalog/pg_inherits.c
index 92afbc2f258..3a1eda413f9 100644
--- a/src/backend/catalog/pg_inherits.c
+++ b/src/backend/catalog/pg_inherits.c
@@ -146,7 +146,7 @@ find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
TransactionId xmin;
Snapshot snap;
- xmin = HeapTupleHeaderGetXmin(inheritsTuple->t_data);
+ xmin = HeapTupleGetXmin(inheritsTuple);
snap = GetActiveSnapshot();
if (!XidInMVCCSnapshot(xmin, snap))
diff --git a/src/backend/commands/async.c b/src/backend/commands/async.c
index 3e1b92df030..59ebf09a2e6 100644
--- a/src/backend/commands/async.c
+++ b/src/backend/commands/async.c
@@ -186,8 +186,8 @@ typedef struct AsyncQueueEntry
char data[NAMEDATALEN + NOTIFY_PAYLOAD_MAX_LENGTH];
} AsyncQueueEntry;
-/* Currently, no field of AsyncQueueEntry requires more than int alignment */
-#define QUEUEALIGN(len) INTALIGN(len)
+/* AsyncQueueEntry.xid requires 8-byte alignment */
+#define QUEUEALIGN(len) MAXALIGN(len)
#define AsyncQueueEntryEmptySize (offsetof(AsyncQueueEntry, data) + 2)
@@ -444,7 +444,7 @@ bool Trace_notify = false;
/* local function prototypes */
static int asyncQueuePageDiff(int p, int q);
-static bool asyncQueuePagePrecedes(int p, int q);
+static bool asyncQueuePagePrecedes(int64 p, int64 q);
static void queue_listen(ListenActionKind action, const char *channel);
static void Async_UnlistenOnExit(int code, Datum arg);
static void Exec_ListenPreCommit(void);
@@ -497,16 +497,10 @@ asyncQueuePageDiff(int p, int q)
return diff;
}
-/*
- * Is p < q, accounting for wraparound?
- *
- * Since asyncQueueIsFull() blocks creation of a page that could precede any
- * extant page, we need not assess entries within a page.
- */
static bool
-asyncQueuePagePrecedes(int p, int q)
+asyncQueuePagePrecedes(int64 p, int64 q)
{
- return asyncQueuePageDiff(p, q) < 0;
+ return p < q;
}
/*
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 27cb6307581..47f4c382751 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -55,16 +55,6 @@
*/
#define SEQ_LOG_VALS 32
-/*
- * The "special area" of a sequence's buffer page looks like this.
- */
-#define SEQ_MAGIC 0x1717
-
-typedef struct sequence_magic
-{
- uint32 magic;
-} sequence_magic;
-
/*
* We store a SeqTable item for every sequence we have touched in the current
* session. This is needed to hold onto nextval/currval state. (We can't
@@ -342,10 +332,9 @@ ResetSequence(Oid seq_relid)
static void
fill_seq_with_data(Relation rel, HeapTuple tuple)
{
- Buffer buf;
- Page page;
- sequence_magic *sm;
- OffsetNumber offnum;
+ Buffer buf;
+ Page page;
+ OffsetNumber offnum;
/* Initialize first page of relation with special magic number */
@@ -354,9 +343,8 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
page = BufferGetPage(buf);
- PageInit(page, BufferGetPageSize(buf), sizeof(sequence_magic));
- sm = (sequence_magic *) PageGetSpecialPointer(page);
- sm->magic = SEQ_MAGIC;
+ PageInit(page, BufferGetPageSize(buf), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(page)->pd_xid_base = RecentXmin - FirstNormalTransactionId;
/* Now insert sequence tuple */
@@ -369,10 +357,10 @@ fill_seq_with_data(Relation rel, HeapTuple tuple)
* because if the current transaction aborts, no other xact will ever
* examine the sequence tuple anyway.
*/
- HeapTupleHeaderSetXmin(tuple->t_data, FrozenTransactionId);
+ HeapTupleSetXmin(tuple, FrozenTransactionId);
HeapTupleHeaderSetXminFrozen(tuple->t_data);
HeapTupleHeaderSetCmin(tuple->t_data, FirstCommandId);
- HeapTupleHeaderSetXmax(tuple->t_data, InvalidTransactionId);
+ HeapTupleSetXmax(tuple, InvalidTransactionId);
tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
ItemPointerSet(&tuple->t_data->t_ctid, 0, FirstOffsetNumber);
@@ -1170,25 +1158,19 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
{
Page page;
ItemId lp;
- sequence_magic *sm;
Form_pg_sequence_data seq;
*buf = ReadBuffer(rel, 0);
LockBuffer(*buf, BUFFER_LOCK_EXCLUSIVE);
page = BufferGetPage(*buf);
- sm = (sequence_magic *) PageGetSpecialPointer(page);
-
- if (sm->magic != SEQ_MAGIC)
- elog(ERROR, "bad magic number in sequence \"%s\": %08X",
- RelationGetRelationName(rel), sm->magic);
-
lp = PageGetItemId(page, FirstOffsetNumber);
Assert(ItemIdIsNormal(lp));
/* Note we currently only bother to set these two fields of *seqdatatuple */
seqdatatuple->t_data = (HeapTupleHeader) PageGetItem(page, lp);
seqdatatuple->t_len = ItemIdGetLength(lp);
+ HeapTupleCopyBaseFromPage(seqdatatuple, page);
/*
* Previous releases of Postgres neglected to prevent SELECT FOR UPDATE on
@@ -1199,9 +1181,9 @@ read_seq_tuple(Relation rel, Buffer *buf, HeapTuple seqdatatuple)
* this again if the update gets lost.
*/
Assert(!(seqdatatuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI));
- if (HeapTupleHeaderGetRawXmax(seqdatatuple->t_data) != InvalidTransactionId)
+ if (HeapTupleGetRawXmax(seqdatatuple) != InvalidTransactionId)
{
- HeapTupleHeaderSetXmax(seqdatatuple->t_data, InvalidTransactionId);
+ HeapTupleSetXmax(seqdatatuple, InvalidTransactionId);
seqdatatuple->t_data->t_infomask &= ~HEAP_XMAX_COMMITTED;
seqdatatuple->t_data->t_infomask |= HEAP_XMAX_INVALID;
MarkBufferDirtyHint(*buf, true);
@@ -1863,7 +1845,6 @@ seq_redo(XLogReaderState *record)
char *item;
Size itemsz;
xl_seq_rec *xlrec = (xl_seq_rec *) XLogRecGetData(record);
- sequence_magic *sm;
if (info != XLOG_SEQ_LOG)
elog(PANIC, "seq_redo: unknown op code %u", info);
@@ -1882,9 +1863,8 @@ seq_redo(XLogReaderState *record)
*/
localpage = (Page) palloc(BufferGetPageSize(buffer));
- PageInit(localpage, BufferGetPageSize(buffer), sizeof(sequence_magic));
- sm = (sequence_magic *) PageGetSpecialPointer(localpage);
- sm->magic = SEQ_MAGIC;
+ PageInit(localpage, BufferGetPageSize(buffer), sizeof(HeapPageSpecialData));
+ HeapPageGetSpecial(localpage)->pd_xid_base = RecentXmin - FirstNormalTransactionId;
item = (char *) xlrec + sizeof(xl_seq_rec);
itemsz = XLogRecGetDataLen(record) - sizeof(xl_seq_rec);
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 283ffaea77d..347ba129f82 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -63,13 +63,12 @@
/*
* GUC parameters
*/
-int vacuum_freeze_min_age;
-int vacuum_freeze_table_age;
-int vacuum_multixact_freeze_min_age;
-int vacuum_multixact_freeze_table_age;
-int vacuum_failsafe_age;
-int vacuum_multixact_failsafe_age;
-
+int64 vacuum_freeze_min_age;
+int64 vacuum_freeze_table_age;
+int64 vacuum_multixact_freeze_min_age;
+int64 vacuum_multixact_freeze_table_age;
+int64 vacuum_failsafe_age;
+int64 vacuum_multixact_failsafe_age;
/* A few variables that don't seem worth passing around as parameters */
static MemoryContext vac_context = NULL;
@@ -962,24 +961,26 @@ get_all_vacuum_rels(int options)
*/
void
vacuum_set_xid_limits(Relation rel,
- int freeze_min_age,
- int freeze_table_age,
- int multixact_freeze_min_age,
- int multixact_freeze_table_age,
+ int64 freeze_min_age,
+ int64 freeze_table_age,
+ int64 multixact_freeze_min_age,
+ int64 multixact_freeze_table_age,
TransactionId *oldestXmin,
TransactionId *freezeLimit,
TransactionId *xidFullScanLimit,
MultiXactId *multiXactCutoff,
MultiXactId *mxactFullScanLimit)
{
- int freezemin;
- int mxid_freezemin;
- int effective_multixact_freeze_max_age;
+ int64 freezemin;
+ int64 mxid_freezemin;
+ int64 effective_multixact_freeze_max_age;
TransactionId limit;
TransactionId safeLimit;
+ TransactionId nextXid;
MultiXactId oldestMxact;
MultiXactId mxactLimit;
MultiXactId safeMxactLimit;
+ MultiXactId nextMxactId;
/*
* We can always ignore processes running lazy vacuum. This is because we
@@ -1028,8 +1029,10 @@ vacuum_set_xid_limits(Relation rel,
/*
* Compute the cutoff XID, being careful not to generate a "permanent" XID
*/
- limit = *oldestXmin - freezemin;
- if (!TransactionIdIsNormal(limit))
+ limit = *oldestXmin;
+ if (limit > FirstNormalTransactionId + freezemin)
+ limit -= freezemin;
+ else
limit = FirstNormalTransactionId;
/*
@@ -1037,15 +1040,17 @@ vacuum_set_xid_limits(Relation rel,
* autovacuum_freeze_max_age / 2 XIDs old), complain and force a minimum
* freeze age of zero.
*/
- safeLimit = ReadNextTransactionId() - autovacuum_freeze_max_age;
- if (!TransactionIdIsNormal(safeLimit))
+ nextXid = ReadNextTransactionId();
+ if (nextXid > FirstNormalTransactionId + autovacuum_freeze_max_age)
+ safeLimit = nextXid - autovacuum_freeze_max_age;
+ else
safeLimit = FirstNormalTransactionId;
if (TransactionIdPrecedes(limit, safeLimit))
{
ereport(WARNING,
(errmsg("oldest xmin is far in the past"),
- errhint("Close open transactions soon to avoid wraparound problems.\n"
+ errhint("Close open transactions soon to enable SLRU truncation.\n"
"You might also need to commit or roll back old prepared transactions, or drop stale replication slots.")));
limit = *oldestXmin;
}
@@ -1057,7 +1062,7 @@ vacuum_set_xid_limits(Relation rel,
* normally autovacuum_multixact_freeze_max_age, but may be less if we are
* short of multixact member space.
*/
- effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+ effective_multixact_freeze_max_age = autovacuum_multixact_freeze_max_age;
/*
* Determine the minimum multixact freeze age to use: as specified by
@@ -1078,16 +1083,23 @@ vacuum_set_xid_limits(Relation rel,
if (mxactLimit < FirstMultiXactId)
mxactLimit = FirstMultiXactId;
- safeMxactLimit =
- ReadNextMultiXactId() - effective_multixact_freeze_max_age;
- if (safeMxactLimit < FirstMultiXactId)
+ nextMxactId = ReadNextMultiXactId();
+ if (nextMxactId > FirstMultiXactId + effective_multixact_freeze_max_age)
+ safeMxactLimit = nextMxactId - effective_multixact_freeze_max_age;
+ else
safeMxactLimit = FirstMultiXactId;
if (MultiXactIdPrecedes(mxactLimit, safeMxactLimit))
{
ereport(WARNING,
- (errmsg("oldest multixact is far in the past"),
- errhint("Close open transactions with multixacts soon to avoid wraparound problems.")));
+ (errmsg("oldest multixact is far in the past: "
+ INT64_FORMAT " " INT64_FORMAT " "
+ INT64_FORMAT " " INT64_FORMAT " " INT64_FORMAT " "
+ INT64_FORMAT " " INT64_FORMAT " " INT64_FORMAT " ",
+ multixact_freeze_min_age, vacuum_multixact_freeze_min_age,
+ mxactLimit, mxid_freezemin, oldestMxact,
+ safeMxactLimit, effective_multixact_freeze_max_age, nextMxactId),
+ errhint("Close open transactions with multixacts soon to enable SLRU truncation.")));
/* Use the safe limit, unless an older mxact is still running */
if (MultiXactIdPrecedes(oldestMxact, safeMxactLimit))
mxactLimit = oldestMxact;
@@ -1099,7 +1111,7 @@ vacuum_set_xid_limits(Relation rel,
if (xidFullScanLimit != NULL)
{
- int freezetable;
+ int64 freezetable;
Assert(mxactFullScanLimit != NULL);
@@ -1120,8 +1132,10 @@ vacuum_set_xid_limits(Relation rel,
* Compute XID limit causing a full-table vacuum, being careful not to
* generate a "permanent" XID.
*/
- limit = ReadNextTransactionId() - freezetable;
- if (!TransactionIdIsNormal(limit))
+ limit = ReadNextTransactionId();
+ if (limit > FirstNormalTransactionId + freezetable)
+ limit -= freezetable;
+ else
limit = FirstNormalTransactionId;
*xidFullScanLimit = limit;
@@ -1145,8 +1159,10 @@ vacuum_set_xid_limits(Relation rel,
* Compute MultiXact limit causing a full-table vacuum, being careful
* to generate a valid MultiXact value.
*/
- mxactLimit = ReadNextMultiXactId() - freezetable;
- if (mxactLimit < FirstMultiXactId)
+ mxactLimit = ReadNextMultiXactId();
+ if (mxactLimit > FirstMultiXactId + freezetable)
+ mxactLimit -= freezetable;
+ else
mxactLimit = FirstMultiXactId;
*mxactFullScanLimit = mxactLimit;
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index d6f7d7c2d73..1276035aef8 100644
--- a/src/backend/executor/execExprInterp.c
+++ b/src/backend/executor/execExprInterp.c
@@ -3154,6 +3154,7 @@ ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tmptup);
tmptup.t_data = tuphdr;
heap_deform_tuple(&tmptup, tupDesc,
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 9df1f81ea89..d8c92f48469 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -1070,6 +1070,7 @@ GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tmptup);
tmptup.t_data = tuple;
result = heap_getattr(&tmptup,
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 5ec699a9bd1..8c2b9225a5b 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -2538,6 +2538,7 @@ ExecModifyTable(PlanState *pstate)
HeapTupleHeaderGetDatumLength(oldtupdata.t_data);
ItemPointerSetInvalid(&(oldtupdata.t_self));
/* Historically, view triggers see invalid t_tableOid. */
+ HeapTupleSetZeroBase(&oldtupdata);
oldtupdata.t_tableOid =
(relkind == RELKIND_VIEW) ? InvalidOid :
RelationGetRelid(resultRelInfo->ri_RelationDesc);
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index c93f90de9b7..c9f409286b4 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1061,6 +1061,7 @@ SPI_modifytuple(Relation rel, HeapTuple tuple, int natts, int *attnum,
mtuple->t_data->t_ctid = tuple->t_data->t_ctid;
mtuple->t_self = tuple->t_self;
mtuple->t_tableOid = tuple->t_tableOid;
+ HeapTupleCopyBase(mtuple, tuple);
}
else
{
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2b0236937aa..f413f5c8443 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2772,8 +2772,8 @@ _outIndexStmt(StringInfo str, const IndexStmt *node)
WRITE_STRING_FIELD(idxcomment);
WRITE_OID_FIELD(indexOid);
WRITE_OID_FIELD(oldNode);
- WRITE_UINT_FIELD(oldCreateSubid);
- WRITE_UINT_FIELD(oldFirstRelfilenodeSubid);
+ WRITE_UINT64_FIELD(oldCreateSubid);
+ WRITE_UINT64_FIELD(oldFirstRelfilenodeSubid);
WRITE_BOOL_FIELD(unique);
WRITE_BOOL_FIELD(primary);
WRITE_BOOL_FIELD(isconstraint);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index a5002ad8955..5461a38b41f 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -228,7 +228,7 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
* src/backend/access/heap/README.HOT for discussion.
*/
if (index->indcheckxmin &&
- !TransactionIdPrecedes(HeapTupleHeaderGetXmin(indexRelation->rd_indextuple->t_data),
+ !TransactionIdPrecedes(HeapTupleGetXmin(indexRelation->rd_indextuple),
TransactionXmin))
{
root->glob->transientPlan = true;
diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c
index 681ef91b81e..98e5e48e194 100644
--- a/src/backend/postmaster/autovacuum.c
+++ b/src/backend/postmaster/autovacuum.c
@@ -121,8 +121,8 @@ int autovacuum_vac_ins_thresh;
double autovacuum_vac_ins_scale;
int autovacuum_anl_thresh;
double autovacuum_anl_scale;
-int autovacuum_freeze_max_age;
-int autovacuum_multixact_freeze_max_age;
+int64 autovacuum_freeze_max_age;
+int64 autovacuum_multixact_freeze_max_age;
double autovacuum_vac_cost_delay;
int autovacuum_vac_cost_limit;
@@ -148,10 +148,10 @@ static TransactionId recentXid;
static MultiXactId recentMulti;
/* Default freeze ages to use for autovacuum (varies by database) */
-static int default_freeze_min_age;
-static int default_freeze_table_age;
-static int default_multixact_freeze_min_age;
-static int default_multixact_freeze_table_age;
+static int64 default_freeze_min_age;
+static int64 default_freeze_table_age;
+static int64 default_multixact_freeze_min_age;
+static int64 default_multixact_freeze_table_age;
/* Memory context for long-lived data */
static MemoryContext AutovacMemCxt;
@@ -327,15 +327,15 @@ static void FreeWorkerInfo(int code, Datum arg);
static autovac_table *table_recheck_autovac(Oid relid, HTAB *table_toast_map,
TupleDesc pg_class_desc,
- int effective_multixact_freeze_max_age);
+ int64 effective_multixact_freeze_max_age);
static void recheck_relation_needs_vacanalyze(Oid relid, AutoVacOpts *avopts,
Form_pg_class classForm,
- int effective_multixact_freeze_max_age,
+ int64 effective_multixact_freeze_max_age,
bool *dovacuum, bool *doanalyze, bool *wraparound);
static void relation_needs_vacanalyze(Oid relid, AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
- int effective_multixact_freeze_max_age,
+ int64 effective_multixact_freeze_max_age,
bool *dovacuum, bool *doanalyze, bool *wraparound);
static void autovacuum_do_vac_analyze(autovac_table *tab,
@@ -1157,6 +1157,7 @@ do_start_worker(void)
ListCell *cell;
TransactionId xidForceLimit;
MultiXactId multiForceLimit;
+ int64 multiMembersThreshold;
bool for_xid_wrap;
bool for_multi_wrap;
avw_dbase *avdb;
@@ -1196,17 +1197,18 @@ do_start_worker(void)
* particular tables, but not loosened.)
*/
recentXid = ReadNextTransactionId();
- xidForceLimit = recentXid - autovacuum_freeze_max_age;
- /* ensure it's a "normal" XID, else TransactionIdPrecedes misbehaves */
- /* this can cause the limit to go backwards by 3, but that's OK */
- if (xidForceLimit < FirstNormalTransactionId)
- xidForceLimit -= FirstNormalTransactionId;
+ if (recentXid > FirstNormalTransactionId + autovacuum_freeze_max_age)
+ xidForceLimit = recentXid - autovacuum_freeze_max_age;
+ else
+ xidForceLimit = FirstNormalTransactionId;
/* Also determine the oldest datminmxid we will consider. */
recentMulti = ReadNextMultiXactId();
- multiForceLimit = recentMulti - MultiXactMemberFreezeThreshold();
- if (multiForceLimit < FirstMultiXactId)
- multiForceLimit -= FirstMultiXactId;
+ multiMembersThreshold = autovacuum_multixact_freeze_max_age;
+ if (recentMulti > FirstMultiXactId + multiMembersThreshold)
+ multiForceLimit = recentMulti - multiMembersThreshold;
+ else
+ multiForceLimit = FirstMultiXactId;
/*
* Choose a database to connect to. We pick the database that was least
@@ -1971,7 +1973,7 @@ do_autovacuum(void)
BufferAccessStrategy bstrategy;
ScanKeyData key;
TupleDesc pg_class_desc;
- int effective_multixact_freeze_max_age;
+ int64 effective_multixact_freeze_max_age;
bool did_vacuum = false;
bool found_concurrent_worker = false;
int i;
@@ -2007,7 +2009,7 @@ do_autovacuum(void)
* normally autovacuum_multixact_freeze_max_age, but may be less if we are
* short of multixact member space.
*/
- effective_multixact_freeze_max_age = MultiXactMemberFreezeThreshold();
+ effective_multixact_freeze_max_age = autovacuum_multixact_freeze_max_age;
/*
* Find the pg_database entry and select the default freeze ages. We use
@@ -2803,7 +2805,7 @@ get_pgstat_tabentry_relid(Oid relid, bool isshared, PgStat_StatDBEntry *shared,
static autovac_table *
table_recheck_autovac(Oid relid, HTAB *table_toast_map,
TupleDesc pg_class_desc,
- int effective_multixact_freeze_max_age)
+ int64 effective_multixact_freeze_max_age)
{
Form_pg_class classForm;
HeapTuple classTup;
@@ -2872,10 +2874,10 @@ table_recheck_autovac(Oid relid, HTAB *table_toast_map,
/* OK, it needs something done */
if (doanalyze || dovacuum)
{
- int freeze_min_age;
- int freeze_table_age;
- int multixact_freeze_min_age;
- int multixact_freeze_table_age;
+ int64 freeze_min_age;
+ int64 freeze_table_age;
+ int64 multixact_freeze_min_age;
+ int64 multixact_freeze_table_age;
int vac_cost_limit;
double vac_cost_delay;
int log_min_duration;
@@ -2995,7 +2997,7 @@ static void
recheck_relation_needs_vacanalyze(Oid relid,
AutoVacOpts *avopts,
Form_pg_class classForm,
- int effective_multixact_freeze_max_age,
+ int64 effective_multixact_freeze_max_age,
bool *dovacuum,
bool *doanalyze,
bool *wraparound)
@@ -3064,7 +3066,7 @@ relation_needs_vacanalyze(Oid relid,
AutoVacOpts *relopts,
Form_pg_class classForm,
PgStat_StatTabEntry *tabentry,
- int effective_multixact_freeze_max_age,
+ int64 effective_multixact_freeze_max_age,
/* output params below */
bool *dovacuum,
bool *doanalyze,
@@ -3093,8 +3095,8 @@ relation_needs_vacanalyze(Oid relid,
anltuples;
/* freeze parameters */
- int freeze_max_age;
- int multixact_freeze_max_age;
+ int64 freeze_max_age;
+ int64 multixact_freeze_max_age;
TransactionId xidForceLimit;
MultiXactId multiForceLimit;
@@ -3144,17 +3146,19 @@ relation_needs_vacanalyze(Oid relid,
av_enabled = (relopts ? relopts->enabled : true);
/* Force vacuum if table is at risk of wraparound */
- xidForceLimit = recentXid - freeze_max_age;
- if (xidForceLimit < FirstNormalTransactionId)
- xidForceLimit -= FirstNormalTransactionId;
+ if (recentXid > FirstNormalTransactionId + freeze_max_age)
+ xidForceLimit = recentXid - freeze_max_age;
+ else
+ xidForceLimit = FirstNormalTransactionId;
force_vacuum = (TransactionIdIsNormal(classForm->relfrozenxid) &&
TransactionIdPrecedes(classForm->relfrozenxid,
xidForceLimit));
if (!force_vacuum)
{
- multiForceLimit = recentMulti - multixact_freeze_max_age;
- if (multiForceLimit < FirstMultiXactId)
- multiForceLimit -= FirstMultiXactId;
+ if (recentMulti > FirstMultiXactId + multixact_freeze_max_age)
+ multiForceLimit = recentMulti - multixact_freeze_max_age;
+ else
+ multiForceLimit = FirstMultiXactId;
force_vacuum = MultiXactIdIsValid(classForm->relminmxid) &&
MultiXactIdPrecedes(classForm->relminmxid, multiForceLimit);
}
diff --git a/src/backend/replication/logical/decode.c b/src/backend/replication/logical/decode.c
index 9b450c9f90e..161b2b955e4 100644
--- a/src/backend/replication/logical/decode.c
+++ b/src/backend/replication/logical/decode.c
@@ -846,8 +846,12 @@ DecodeInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
xl_heap_insert *xlrec;
ReorderBufferChange *change;
RelFileNode target_node;
+ bool isinit = (XLogRecGetInfo(r) & XLOG_HEAP_INIT_PAGE) != 0;
+ Pointer rec_data = (Pointer) XLogRecGetData(r);
- xlrec = (xl_heap_insert *) XLogRecGetData(r);
+ if (isinit)
+ rec_data += sizeof(TransactionId);
+ xlrec = (xl_heap_insert *) rec_data;
/*
* Ignore insert records without new tuples (this does happen when
@@ -903,8 +907,12 @@ DecodeUpdate(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
ReorderBufferChange *change;
char *data;
RelFileNode target_node;
+ bool isinit = (XLogRecGetInfo(r) & XLOG_HEAP_INIT_PAGE) != 0;
+ Pointer rec_data = (Pointer) XLogRecGetData(r);
- xlrec = (xl_heap_update *) XLogRecGetData(r);
+ if (isinit)
+ rec_data += sizeof(TransactionId);
+ xlrec = (xl_heap_update *) rec_data;
/* only interested in our database */
XLogRecGetBlockTag(r, 0, &target_node, NULL, NULL);
@@ -1064,8 +1072,12 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
char *tupledata;
Size tuplelen;
RelFileNode rnode;
+ bool isinit = (XLogRecGetInfo(r) & XLOG_HEAP_INIT_PAGE) != 0;
+ Pointer rec_data = (Pointer) XLogRecGetData(r);
- xlrec = (xl_heap_multi_insert *) XLogRecGetData(r);
+ if (isinit)
+ rec_data += sizeof(TransactionId);
+ xlrec = (xl_heap_multi_insert *) rec_data;
/*
* Ignore insert records without new tuples. This happens when a
@@ -1122,6 +1134,7 @@ DecodeMultiInsert(LogicalDecodingContext *ctx, XLogRecordBuffer *buf)
* We can only figure this out after reassembling the transactions.
*/
tuple->tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&(tuple->tuple));
tuple->tuple.t_len = datalen + SizeofHeapTupleHeader;
@@ -1213,6 +1226,7 @@ DecodeXLogTuple(char *data, Size len, ReorderBufferTupleBuf *tuple)
/* we can only figure this out after reassembling the transactions */
tuple->tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&(tuple->tuple));
/* data is not stored aligned, copy to aligned storage */
memcpy((char *) &xlhdr,
diff --git a/src/backend/replication/logical/logical.c b/src/backend/replication/logical/logical.c
index 9bc3a2d8deb..0e00dac8d94 100644
--- a/src/backend/replication/logical/logical.c
+++ b/src/backend/replication/logical/logical.c
@@ -1614,7 +1614,7 @@ LogicalIncreaseXminForSlot(XLogRecPtr current_lsn, TransactionId xmin)
SpinLockRelease(&slot->mutex);
if (got_new_xmin)
- elog(DEBUG1, "got new catalog xmin %u at %X/%X", xmin,
+ elog(DEBUG1, "got new catalog xmin " XID_FMT " at %X/%X", xmin,
LSN_FORMAT_ARGS(current_lsn));
/* candidate already valid with the current flush position, apply */
diff --git a/src/backend/replication/logical/proto.c b/src/backend/replication/logical/proto.c
index 953942692ce..04560fbd889 100644
--- a/src/backend/replication/logical/proto.c
+++ b/src/backend/replication/logical/proto.c
@@ -50,7 +50,7 @@ logicalrep_write_begin(StringInfo out, ReorderBufferTXN *txn)
/* fixed fields */
pq_sendint64(out, txn->final_lsn);
pq_sendint64(out, txn->xact_time.commit_time);
- pq_sendint32(out, txn->xid);
+ pq_sendint64(out, txn->xid);
}
/*
@@ -64,7 +64,7 @@ logicalrep_read_begin(StringInfo in, LogicalRepBeginData *begin_data)
if (begin_data->final_lsn == InvalidXLogRecPtr)
elog(ERROR, "final_lsn not set in begin message");
begin_data->committime = pq_getmsgint64(in);
- begin_data->xid = pq_getmsgint(in, 4);
+ begin_data->xid = pq_getmsgint64(in);
}
diff --git a/src/backend/replication/logical/reorderbuffer.c b/src/backend/replication/logical/reorderbuffer.c
index d317a626e1e..f2855cadcf5 100644
--- a/src/backend/replication/logical/reorderbuffer.c
+++ b/src/backend/replication/logical/reorderbuffer.c
@@ -2442,7 +2442,7 @@ ReorderBufferProcessTXN(ReorderBuffer *rb, ReorderBufferTXN *txn,
/* this is just a sanity check against bad output plugin behaviour */
if (GetCurrentTransactionIdIfAny() != InvalidTransactionId)
- elog(ERROR, "output plugin used XID %u",
+ elog(ERROR, "output plugin used XID " XID_FMT,
GetCurrentTransactionId());
/*
@@ -2881,7 +2881,7 @@ ReorderBufferAbortOld(ReorderBuffer *rb, TransactionId oldestRunningXid)
if (TransactionIdPrecedes(txn->xid, oldestRunningXid))
{
- elog(DEBUG2, "aborting old transaction %u", txn->xid);
+ elog(DEBUG2, "aborting old transaction " XID_FMT, txn->xid);
/* remove potential on-disk data, and deallocate this tx */
ReorderBufferCleanupTXN(rb, txn);
@@ -3523,7 +3523,7 @@ ReorderBufferSerializeTXN(ReorderBuffer *rb, ReorderBufferTXN *txn)
Size spilled = 0;
Size size = txn->size;
- elog(DEBUG2, "spill %u changes in XID %u to disk",
+ elog(DEBUG2, "spill %u changes in XID " XID_FMT " to disk",
(uint32) txn->nentries_mem, txn->xid);
/* do the same to all child TXs */
@@ -3800,7 +3800,7 @@ ReorderBufferSerializeChange(ReorderBuffer *rb, ReorderBufferTXN *txn,
errno = save_errno ? save_errno : ENOSPC;
ereport(ERROR,
(errcode_for_file_access(),
- errmsg("could not write to data file for XID %u: %m",
+ errmsg("could not write to data file for XID %" PRIu64 ": %m",
txn->xid)));
}
pgstat_report_wait_end();
@@ -4443,7 +4443,7 @@ ReorderBufferSerializedPath(char *path, ReplicationSlot *slot, TransactionId xid
XLogSegNoOffsetToRecPtr(segno, 0, wal_segment_size, recptr);
- snprintf(path, MAXPGPATH, "pg_replslot/%s/xid-%u-lsn-%X-%X.spill",
+ snprintf(path, MAXPGPATH, "pg_replslot/%s/xid-" XID_FMT "-lsn-%X-%X.spill",
NameStr(MyReplicationSlot->data.name),
xid, LSN_FORMAT_ARGS(recptr));
}
@@ -5024,8 +5024,12 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
TransactionId f_mapped_xid;
TransactionId f_create_xid;
XLogRecPtr f_lsn;
- uint32 f_hi,
- f_lo;
+ uint32 f_lsn_hi,
+ f_lsn_lo,
+ f_mapped_xid_hi,
+ f_mapped_xid_lo,
+ f_create_xid_hi,
+ f_create_xid_lo;
RewriteMappingFile *f;
if (strcmp(mapping_de->d_name, ".") == 0 ||
@@ -5037,11 +5041,14 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
continue;
if (sscanf(mapping_de->d_name, LOGICAL_REWRITE_FORMAT,
- &f_dboid, &f_relid, &f_hi, &f_lo,
- &f_mapped_xid, &f_create_xid) != 6)
+ &f_dboid, &f_relid, &f_lsn_hi, &f_lsn_lo,
+ &f_mapped_xid_hi, &f_mapped_xid_lo,
+ &f_create_xid_hi, &f_create_xid_lo) != 8)
elog(ERROR, "could not parse filename \"%s\"", mapping_de->d_name);
- f_lsn = ((uint64) f_hi) << 32 | f_lo;
+ f_lsn = ((uint64) f_lsn_hi) << 32 | f_lsn_lo;
+ f_mapped_xid = ((uint64) f_mapped_xid_hi) << 32 | f_mapped_xid_lo;
+ f_create_xid = ((uint64) f_create_xid_hi) << 32 | f_create_xid_lo;
/* mapping for another database */
if (f_dboid != dboid)
@@ -5074,7 +5081,7 @@ UpdateLogicalMappings(HTAB *tuplecid_data, Oid relid, Snapshot snapshot)
{
RewriteMappingFile *f = (RewriteMappingFile *) lfirst(file);
- elog(DEBUG1, "applying mapping: \"%s\" in %u", f->fname,
+ elog(DEBUG1, "applying mapping: \"%s\" in " XID_FMT, f->fname,
snapshot->subxip[0]);
ApplyLogicalMappingFile(tuplecid_data, relid, f->fname);
pfree(f);
diff --git a/src/backend/replication/logical/snapbuild.c b/src/backend/replication/logical/snapbuild.c
index ab04e946bc2..1cc0bd2d891 100644
--- a/src/backend/replication/logical/snapbuild.c
+++ b/src/backend/replication/logical/snapbuild.c
@@ -847,7 +847,7 @@ SnapBuildDistributeNewCatalogSnapshot(SnapBuild *builder, XLogRecPtr lsn)
if (rbtxn_prepared(txn) || rbtxn_skip_prepared(txn))
continue;
- elog(DEBUG2, "adding a new snapshot to %u at %X/%X",
+ elog(DEBUG2, "adding a new snapshot to " XID_FMT " at %X/%X",
txn->xid, LSN_FORMAT_ARGS(lsn));
/*
@@ -922,7 +922,7 @@ SnapBuildPurgeCommittedTxn(SnapBuild *builder)
memcpy(builder->committed.xip, workspace,
surviving_xids * sizeof(TransactionId));
- elog(DEBUG3, "purged committed transactions from %u to %u, xmin: %u, xmax: %u",
+ elog(DEBUG3, "purged committed transactions from %u to %u, xmin: " XID_FMT ", xmax: " XID_FMT,
(uint32) builder->committed.xcnt, (uint32) surviving_xids,
builder->xmin, builder->xmax);
builder->committed.xcnt = surviving_xids;
@@ -988,7 +988,7 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
sub_needs_timetravel = true;
needs_snapshot = true;
- elog(DEBUG1, "found subtransaction %u:%u with catalog changes",
+ elog(DEBUG1, "found subtransaction " XID_FMT ":" XID_FMT " with catalog changes",
xid, subxid);
SnapBuildAddCommittedTxn(builder, subxid);
@@ -1014,7 +1014,7 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
/* if top-level modified catalog, it'll need a snapshot */
if (ReorderBufferXidHasCatalogChanges(builder->reorder, xid))
{
- elog(DEBUG2, "found top level transaction %u, with catalog changes",
+ elog(DEBUG2, "found top level transaction " XID_FMT ", with catalog changes",
xid);
needs_snapshot = true;
needs_timetravel = true;
@@ -1027,7 +1027,7 @@ SnapBuildCommitTxn(SnapBuild *builder, XLogRecPtr lsn, TransactionId xid,
}
else if (needs_timetravel)
{
- elog(DEBUG2, "forced transaction %u to do timetravel", xid);
+ elog(DEBUG2, "forced transaction " XID_FMT " to do timetravel", xid);
SnapBuildAddCommittedTxn(builder, xid);
}
@@ -1149,7 +1149,7 @@ SnapBuildProcessRunningXacts(SnapBuild *builder, XLogRecPtr lsn, xl_running_xact
xmin = ReorderBufferGetOldestXmin(builder->reorder);
if (xmin == InvalidTransactionId)
xmin = running->oldestRunningXid;
- elog(DEBUG3, "xmin: %u, xmax: %u, oldest running: %u, oldest xmin: %u",
+ elog(DEBUG3, "xmin: " XID_FMT ", xmax: " XID_FMT ", oldest running: " XID_FMT ", oldest xmin: " XID_FMT,
builder->xmin, builder->xmax, running->oldestRunningXid, xmin);
LogicalIncreaseXminForSlot(lsn, xmin);
@@ -1239,7 +1239,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
ereport(DEBUG1,
(errmsg_internal("skipping snapshot at %X/%X while building logical decoding snapshot, xmin horizon too low",
LSN_FORMAT_ARGS(lsn)),
- errdetail_internal("initial xmin horizon of %u vs the snapshot's %u",
+ errdetail_internal("initial xmin horizon of " XID_FMT " vs the snapshot's " XID_FMT,
builder->initial_xmin_horizon, running->oldestRunningXid)));
@@ -1322,7 +1322,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
ereport(LOG,
(errmsg("logical decoding found initial starting point at %X/%X",
LSN_FORMAT_ARGS(lsn)),
- errdetail("Waiting for transactions (approximately %d) older than %u to end.",
+ errdetail("Waiting for transactions (approximately %d) older than %" PRIu64 " to end.",
running->xcnt, running->nextXid)));
SnapBuildWaitSnapshot(running, running->nextXid);
@@ -1346,7 +1346,7 @@ SnapBuildFindSnapshot(SnapBuild *builder, XLogRecPtr lsn, xl_running_xacts *runn
ereport(LOG,
(errmsg("logical decoding found initial consistent point at %X/%X",
LSN_FORMAT_ARGS(lsn)),
- errdetail("Waiting for transactions (approximately %d) older than %u to end.",
+ errdetail("Waiting for transactions (approximately %d) older than %" PRIu64 " to end.",
running->xcnt, running->nextXid)));
SnapBuildWaitSnapshot(running, running->nextXid);
diff --git a/src/backend/replication/logical/worker.c b/src/backend/replication/logical/worker.c
index c9af775bc18..7a43c275cd3 100644
--- a/src/backend/replication/logical/worker.c
+++ b/src/backend/replication/logical/worker.c
@@ -1046,7 +1046,7 @@ apply_handle_stream_prepare(StringInfo s)
logicalrep_read_stream_prepare(s, &prepare_data);
set_apply_error_context_xact(prepare_data.xid, prepare_data.prepare_time);
- elog(DEBUG1, "received prepare for streamed transaction %u", prepare_data.xid);
+ elog(DEBUG1, "received prepare for streamed transaction " XID_FMT, prepare_data.xid);
/* Replay all the spooled operations. */
apply_spooled_messages(prepare_data.xid, prepare_data.prepare_lsn);
@@ -1428,7 +1428,7 @@ apply_handle_stream_commit(StringInfo s)
xid = logicalrep_read_stream_commit(s, &commit_data);
set_apply_error_context_xact(xid, commit_data.committime);
- elog(DEBUG1, "received commit for streamed transaction %u", xid);
+ elog(DEBUG1, "received commit for streamed transaction " XID_FMT, xid);
apply_spooled_messages(xid, commit_data.commit_lsn);
@@ -3203,14 +3203,14 @@ subxact_info_add(TransactionId xid)
static inline void
subxact_filename(char *path, Oid subid, TransactionId xid)
{
- snprintf(path, MAXPGPATH, "%u-%u.subxacts", subid, xid);
+ snprintf(path, MAXPGPATH, "%u-" XID_FMT ".subxacts", subid, xid);
}
/* format filename for file containing serialized changes */
static inline void
changes_filename(char *path, Oid subid, TransactionId xid)
{
- snprintf(path, MAXPGPATH, "%u-%u.changes", subid, xid);
+ snprintf(path, MAXPGPATH, "%u-" XID_FMT ".changes", subid, xid);
}
/*
@@ -3372,7 +3372,7 @@ TwoPhaseTransactionGid(Oid subid, TransactionId xid, char *gid, int szgid)
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg_internal("invalid two-phase transaction ID")));
- snprintf(gid, szgid, "pg_gid_%u_%u", subid, xid);
+ snprintf(gid, szgid, "pg_gid_%u_" XID_FMT, subid, xid);
}
/* Logical Replication Apply worker entry point */
@@ -3689,7 +3689,7 @@ apply_error_callback(void *arg)
/* append transaction information */
if (TransactionIdIsNormal(errarg->remote_xid))
{
- appendStringInfo(&buf, _(" in transaction %u"), errarg->remote_xid);
+ appendStringInfo(&buf, _(" in transaction " XID_FMT), errarg->remote_xid);
if (errarg->ts != 0)
appendStringInfo(&buf, _(" at %s"),
timestamptz_to_str(errarg->ts));
diff --git a/src/backend/replication/walreceiver.c b/src/backend/replication/walreceiver.c
index b39fce8c23c..a64249e5daf 100644
--- a/src/backend/replication/walreceiver.c
+++ b/src/backend/replication/walreceiver.c
@@ -1142,10 +1142,6 @@ static void
XLogWalRcvSendHSFeedback(bool immed)
{
TimestampTz now;
- FullTransactionId nextFullXid;
- TransactionId nextXid;
- uint32 xmin_epoch,
- catalog_xmin_epoch;
TransactionId xmin,
catalog_xmin;
static TimestampTz sendTime = 0;
@@ -1202,30 +1198,15 @@ XLogWalRcvSendHSFeedback(bool immed)
catalog_xmin = InvalidTransactionId;
}
- /*
- * Get epoch and adjust if nextXid and oldestXmin are different sides of
- * the epoch boundary.
- */
- nextFullXid = ReadNextFullTransactionId();
- nextXid = XidFromFullTransactionId(nextFullXid);
- xmin_epoch = EpochFromFullTransactionId(nextFullXid);
- catalog_xmin_epoch = xmin_epoch;
- if (nextXid < xmin)
- xmin_epoch--;
- if (nextXid < catalog_xmin)
- catalog_xmin_epoch--;
-
- elog(DEBUG2, "sending hot standby feedback xmin %u epoch %u catalog_xmin %u catalog_xmin_epoch %u",
- xmin, xmin_epoch, catalog_xmin, catalog_xmin_epoch);
+ elog(DEBUG2, "sending hot standby feedback xmin " XID_FMT " catalog_xmin " XID_FMT,
+ xmin, catalog_xmin);
/* Construct the message and send it. */
resetStringInfo(&reply_message);
pq_sendbyte(&reply_message, 'h');
pq_sendint64(&reply_message, GetCurrentTimestamp());
- pq_sendint32(&reply_message, xmin);
- pq_sendint32(&reply_message, xmin_epoch);
- pq_sendint32(&reply_message, catalog_xmin);
- pq_sendint32(&reply_message, catalog_xmin_epoch);
+ pq_sendint64(&reply_message, xmin);
+ pq_sendint64(&reply_message, catalog_xmin);
walrcv_send(wrconn, reply_message.data, reply_message.len);
if (TransactionIdIsValid(xmin) || TransactionIdIsValid(catalog_xmin))
primary_has_standby_xmin = true;
diff --git a/src/backend/replication/walsender.c b/src/backend/replication/walsender.c
index 4cf95ce4392..9268bd62ad5 100644
--- a/src/backend/replication/walsender.c
+++ b/src/backend/replication/walsender.c
@@ -252,7 +252,6 @@ static void WalSndUpdateProgress(LogicalDecodingContext *ctx, XLogRecPtr lsn, Tr
static XLogRecPtr WalSndWaitForWal(XLogRecPtr loc);
static void LagTrackerWrite(XLogRecPtr lsn, TimestampTz local_flush_time);
static TimeOffset LagTrackerRead(int head, XLogRecPtr lsn, TimestampTz now);
-static bool TransactionIdInRecentPast(TransactionId xid, uint32 epoch);
static void WalSndSegmentOpen(XLogReaderState *state, XLogSegNo nextSegNo,
TimeLineID *tli_p);
@@ -2136,44 +2135,6 @@ PhysicalReplicationSlotNewXmin(TransactionId feedbackXmin, TransactionId feedbac
}
}
-/*
- * Check that the provided xmin/epoch are sane, that is, not in the future
- * and not so far back as to be already wrapped around.
- *
- * Epoch of nextXid should be same as standby, or if the counter has
- * wrapped, then one greater than standby.
- *
- * This check doesn't care about whether clog exists for these xids
- * at all.
- */
-static bool
-TransactionIdInRecentPast(TransactionId xid, uint32 epoch)
-{
- FullTransactionId nextFullXid;
- TransactionId nextXid;
- uint32 nextEpoch;
-
- nextFullXid = ReadNextFullTransactionId();
- nextXid = XidFromFullTransactionId(nextFullXid);
- nextEpoch = EpochFromFullTransactionId(nextFullXid);
-
- if (xid <= nextXid)
- {
- if (epoch != nextEpoch)
- return false;
- }
- else
- {
- if (epoch + 1 != nextEpoch)
- return false;
- }
-
- if (!TransactionIdPrecedesOrEquals(xid, nextXid))
- return false; /* epoch OK, but it's wrapped around */
-
- return true;
-}
-
/*
* Hot Standby feedback
*/
@@ -2181,9 +2142,7 @@ static void
ProcessStandbyHSFeedbackMessage(void)
{
TransactionId feedbackXmin;
- uint32 feedbackEpoch;
TransactionId feedbackCatalogXmin;
- uint32 feedbackCatalogEpoch;
TimestampTz replyTime;
/*
@@ -2192,10 +2151,8 @@ ProcessStandbyHSFeedbackMessage(void)
* of this message.
*/
replyTime = pq_getmsgint64(&reply_message);
- feedbackXmin = pq_getmsgint(&reply_message, 4);
- feedbackEpoch = pq_getmsgint(&reply_message, 4);
- feedbackCatalogXmin = pq_getmsgint(&reply_message, 4);
- feedbackCatalogEpoch = pq_getmsgint(&reply_message, 4);
+ feedbackXmin = pq_getmsgint64(&reply_message);
+ feedbackCatalogXmin = pq_getmsgint64(&reply_message);
if (message_level_is_interesting(DEBUG2))
{
@@ -2204,11 +2161,9 @@ ProcessStandbyHSFeedbackMessage(void)
/* Copy because timestamptz_to_str returns a static buffer */
replyTimeStr = pstrdup(timestamptz_to_str(replyTime));
- elog(DEBUG2, "hot standby feedback xmin %u epoch %u, catalog_xmin %u epoch %u reply_time %s",
+ elog(DEBUG2, "hot standby feedback xmin " XID_FMT ", catalog_xmin " XID_FMT " reply_time %s",
feedbackXmin,
- feedbackEpoch,
feedbackCatalogXmin,
- feedbackCatalogEpoch,
replyTimeStr);
pfree(replyTimeStr);
@@ -2239,18 +2194,6 @@ ProcessStandbyHSFeedbackMessage(void)
return;
}
- /*
- * Check that the provided xmin/epoch are sane, that is, not in the future
- * and not so far back as to be already wrapped around. Ignore if not.
- */
- if (TransactionIdIsNormal(feedbackXmin) &&
- !TransactionIdInRecentPast(feedbackXmin, feedbackEpoch))
- return;
-
- if (TransactionIdIsNormal(feedbackCatalogXmin) &&
- !TransactionIdInRecentPast(feedbackCatalogXmin, feedbackCatalogEpoch))
- return;
-
/*
* Set the WalSender's xmin equal to the standby's requested xmin, so that
* the xmin will be taken into account by GetSnapshotData() /
diff --git a/src/backend/statistics/extended_stats.c b/src/backend/statistics/extended_stats.c
index 87fe82ed114..793217f3bef 100644
--- a/src/backend/statistics/extended_stats.c
+++ b/src/backend/statistics/extended_stats.c
@@ -2431,6 +2431,7 @@ statext_expressions_load(Oid stxoid, bool inh, int idx)
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = td;
+ HeapTupleSetZeroBase(&tmptup);
tup = heap_copytuple(&tmptup);
diff --git a/src/backend/storage/buffer/Makefile b/src/backend/storage/buffer/Makefile
index fd7c40dcb08..ffcc0fc290e 100644
--- a/src/backend/storage/buffer/Makefile
+++ b/src/backend/storage/buffer/Makefile
@@ -17,6 +17,7 @@ OBJS = \
buf_table.o \
bufmgr.o \
freelist.o \
- localbuf.o
+ localbuf.o \
+ heap_convert.o
include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index a2512e750c2..e30875422b0 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -457,7 +457,7 @@ ForgetPrivateRefCountEntry(PrivateRefCountEntry *ref)
)
-static Buffer ReadBuffer_common(SMgrRelation reln, char relpersistence,
+static Buffer ReadBuffer_common(Relation rel, SMgrRelation reln, char relpersistence,
ForkNumber forkNum, BlockNumber blockNum,
ReadBufferMode mode, BufferAccessStrategy strategy,
bool *hit);
@@ -759,7 +759,8 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
* miss.
*/
pgstat_count_buffer_read(reln);
- buf = ReadBuffer_common(RelationGetSmgr(reln), reln->rd_rel->relpersistence,
+ buf = ReadBuffer_common(reln, RelationGetSmgr(reln),
+ reln->rd_rel->relpersistence,
forkNum, blockNum, mode, strategy, &hit);
if (hit)
pgstat_count_buffer_hit(reln);
@@ -787,7 +788,7 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
Assert(InRecovery);
- return ReadBuffer_common(smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
+ return ReadBuffer_common(NULL, smgr, RELPERSISTENCE_PERMANENT, forkNum, blockNum,
mode, strategy, &hit);
}
@@ -798,7 +799,7 @@ ReadBufferWithoutRelcache(RelFileNode rnode, ForkNumber forkNum,
* *hit is set to true if the request was satisfied from shared buffer cache.
*/
static Buffer
-ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
+ReadBuffer_common(Relation rel, SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
BlockNumber blockNum, ReadBufferMode mode,
BufferAccessStrategy strategy, bool *hit)
{
@@ -1030,6 +1031,16 @@ ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
blockNum,
relpath(smgr->smgr_rnode, forkNum))));
}
+
+ if (PageGetPageLayoutVersion(bufBlock) != PG_PAGE_LAYOUT_VERSION &&
+ !PageIsNew((Page) bufBlock))
+ {
+ Buffer buf = BufferDescriptorGetBuffer(bufHdr);
+
+ LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_EXCLUSIVE);
+ convert_page(rel, bufBlock, buf, blockNum);
+ LWLockRelease(BufferDescriptorGetContentLock(bufHdr));
+ }
}
}
diff --git a/src/backend/storage/buffer/heap_convert.c b/src/backend/storage/buffer/heap_convert.c
new file mode 100644
index 00000000000..6c9c5456b04
--- /dev/null
+++ b/src/backend/storage/buffer/heap_convert.c
@@ -0,0 +1,496 @@
+/*
+ * heap_convert.c
+ * Heap page converter from 32bit to 64bit xid format
+ *
+ * Copyright (c) 2017, Postgres Professional
+ *
+ * src/backend/storage/buffer/heap_convert.c
+ */
+
+#include "postgres.h"
+
+#include "access/brin_page.h"
+#include "access/ginblock.h"
+#include "access/generic_xlog.h"
+#include "access/heapam.h"
+#include "access/htup_details.h"
+#include "access/multixact.h"
+#include "access/nbtree.h"
+#include "access/transam.h"
+#include "access/xact.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_control.h"
+#include "common/controldata_utils.h"
+#include "miscadmin.h"
+#include "storage/bufmgr.h"
+#include "storage/bufpage.h"
+#include "storage/checksum.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+/*
+ * Sorting support for repack_tuples()
+ */
+typedef struct itemData
+{
+ uint16 index; /* linpointer array index */
+ int16 offset; /* page offset of item data */
+ uint16 alignedlen; /* MAXALIGN(item data len) */
+} itemData;
+
+/* Initialize special heap page area */
+static void
+InitHeapPageSpecial(PageHeader new, TransactionId xid_base,
+ MultiXactId multi_base, TransactionId prune_xid)
+{
+ HeapPageSpecial special;
+
+ new->pd_special = BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData));
+
+ special = (HeapPageSpecial) ((char *) new + new->pd_special);
+ special->pd_xid_base = xid_base;
+ special->pd_multi_base = multi_base;
+ HeapPageSetPruneXid(new, prune_xid);
+}
+
+/* Used by get_previous_edition() */
+ControlFileData *ControlFile = NULL;
+
+static bool was_32bit_xid(PageHeader page);
+static void convert_heap(Relation rel, Page page, Buffer buf, BlockNumber blkno);
+static void repack_heap_tuples(Relation rel, Page page, Buffer buf,
+ BlockNumber blkno, bool double_xmax);
+static void tuple_set_double_xmax(HeapTupleHeader tuple);
+
+
+/*
+ * itemoffcompare
+ * Sorting support for repack_tuples()
+ */
+int
+itemoffcompare(const void *item1, const void *item2)
+{
+ /* Sort in decreasing itemoff order */
+ return ((itemIdCompactData *) item2)->itemoff -
+ ((itemIdCompactData *) item1)->itemoff;
+}
+
+static bool
+was_32bit_xid(PageHeader page)
+{
+ return PageGetPageLayoutVersion(page) < 5;
+}
+
+void
+convert_page(Relation rel, Page page, Buffer buf, BlockNumber blkno)
+{
+ PageHeader hdr = (PageHeader) page;
+ GenericXLogState *state = NULL;
+ Page tmp_page = page;
+ uint16 checksum;
+
+ if (!rel)
+ return;
+
+ /* Verify checksum */
+ if (hdr->pd_checksum)
+ {
+ checksum = pg_checksum_page((char *) page, blkno);
+ if (checksum != hdr->pd_checksum)
+ ereport(ERROR,
+ (errcode(ERRCODE_INDEX_CORRUPTED),
+ errmsg("page verification failed, calculated checksum %u but expected %u",
+ checksum, hdr->pd_checksum)));
+ }
+
+ /* Start xlog record */
+ if (!XactReadOnly && XLogIsNeeded() && RelationNeedsWAL(rel))
+ {
+ state = GenericXLogStart(rel);
+ tmp_page = GenericXLogRegisterBuffer(state, buf, GENERIC_XLOG_FULL_IMAGE);
+ }
+
+ PageSetPageSizeAndVersion((hdr), PageGetPageSize(hdr),
+ PG_PAGE_LAYOUT_VERSION);
+
+ if (was_32bit_xid(hdr))
+ {
+ switch (rel->rd_rel->relkind)
+ {
+ case 'r':
+ case 'p':
+ case 't':
+ case 'm':
+ convert_heap(rel, tmp_page, buf, blkno);
+ break;
+ case 'i':
+ /* no need to convert index */
+ case 'S':
+ /* no real need to convert sequences */
+ break;
+ default:
+ elog(ERROR,
+ "Conversion for relkind '%c' is not implemented",
+ rel->rd_rel->relkind);
+ }
+ }
+
+ /*
+ * Mark buffer dirty unless this is a read-only transaction (e.g. query
+ * is running on hot standby instance)
+ */
+ if (!XactReadOnly)
+ {
+ /* Finish xlog record */
+ if (XLogIsNeeded() && RelationNeedsWAL(rel))
+ {
+ Assert(state != NULL);
+ GenericXLogFinish(state);
+ }
+
+ MarkBufferDirty(buf);
+ }
+
+ hdr = (PageHeader) page;
+ hdr->pd_checksum = pg_checksum_page((char *) page, blkno);
+}
+
+static void
+convert_heap(Relation rel, Page page, Buffer buf, BlockNumber blkno)
+{
+ PageHeader page_hdr = (PageHeader) page;
+ bool heap_special_fits;
+
+ /* Is there enough space to fit new page format? */
+ heap_special_fits = page_hdr->pd_upper - page_hdr->pd_lower >= SizeOfPageSpecial;
+ repack_heap_tuples(rel, page, buf, blkno, !heap_special_fits);
+}
+
+/*
+ * Convert possibly wrapped around heap tuple's transaction and
+ * multixact IDs after pg_upgrade.
+ */
+static void
+convert_heap_tuple_xids(HeapTupleHeader tuple, TransactionId xid_base,
+ MultiXactId mxid_base, bool double_xmax)
+{
+ TransactionId xid;
+
+ /* Convert xmin xid */
+ if (double_xmax)
+ tuple_set_double_xmax(tuple);
+ else
+ {
+ /* Subtract xid_base from normal xmin */
+ xid = tuple->t_choice.t_heap.t_xmin;
+
+ if (TransactionIdIsNormal(xid))
+ {
+ Assert(xid >= xid_base + FirstNormalTransactionId);
+ tuple->t_choice.t_heap.t_xmin = xid - xid_base;
+ }
+ }
+
+ /* If tuple has multixact flag, handle mxid wraparound */
+ if ((tuple->t_infomask & HEAP_XMAX_IS_MULTI) &&
+ !(tuple->t_infomask & HEAP_XMAX_INVALID))
+ {
+ MultiXactId mxid = tuple->t_choice.t_heap.t_xmax;
+
+ /* Handle mxid wraparound */
+ if (mxid < mxid_base)
+ {
+ mxid += ((MultiXactId) 1 << 32) - FirstMultiXactId;
+ Assert(mxid >= mxid_base);
+ }
+
+ if (double_xmax)
+ {
+ /* Save converted mxid into xmin/max */
+ HeapTupleHeaderSetDoubleXmax(tuple, mxid);
+ }
+ else
+ {
+ /*
+ * Save converted mxid offset relative to (minmxid - 1),
+ * which will be page's mxid base.
+ */
+ Assert(mxid - mxid_base + FirstMultiXactId <= PG_UINT32_MAX);
+ tuple->t_choice.t_heap.t_xmax =
+ (uint32) (mxid - mxid_base + FirstMultiXactId);
+ }
+ }
+ /* Convert xmax xid */
+ else if (!(tuple->t_infomask & HEAP_XMAX_INVALID))
+ {
+ xid = tuple->t_choice.t_heap.t_xmax;
+
+ if (double_xmax)
+ {
+ /* Save converted mxid into xmin/max */
+ HeapTupleHeaderSetDoubleXmax(tuple, xid);
+ }
+ else if (TransactionIdIsNormal(xid))
+ {
+ /* Subtract xid_base from normal xmax */
+ Assert(xid >= xid_base + FirstNormalTransactionId);
+ tuple->t_choice.t_heap.t_xmax = xid - xid_base;
+ }
+ }
+}
+
+/*
+ * Compute page's [m]xid min/max values for based/"double xmax"
+ * format conversions
+ */
+static void
+compute_xid_min_max(HeapTuple tuple, MultiXactId mxid_base,
+ TransactionId *xid_min, TransactionId *xid_max,
+ MultiXactId *mxid_min, MultiXactId *mxid_max)
+{
+ if (!HeapTupleHeaderXminInvalid(tuple->t_data) &&
+ !HeapTupleHeaderXminFrozen(tuple->t_data))
+ {
+ TransactionId xid = HeapTupleGetRawXmin(tuple);
+
+ if (TransactionIdIsNormal(xid))
+ {
+ if (*xid_max < xid)
+ *xid_max = xid;
+ if (*xid_min > xid)
+ *xid_min = xid;
+ }
+ }
+
+ if (!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID))
+ {
+ TransactionId xid;
+
+ if (tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI)
+ {
+ MultiXactId mxid = HeapTupleGetRawXmax(tuple);
+
+ Assert(MultiXactIdIsValid(mxid));
+
+ /* Handle mxid wraparound */
+ if (mxid < mxid_base)
+ {
+ mxid += ((MultiXactId) 1 << 32) - FirstMultiXactId;
+ Assert(mxid >= mxid_base);
+ }
+
+ if (*mxid_max < mxid)
+ *mxid_max = mxid;
+ if (*mxid_min > mxid)
+ *mxid_min = mxid;
+
+ /*
+ * Also take into account hidden update xid, which can be
+ * extracted by the vacuum.
+ */
+ if (tuple->t_data->t_infomask & HEAP_XMAX_LOCK_ONLY)
+ xid = InvalidTransactionId;
+ else
+ xid = HeapTupleGetUpdateXid(tuple);
+ }
+ else
+ {
+ xid = HeapTupleGetRawXmax(tuple);
+ }
+
+ if (TransactionIdIsNormal(xid))
+ {
+ if (*xid_max < xid)
+ *xid_max = xid;
+ if (*xid_min > xid)
+ *xid_min = xid;
+ }
+ }
+}
+
+/* Returns true, if "double xmax" format */
+static bool
+init_heap_page_header(Relation rel, BlockNumber blkno, PageHeader new_hdr,
+ TransactionId prune_xid, bool header_fits,
+ TransactionId xid_min, TransactionId xid_max,
+ MultiXactId mxid_min, MultiXactId mxid_max,
+ TransactionId *xid_base, MultiXactId *mxid_base)
+{
+ if (header_fits &&
+ (xid_max == InvalidTransactionId ||
+ xid_max - xid_min <= MaxShortTransactionId - FirstNormalTransactionId) &&
+ (mxid_max == InvalidMultiXactId ||
+ mxid_max - mxid_min <= MaxShortTransactionId - FirstMultiXactId))
+ {
+ Assert(xid_max == InvalidTransactionId || xid_max >= xid_min);
+ Assert(mxid_max == InvalidMultiXactId || mxid_max >= mxid_min);
+ *xid_base = xid_max == InvalidTransactionId ? InvalidTransactionId : xid_min - FirstNormalTransactionId;
+ *mxid_base = mxid_max == InvalidMultiXactId ? InvalidMultiXactId : mxid_min - FirstMultiXactId;
+
+ InitHeapPageSpecial(new_hdr, *xid_base, *mxid_base, prune_xid);
+ return false;
+ }
+ else
+ {
+ /* No space for special area, switch to "double xmax" format */
+ new_hdr->pd_special = BLCKSZ;
+
+ *xid_base = InvalidTransactionId;
+ *mxid_base = InvalidMultiXactId;
+
+ elog(DEBUG2, "convert heap page %u of relation %u to double xmax format",
+ blkno, RelationGetRelid(rel));
+ return true;
+ }
+}
+
+/*
+ * repack_heap_tuples
+ * Convert heap page format reusing space of dead tuples
+ */
+static void
+repack_heap_tuples(Relation rel, Page page, Buffer buf, BlockNumber blkno,
+ bool perhaps_double_xmax)
+{
+ itemIdCompactData items[MaxHeapTuplesPerPage];
+ itemIdCompact itemPtr = items;
+ ItemId lp;
+ int nitems = 0;
+ int maxoff = PageGetMaxOffsetNumber(page);
+ Offset upper;
+ int idx;
+ bool double_xmax;
+
+ PageHeader hdr;
+ PageHeader new_hdr;
+ char new_page[BLCKSZ];
+ MultiXactId mxid_base = rel->rd_rel->relminmxid;
+ MultiXactId mxid_min = MaxMultiXactId;
+ MultiXactId mxid_max = InvalidMultiXactId;
+ TransactionId xid_base = rel->rd_rel->relfrozenxid;
+ TransactionId xid_min = MaxTransactionId;
+ TransactionId xid_max = InvalidTransactionId;
+
+ int occupied_space = 0;
+
+ hdr = (PageHeader) page;
+
+ if (TransactionIdIsNormal(hdr->pd_prune_xid))
+ xid_min = xid_max = hdr->pd_prune_xid;
+
+ for (idx = 0; idx < maxoff; idx++)
+ {
+ HeapTupleData tuple;
+
+ lp = PageGetItemId(page, idx + 1);
+
+ /* Skip redirects and items without storage */
+ if (!ItemIdHasStorage(lp))
+ continue;
+
+ /* Build in-memory tuple representation */
+ tuple.t_tableOid = 1; /* doesn't matter in this case */
+ tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp);
+ tuple.t_xid_base = 0;
+ tuple.t_multi_base = 0;
+ tuple.t_len = ItemIdGetLength(lp);
+ ItemPointerSet(&(tuple.t_self), blkno, ItemIdGetOffset(lp));
+
+ /*
+ * This is only needed to determine whether tuple is HEAPTUPLE_DEAD or
+ * HEAPTUPLE_RECENTLY_DEAD. And since this is the first time we read
+ * page after pg_upgrade, it cannot be HEAPTUPLE_RECENTLY_DEAD. See
+ * HeapTupleSatisfiesVacuum() for details
+ */
+ if (perhaps_double_xmax &&
+ HeapTupleSatisfiesVacuum(&tuple, FirstUpgradedTransactionId, buf) == HEAPTUPLE_DEAD)
+ {
+ ItemIdSetDead(lp);
+ }
+
+ if (ItemIdIsNormal(lp) && ItemIdHasStorage(lp))
+ {
+ itemPtr->offsetindex = idx;
+ itemPtr->itemoff = ItemIdGetOffset(lp);
+ if (unlikely(itemPtr->itemoff < hdr->pd_upper ||
+ itemPtr->itemoff >= hdr->pd_special))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_CORRUPTED),
+ errmsg("corrupted item pointer: %u",
+ itemPtr->itemoff)));
+ itemPtr->alignedlen = MAXALIGN(ItemIdGetLength(lp));
+ occupied_space += itemPtr->alignedlen;
+ nitems++;
+ itemPtr++;
+ if (perhaps_double_xmax)
+ {
+ HeapTupleSetXmin(&tuple, FrozenTransactionId);
+ HeapTupleHeaderSetXminFrozen(tuple.t_data);
+ }
+
+ compute_xid_min_max(&tuple, mxid_base,
+ &xid_min, &xid_max,
+ &mxid_min, &mxid_max);
+ }
+ }
+
+ /* Write new header */
+ new_hdr = (PageHeader) new_page;
+ *new_hdr = *hdr;
+ new_hdr->pd_lower = SizeOfPageHeaderData + maxoff * sizeof(ItemIdData);
+
+ double_xmax = init_heap_page_header(rel, blkno, new_hdr,
+ hdr->pd_prune_xid,
+ BLCKSZ - new_hdr->pd_lower - occupied_space >= sizeof(HeapPageSpecialData),
+ xid_min, xid_max,
+ mxid_min, mxid_max,
+ &xid_base, &mxid_base);
+ if (!perhaps_double_xmax && double_xmax)
+ return repack_heap_tuples(rel, page, buf, blkno, true);
+
+ /* Copy ItemIds with an offset */
+ memcpy(new_page + SizeOfPageHeaderData,
+ page + SizeOfPageHeaderData,
+ hdr->pd_lower - SizeOfPageHeaderData);
+
+ /* Move live tuples */
+ upper = new_hdr->pd_special;
+ for (idx = 0; idx < nitems; idx++)
+ {
+ HeapTupleHeader tuple;
+
+ itemPtr = &items[idx];
+ lp = PageGetItemId(new_page, itemPtr->offsetindex + 1);
+ upper -= itemPtr->alignedlen;
+
+ memcpy((char *) new_page + upper,
+ (char *) page + itemPtr->itemoff,
+ itemPtr->alignedlen);
+
+ tuple = (HeapTupleHeader) (((char *) new_page) + upper);
+
+ convert_heap_tuple_xids(tuple, xid_base, mxid_base, double_xmax);
+
+ lp->lp_off = upper;
+
+ occupied_space -= itemPtr->alignedlen;
+ }
+ Assert(occupied_space == 0);
+
+ new_hdr->pd_upper = upper;
+ if (new_hdr->pd_lower > new_hdr->pd_upper)
+ elog(ERROR, "cannot convert block %u of relation '%s'",
+ blkno, RelationGetRelationName(rel));
+
+ memcpy(page, new_page, BLCKSZ);
+}
+
+/*
+ * Convert tuple for "double xmax" page format.
+ */
+static void
+tuple_set_double_xmax(HeapTupleHeader tuple)
+{
+ tuple->t_infomask |= HEAP_XMIN_FROZEN;
+ tuple->t_choice.t_heap.t_xmin = 0;
+}
diff --git a/src/backend/storage/ipc/procarray.c b/src/backend/storage/ipc/procarray.c
index 3be60402890..5d53c77497b 100644
--- a/src/backend/storage/ipc/procarray.c
+++ b/src/backend/storage/ipc/procarray.c
@@ -66,7 +66,7 @@
#include "utils/rel.h"
#include "utils/snapmgr.h"
-#define UINT32_ACCESS_ONCE(var) ((uint32)(*((volatile uint32 *)&(var))))
+#define UINT64_ACCESS_ONCE(var) ((uint64)(*((volatile uint64 *)&(var))))
/* Our shared memory area */
typedef struct ProcArrayStruct
@@ -351,9 +351,6 @@ static inline void ProcArrayEndTransactionInternal(PGPROC *proc, TransactionId l
static void ProcArrayGroupClearXid(PGPROC *proc, TransactionId latestXid);
static void MaintainLatestCompletedXid(TransactionId latestXid);
static void MaintainLatestCompletedXidRecovery(TransactionId latestXid);
-
-static inline FullTransactionId FullXidRelativeTo(FullTransactionId rel,
- TransactionId xid);
static void GlobalVisUpdateApply(ComputeXidHorizonsResult *horizons);
/*
@@ -953,10 +950,7 @@ MaintainLatestCompletedXid(TransactionId latestXid)
Assert(LWLockHeldByMe(ProcArrayLock));
if (TransactionIdPrecedes(XidFromFullTransactionId(cur_latest), latestXid))
- {
- ShmemVariableCache->latestCompletedXid =
- FullXidRelativeTo(cur_latest, latestXid);
- }
+ ShmemVariableCache->latestCompletedXid = FullTransactionIdFromXid(latestXid);
Assert(IsBootstrapProcessingMode() ||
FullTransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
@@ -969,7 +963,6 @@ static void
MaintainLatestCompletedXidRecovery(TransactionId latestXid)
{
FullTransactionId cur_latest = ShmemVariableCache->latestCompletedXid;
- FullTransactionId rel;
Assert(AmStartupProcess() || !IsUnderPostmaster);
Assert(LWLockHeldByMe(ProcArrayLock));
@@ -979,14 +972,12 @@ MaintainLatestCompletedXidRecovery(TransactionId latestXid)
* latestCompletedXid to be initialized in recovery. But in recovery it's
* safe to access nextXid without a lock for the startup process.
*/
- rel = ShmemVariableCache->nextXid;
Assert(FullTransactionIdIsValid(ShmemVariableCache->nextXid));
if (!FullTransactionIdIsValid(cur_latest) ||
TransactionIdPrecedes(XidFromFullTransactionId(cur_latest), latestXid))
{
- ShmemVariableCache->latestCompletedXid =
- FullXidRelativeTo(rel, latestXid);
+ ShmemVariableCache->latestCompletedXid = FullTransactionIdFromXid(latestXid);
}
Assert(FullTransactionIdIsNormal(ShmemVariableCache->latestCompletedXid));
@@ -1096,7 +1087,7 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
else
elog(trace_recovery(DEBUG1),
"recovery snapshot waiting for non-overflowed snapshot or "
- "until oldest active xid on standby is at least %u (now %u)",
+ "until oldest active xid on standby is at least " XID_FMT " (now " XID_FMT ")",
standbySnapshotPendingXmin,
running->oldestRunningXid);
return;
@@ -1177,7 +1168,7 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
if (i > 0 && TransactionIdEquals(xids[i - 1], xids[i]))
{
elog(DEBUG1,
- "found duplicated transaction %u for KnownAssignedXids insertion",
+ "found duplicated transaction " XID_FMT " for KnownAssignedXids insertion",
xids[i]);
continue;
}
@@ -1262,7 +1253,7 @@ ProcArrayApplyRecoveryInfo(RunningTransactions running)
else
elog(trace_recovery(DEBUG1),
"recovery snapshot waiting for non-overflowed snapshot or "
- "until oldest active xid on standby is at least %u (now %u)",
+ "until oldest active xid on standby is at least " XID_FMT " (now " XID_FMT ")",
standbySnapshotPendingXmin,
running->oldestRunningXid);
}
@@ -1455,7 +1446,7 @@ TransactionIdIsInProgress(TransactionId xid)
continue;
/* Fetch xid just once - see GetNewTransactionId */
- pxid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
+ pxid = UINT64_ACCESS_ONCE(other_xids[pgxactoff]);
if (!TransactionIdIsValid(pxid))
continue;
@@ -1487,7 +1478,7 @@ TransactionIdIsInProgress(TransactionId xid)
for (j = pxids - 1; j >= 0; j--)
{
/* Fetch xid just once - see GetNewTransactionId */
- TransactionId cxid = UINT32_ACCESS_ONCE(proc->subxids.xids[j]);
+ TransactionId cxid = UINT64_ACCESS_ONCE(proc->subxids.xids[j]);
if (TransactionIdEquals(cxid, xid))
{
@@ -1611,7 +1602,7 @@ TransactionIdIsActive(TransactionId xid)
TransactionId pxid;
/* Fetch xid just once - see GetNewTransactionId */
- pxid = UINT32_ACCESS_ONCE(other_xids[i]);
+ pxid = UINT64_ACCESS_ONCE(other_xids[i]);
if (!TransactionIdIsValid(pxid))
continue;
@@ -1762,8 +1753,8 @@ ComputeXidHorizons(ComputeXidHorizonsResult *h)
TransactionId xmin;
/* Fetch xid just once - see GetNewTransactionId */
- xid = UINT32_ACCESS_ONCE(other_xids[index]);
- xmin = UINT32_ACCESS_ONCE(proc->xmin);
+ xid = UINT64_ACCESS_ONCE(other_xids[index]);
+ xmin = UINT64_ACCESS_ONCE(proc->xmin);
/*
* Consider both the transaction's Xmin, and its Xid.
@@ -2304,7 +2295,7 @@ GetSnapshotData(Snapshot snapshot)
for (int pgxactoff = 0; pgxactoff < numProcs; pgxactoff++)
{
/* Fetch xid just once - see GetNewTransactionId */
- TransactionId xid = UINT32_ACCESS_ONCE(other_xids[pgxactoff]);
+ TransactionId xid = UINT64_ACCESS_ONCE(other_xids[pgxactoff]);
uint8 statusFlags;
Assert(allProcs[arrayP->pgprocnos[pgxactoff]].pgxactoff == pgxactoff);
@@ -2454,12 +2445,7 @@ GetSnapshotData(Snapshot snapshot)
FullTransactionId def_vis_fxid_data;
FullTransactionId oldestfxid;
- /*
- * Converting oldestXid is only safe when xid horizon cannot advance,
- * i.e. holding locks. While we don't hold the lock anymore, all the
- * necessary data has been gathered with lock held.
- */
- oldestfxid = FullXidRelativeTo(latest_completed, oldestxid);
+ oldestfxid = FullTransactionIdFromXid(oldestxid);
/* apply vacuum_defer_cleanup_age */
def_vis_xid_data =
@@ -2482,8 +2468,8 @@ GetSnapshotData(Snapshot snapshot)
def_vis_xid =
TransactionIdOlder(replication_slot_catalog_xmin, def_vis_xid);
- def_vis_fxid = FullXidRelativeTo(latest_completed, def_vis_xid);
- def_vis_fxid_data = FullXidRelativeTo(latest_completed, def_vis_xid_data);
+ def_vis_fxid = FullTransactionIdFromXid(def_vis_xid);
+ def_vis_fxid_data = FullTransactionIdFromXid(def_vis_xid_data);
/*
* Check if we can increase upper bound. As a previous
@@ -2502,7 +2488,7 @@ GetSnapshotData(Snapshot snapshot)
/* See temp_oldest_nonremovable computation in ComputeXidHorizons() */
if (TransactionIdIsNormal(myxid))
GlobalVisTempRels.definitely_needed =
- FullXidRelativeTo(latest_completed, myxid);
+ FullTransactionIdFromXid(myxid);
else
{
GlobalVisTempRels.definitely_needed = latest_completed;
@@ -2609,7 +2595,7 @@ ProcArrayInstallImportedXmin(TransactionId xmin,
/*
* Likewise, let's just make real sure its xmin does cover us.
*/
- xid = UINT32_ACCESS_ONCE(proc->xmin);
+ xid = UINT64_ACCESS_ONCE(proc->xmin);
if (!TransactionIdIsNormal(xid) ||
!TransactionIdPrecedesOrEquals(xid, xmin))
continue;
@@ -2664,7 +2650,7 @@ ProcArrayInstallRestoredXmin(TransactionId xmin, PGPROC *proc)
* can't go backwards. Also, make sure it's running in the same database,
* so that the per-database xmin cannot go backwards.
*/
- xid = UINT32_ACCESS_ONCE(proc->xmin);
+ xid = UINT64_ACCESS_ONCE(proc->xmin);
if (proc->databaseId == MyDatabaseId &&
TransactionIdIsNormal(xid) &&
TransactionIdPrecedesOrEquals(xid, xmin))
@@ -2782,7 +2768,7 @@ GetRunningTransactionData(void)
TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */
- xid = UINT32_ACCESS_ONCE(other_xids[index]);
+ xid = UINT64_ACCESS_ONCE(other_xids[index]);
/*
* We don't need to store transactions that don't have a TransactionId
@@ -2921,7 +2907,7 @@ GetOldestActiveTransactionId(void)
TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */
- xid = UINT32_ACCESS_ONCE(other_xids[index]);
+ xid = UINT64_ACCESS_ONCE(other_xids[index]);
if (!TransactionIdIsNormal(xid))
continue;
@@ -3019,7 +3005,7 @@ GetOldestSafeDecodingTransactionId(bool catalogOnly)
TransactionId xid;
/* Fetch xid just once - see GetNewTransactionId */
- xid = UINT32_ACCESS_ONCE(other_xids[index]);
+ xid = UINT64_ACCESS_ONCE(other_xids[index]);
if (!TransactionIdIsNormal(xid))
continue;
@@ -3301,7 +3287,7 @@ GetCurrentVirtualXIDs(TransactionId limitXmin, bool excludeXmin0,
if (allDbs || proc->databaseId == MyDatabaseId)
{
/* Fetch xmin just once - might change on us */
- TransactionId pxmin = UINT32_ACCESS_ONCE(proc->xmin);
+ TransactionId pxmin = UINT64_ACCESS_ONCE(proc->xmin);
if (excludeXmin0 && !TransactionIdIsValid(pxmin))
continue;
@@ -3396,7 +3382,7 @@ GetConflictingVirtualXIDs(TransactionId limitXmin, Oid dbOid)
proc->databaseId == dbOid)
{
/* Fetch xmin just once - can't change on us, but good coding */
- TransactionId pxmin = UINT32_ACCESS_ONCE(proc->xmin);
+ TransactionId pxmin = UINT64_ACCESS_ONCE(proc->xmin);
/*
* We ignore an invalid pxmin because this means that backend has
@@ -3978,7 +3964,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
* debug warning.
*/
if (j < 0 && !MyProc->subxidStatus.overflowed)
- elog(WARNING, "did not find subXID %u in MyProc", anxid);
+ elog(WARNING, "did not find subXID " XID_FMT " in MyProc", anxid);
}
for (j = MyProc->subxidStatus.count - 1; j >= 0; j--)
@@ -3994,7 +3980,7 @@ XidCacheRemoveRunningXids(TransactionId xid,
}
/* Ordinarily we should have found it, unless the cache has overflowed */
if (j < 0 && !MyProc->subxidStatus.overflowed)
- elog(WARNING, "did not find subXID %u in MyProc", xid);
+ elog(WARNING, "did not find subXID " XID_FMT " in MyProc", xid);
/* Also advance global latestCompletedXid while holding the lock */
MaintainLatestCompletedXid(latestXid);
@@ -4101,17 +4087,13 @@ static void
GlobalVisUpdateApply(ComputeXidHorizonsResult *horizons)
{
GlobalVisSharedRels.maybe_needed =
- FullXidRelativeTo(horizons->latest_completed,
- horizons->shared_oldest_nonremovable);
+ FullTransactionIdFromXid(horizons->shared_oldest_nonremovable);
GlobalVisCatalogRels.maybe_needed =
- FullXidRelativeTo(horizons->latest_completed,
- horizons->catalog_oldest_nonremovable);
+ FullTransactionIdFromXid(horizons->catalog_oldest_nonremovable);
GlobalVisDataRels.maybe_needed =
- FullXidRelativeTo(horizons->latest_completed,
- horizons->data_oldest_nonremovable);
+ FullTransactionIdFromXid(horizons->data_oldest_nonremovable);
GlobalVisTempRels.maybe_needed =
- FullXidRelativeTo(horizons->latest_completed,
- horizons->temp_oldest_nonremovable);
+ FullTransactionIdFromXid(horizons->temp_oldest_nonremovable);
/*
* In longer running transactions it's possible that transactions we
@@ -4200,15 +4182,7 @@ GlobalVisTestIsRemovableXid(GlobalVisState *state, TransactionId xid)
{
FullTransactionId fxid;
- /*
- * Convert 32 bit argument to FullTransactionId. We can do so safely
- * because we know the xid has to, at the very least, be between
- * [oldestXid, nextFullXid), i.e. within 2 billion of xid. To avoid taking
- * a lock to determine either, we can just compare with
- * state->definitely_needed, which was based on those value at the time
- * the current snapshot was built.
- */
- fxid = FullXidRelativeTo(state->definitely_needed, xid);
+ fxid = FullTransactionIdFromXid(xid);
return GlobalVisTestIsRemovableFullXid(state, fxid);
}
@@ -4271,32 +4245,6 @@ GlobalVisCheckRemovableXid(Relation rel, TransactionId xid)
return GlobalVisTestIsRemovableXid(state, xid);
}
-/*
- * Convert a 32 bit transaction id into 64 bit transaction id, by assuming it
- * is within MaxTransactionId / 2 of XidFromFullTransactionId(rel).
- *
- * Be very careful about when to use this function. It can only safely be used
- * when there is a guarantee that xid is within MaxTransactionId / 2 xids of
- * rel. That e.g. can be guaranteed if the caller assures a snapshot is
- * held by the backend and xid is from a table (where vacuum/freezing ensures
- * the xid has to be within that range), or if xid is from the procarray and
- * prevents xid wraparound that way.
- */
-static inline FullTransactionId
-FullXidRelativeTo(FullTransactionId rel, TransactionId xid)
-{
- TransactionId rel_xid = XidFromFullTransactionId(rel);
-
- Assert(TransactionIdIsValid(xid));
- Assert(TransactionIdIsValid(rel_xid));
-
- /* not guaranteed to find issues, but likely to catch mistakes */
- AssertTransactionIdInAllowableRange(xid);
-
- return FullTransactionIdFromU64(U64FromFullTransactionId(rel)
- + (int32) (xid - rel_xid));
-}
-
/* ----------------------------------------------
* KnownAssignedTransactionIds sub-module
@@ -4371,7 +4319,7 @@ RecordKnownAssignedTransactionIds(TransactionId xid)
Assert(TransactionIdIsValid(xid));
Assert(TransactionIdIsValid(latestObservedXid));
- elog(trace_recovery(DEBUG4), "record known xact %u latestObservedXid %u",
+ elog(trace_recovery(DEBUG4), "record known xact " XID_FMT " latestObservedXid " XID_FMT,
xid, latestObservedXid);
/*
@@ -4882,7 +4830,7 @@ KnownAssignedXidsRemove(TransactionId xid)
{
Assert(TransactionIdIsValid(xid));
- elog(trace_recovery(DEBUG4), "remove KnownAssignedXid %u", xid);
+ elog(trace_recovery(DEBUG4), "remove KnownAssignedXid " XID_FMT, xid);
/*
* Note: we cannot consider it an error to remove an XID that's not
@@ -4942,7 +4890,7 @@ KnownAssignedXidsRemovePreceding(TransactionId removeXid)
return;
}
- elog(trace_recovery(DEBUG4), "prune KnownAssignedXids to %u", removeXid);
+ elog(trace_recovery(DEBUG4), "prune KnownAssignedXids to " XID_FMT, removeXid);
/*
* Mark entries invalid starting at the tail. Since array is sorted, we
@@ -5130,7 +5078,7 @@ KnownAssignedXidsDisplay(int trace_level)
if (KnownAssignedXidsValid[i])
{
nxids++;
- appendStringInfo(&buf, "[%d]=%u ", i, KnownAssignedXids[i]);
+ appendStringInfo(&buf, "[%d]=" XID_FMT " ", i, KnownAssignedXids[i]);
}
}
diff --git a/src/backend/storage/ipc/standby.c b/src/backend/storage/ipc/standby.c
index 87ac0f74b27..123f0e2c2c5 100644
--- a/src/backend/storage/ipc/standby.c
+++ b/src/backend/storage/ipc/standby.c
@@ -36,7 +36,7 @@
#include "utils/timestamp.h"
/* User-settable GUC parameters */
-int vacuum_defer_cleanup_age;
+int64 vacuum_defer_cleanup_age;
int max_standby_archive_delay = 30 * 1000;
int max_standby_streaming_delay = 30 * 1000;
bool log_recovery_conflict_waits = false;
@@ -994,13 +994,13 @@ StandbyReleaseLockList(List *locks)
LOCKTAG locktag;
elog(trace_recovery(DEBUG4),
- "releasing recovery lock: xid %u db %u rel %u",
+ "releasing recovery lock: xid " XID_FMT " db %u rel %u",
lock->xid, lock->dbOid, lock->relOid);
SET_LOCKTAG_RELATION(locktag, lock->dbOid, lock->relOid);
if (!LockRelease(&locktag, AccessExclusiveLock, true))
{
elog(LOG,
- "RecoveryLockLists contains entry for lock no longer recorded by lock manager: xid %u database %u relation %u",
+ "RecoveryLockLists contains entry for lock no longer recorded by lock manager: xid " XID_FMT " database %u relation %u",
lock->xid, lock->dbOid, lock->relOid);
Assert(false);
}
@@ -1305,7 +1305,7 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
if (CurrRunningXacts->subxid_overflow)
elog(trace_recovery(DEBUG2),
- "snapshot of %u running transactions overflowed (lsn %X/%X oldest xid %u latest complete %u next xid %u)",
+ "snapshot of %u running transactions overflowed (lsn %X/%X oldest xid " XID_FMT " latest complete " XID_FMT " next xid " XID_FMT ")",
CurrRunningXacts->xcnt,
LSN_FORMAT_ARGS(recptr),
CurrRunningXacts->oldestRunningXid,
@@ -1313,7 +1313,7 @@ LogCurrentRunningXacts(RunningTransactions CurrRunningXacts)
CurrRunningXacts->nextXid);
else
elog(trace_recovery(DEBUG2),
- "snapshot of %u+%u running transaction ids (lsn %X/%X oldest xid %u latest complete %u next xid %u)",
+ "snapshot of %u+%u running transaction ids (lsn %X/%X oldest xid " XID_FMT " latest complete " XID_FMT " next xid " XID_FMT ")",
CurrRunningXacts->xcnt, CurrRunningXacts->subxcnt,
LSN_FORMAT_ARGS(recptr),
CurrRunningXacts->oldestRunningXid,
diff --git a/src/backend/storage/lmgr/lmgr.c b/src/backend/storage/lmgr/lmgr.c
index 5ae52dd14db..5246dd3660a 100644
--- a/src/backend/storage/lmgr/lmgr.c
+++ b/src/backend/storage/lmgr/lmgr.c
@@ -1135,10 +1135,17 @@ DescribeLockTag(StringInfo buf, const LOCKTAG *tag)
tag->locktag_field1);
break;
case LOCKTAG_TRANSACTION:
- appendStringInfo(buf,
- _("transaction %u"),
- tag->locktag_field1);
+ {
+ char xid_str[32];
+
+ /* make translatable string */
+ snprintf(xid_str, sizeof(xid_str), XID_FMT,
+ (TransactionId) tag->locktag_field1 |
+ ((TransactionId) tag->locktag_field2 << 32));
+
+ appendStringInfo(buf, _("transaction %s"), xid_str);
break;
+ }
case LOCKTAG_VIRTUALTRANSACTION:
appendStringInfo(buf,
_("virtual transaction %d/%u"),
diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index 25e7e4e37bf..b907844e1a2 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -438,7 +438,7 @@ static void SetPossibleUnsafeConflict(SERIALIZABLEXACT *roXact, SERIALIZABLEXACT
static void ReleaseRWConflict(RWConflict conflict);
static void FlagSxactUnsafe(SERIALIZABLEXACT *sxact);
-static bool SerialPagePrecedesLogically(int page1, int page2);
+static bool SerialPagePrecedesLogically(int64 page1, int64 page2);
static void SerialInit(void);
static void SerialAdd(TransactionId xid, SerCommitSeqNo minConflictCommitSeqNo);
static SerCommitSeqNo SerialGetMinConflictCommitSeqNo(TransactionId xid);
@@ -788,27 +788,21 @@ FlagSxactUnsafe(SERIALIZABLEXACT *sxact)
* Analogous to CLOGPagePrecedes().
*/
static bool
-SerialPagePrecedesLogically(int page1, int page2)
+SerialPagePrecedesLogically(int64 page1, int64 page2)
{
- TransactionId xid1;
- TransactionId xid2;
-
- xid1 = ((TransactionId) page1) * SERIAL_ENTRIESPERPAGE;
- xid1 += FirstNormalTransactionId + 1;
- xid2 = ((TransactionId) page2) * SERIAL_ENTRIESPERPAGE;
- xid2 += FirstNormalTransactionId + 1;
-
- return (TransactionIdPrecedes(xid1, xid2) &&
- TransactionIdPrecedes(xid1, xid2 + SERIAL_ENTRIESPERPAGE - 1));
+ return page1 < page2;
}
#ifdef USE_ASSERT_CHECKING
+#define SerialPagePrecedesLogically(p1, p2) ((p1) < (p2))
+
static void
SerialPagePrecedesLogicallyUnitTests(void)
{
+#if 0 /* XXX remove unit tests */
int per_page = SERIAL_ENTRIESPERPAGE,
offset = per_page / 2;
- int newestPage,
+ int64 newestPage,
oldestPage,
headPage,
targetPage;
@@ -856,9 +850,11 @@ SerialPagePrecedesLogicallyUnitTests(void)
#if 0
Assert(SerialPagePrecedesLogically(headPage, targetPage));
#endif
+#endif
}
#endif
+
/*
* Initialize for the tracking of old serializable committed xids.
*/
@@ -4168,7 +4164,7 @@ CheckForSerializableConflictOut(Relation relation, TransactionId xid, Snapshot s
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
- errdetail_internal("Reason code: Canceled on conflict out to old pivot %u.", xid),
+ errdetail_internal("Reason code: Canceled on conflict out to old pivot " XID_FMT ".", xid),
errhint("The transaction might succeed if retried.")));
if (SxactHasSummaryConflictIn(MySerializableXact)
@@ -4176,7 +4172,7 @@ CheckForSerializableConflictOut(Relation relation, TransactionId xid, Snapshot s
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
- errdetail_internal("Reason code: Canceled on identification as a pivot, with conflict out to old committed transaction %u.", xid),
+ errdetail_internal("Reason code: Canceled on identification as a pivot, with conflict out to old committed transaction " XID_FMT ".", xid),
errhint("The transaction might succeed if retried.")));
MySerializableXact->flags |= SXACT_FLAG_SUMMARY_CONFLICT_OUT;
@@ -4806,7 +4802,7 @@ OnConflict_CheckForSerializationFailure(const SERIALIZABLEXACT *reader,
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to read/write dependencies among transactions"),
- errdetail_internal("Reason code: Canceled on conflict out to pivot %u, during read.", writer->topXid),
+ errdetail_internal("Reason code: Canceled on conflict out to pivot " XID_FMT ", during read.", writer->topXid),
errhint("The transaction might succeed if retried.")));
}
writer->flags |= SXACT_FLAG_DOOMED;
diff --git a/src/backend/storage/page/bufpage.c b/src/backend/storage/page/bufpage.c
index 147ba4d9232..0edfbceb423 100644
--- a/src/backend/storage/page/bufpage.c
+++ b/src/backend/storage/page/bufpage.c
@@ -21,11 +21,26 @@
#include "storage/checksum.h"
#include "utils/memdebug.h"
#include "utils/memutils.h"
+#include "utils/snapmgr.h"
/* GUC variable */
bool ignore_checksum_failure = false;
+/*
+ * HeapPageSpecialData used when pd_special == BLCKSZ. This is special format
+ * used when page with 32-bit xids doesn't fit HeapPageSpecialData. Then
+ * all xmin's are frozen (can do this for all live tuples after pg_upgrade),
+ * while 64-bit xmax is stored in both t_heap.t_xmin and t_heap.t_xmax.
+ * This is so-called "double xmax" format.
+ */
+static HeapPageSpecialData doubleXmaxSpecialData =
+{
+ .pd_xid_base = MaxTransactionId,
+ .pd_multi_base = MaxTransactionId
+};
+HeapPageSpecial doubleXmaxSpecial = &doubleXmaxSpecialData;
+
/* ----------------------------------------------------------------
* Page support functions
@@ -432,15 +447,131 @@ PageRestoreTempPage(Page tempPage, Page oldPage)
}
/*
- * Tuple defrag support for PageRepairFragmentation and PageIndexMultiDelete
+ * Get minimum and maximum values of xid and multixact on "double xmax" page.
*/
-typedef struct itemIdCompactData
+static void
+heap_page_double_xmax_get_min_max(Page page,
+ TransactionId *xid_min,
+ TransactionId *xid_max,
+ MultiXactId *multi_min,
+ MultiXactId *multi_max)
{
- uint16 offsetindex; /* linp array index */
- int16 itemoff; /* page offset of item data */
- uint16 alignedlen; /* MAXALIGN(item data len) */
-} itemIdCompactData;
-typedef itemIdCompactData *itemIdCompact;
+ bool xid_found = false,
+ multi_found = false;
+ OffsetNumber offnum,
+ maxoff;
+
+ maxoff = PageGetMaxOffsetNumber(page);
+
+ for (offnum = FirstOffsetNumber;
+ offnum <= maxoff;
+ offnum = OffsetNumberNext(offnum))
+ {
+ ItemId itemid;
+ HeapTupleHeader htup;
+ TransactionId xmax;
+
+ itemid = PageGetItemId(page, offnum);
+
+ if (!ItemIdIsNormal(itemid))
+ continue;
+
+ htup = (HeapTupleHeader) PageGetItem(page, itemid);
+
+ xmax = HeapTupleHeaderGetDoubleXmax(htup);
+
+ if (!TransactionIdIsNormal(xmax))
+ continue;
+
+ if (!(htup->t_infomask & HEAP_XMAX_IS_MULTI))
+ {
+ if (!xid_found)
+ {
+ *xid_min = *xid_max = xmax;
+ xid_found = true;
+ }
+ else
+ {
+ *xid_min = Min(*xid_min, xmax);
+ *xid_max = Max(*xid_max, xmax);
+ }
+ }
+ else
+ {
+ if (!multi_found)
+ {
+ *multi_min = *multi_max = xmax;
+ multi_found = true;
+ }
+ else
+ {
+ *multi_min = Min(*multi_min, xmax);
+ *multi_max = Max(*multi_max, xmax);
+ }
+ }
+ }
+}
+
+/*
+ * Add special area to heap page, so convert from "double xmax" to normal
+ * format.
+ */
+static void
+heap_page_add_special_area(itemIdCompact itemidbase, int nitems, Page page,
+ TransactionId xid_base, MultiXactId multi_base)
+{
+ char newPage[BLCKSZ];
+ PageHeader phdr = (PageHeader) page;
+ PageHeader new_phdr = (PageHeader) newPage;
+ HeapPageSpecial special;
+ Offset upper;
+ int i;
+
+ memcpy(newPage, page, phdr->pd_lower);
+
+ /* Add special area */
+ new_phdr->pd_special = PageGetPageSize(newPage) - sizeof(HeapPageSpecialData);
+ special = (HeapPageSpecial) ((Pointer) (newPage) + new_phdr->pd_special);
+ special->pd_xid_base = xid_base;
+ special->pd_multi_base = multi_base;
+
+ /* sort itemIdSortData array into decreasing itemoff order */
+ qsort((char *) itemidbase, nitems, sizeof(itemIdCompactData),
+ itemoffcompare);
+
+ upper = new_phdr->pd_special;
+ for (i = 0; i < nitems; i++)
+ {
+ itemIdCompact itemidptr = &itemidbase[i];
+ ItemId lp;
+ HeapTupleHeader old_htup;
+ HeapTupleHeader new_htup;
+ TransactionId xmax;
+
+ lp = PageGetItemId(page, itemidptr->offsetindex + 1);
+ old_htup = (HeapTupleHeader) PageGetItem(page, lp);
+ upper -= itemidptr->alignedlen;
+ memcpy((Pointer) newPage + upper,
+ (Pointer) page + itemidptr->itemoff,
+ itemidptr->alignedlen);
+ lp = PageGetItemId(newPage, itemidptr->offsetindex + 1);
+ lp->lp_off = upper;
+ new_htup = (HeapTupleHeader) PageGetItem(newPage, lp);
+
+ /* Convert xmax value */
+ new_htup->t_choice.t_heap.t_xmin = FrozenTransactionId;
+ xmax = HeapTupleHeaderGetDoubleXmax(old_htup);
+ if (!(new_htup->t_infomask & HEAP_XMAX_IS_MULTI))
+ new_htup->t_choice.t_heap.t_xmax = NormalTransactionIdToShort(xid_base, xmax);
+ else
+ new_htup->t_choice.t_heap.t_xmax = NormalTransactionIdToShort(multi_base, xmax);
+ }
+
+ new_phdr->pd_upper = upper;
+
+ memcpy(page, newPage, PageGetPageSize(newPage));
+ elog(DEBUG2, "convert heap page from double xmax to normal format");
+}
/*
* After removing or marking some line pointers unused, move the tuples to
@@ -471,7 +602,8 @@ typedef itemIdCompactData *itemIdCompact;
* Callers must ensure that nitems is > 0
*/
static void
-compactify_tuples(itemIdCompact itemidbase, int nitems, Page page, bool presorted)
+compactify_tuples(itemIdCompact itemidbase, int nitems, Page page,
+ bool presorted, bool addspecial)
{
PageHeader phdr = (PageHeader) page;
Offset upper;
@@ -483,9 +615,33 @@ compactify_tuples(itemIdCompact itemidbase, int nitems, Page page, bool presorte
/* Code within will not work correctly if nitems == 0 */
Assert(nitems > 0);
- if (presorted)
+ /* Add special area to the heap page if possible */
+ if (addspecial)
{
+ TransactionId xid_min = FirstNormalTransactionId,
+ xid_max = FirstNormalTransactionId;
+ MultiXactId multi_min = FirstNormalTransactionId,
+ multi_max = FirstNormalTransactionId;
+
+ Assert(phdr->pd_special == PageGetPageSize(page));
+ heap_page_double_xmax_get_min_max(page, &xid_min, &xid_max,
+ &multi_min, &multi_max);
+
+ if (xid_max - xid_min < (TransactionId) (MaxShortTransactionId - FirstNormalTransactionId) &&
+ multi_max - multi_min < (TransactionId) (MaxShortTransactionId - FirstNormalTransactionId))
+ {
+ Assert(xid_min >= FirstNormalTransactionId);
+ Assert(multi_min >= FirstNormalTransactionId);
+ heap_page_add_special_area(itemidbase, nitems, page,
+ xid_min - FirstNormalTransactionId,
+ multi_min - FirstNormalTransactionId);
+ return;
+ }
+ }
+
+ if (presorted)
+ {
#ifdef USE_ASSERT_CHECKING
{
/*
@@ -783,11 +939,21 @@ PageRepairFragmentation(Page page)
nstorage = itemidptr - itemidbase;
if (nstorage == 0)
{
+ if (pd_special == PageGetPageSize(page))
+ {
+ pd_special = PageGetPageSize(page) - sizeof(HeapPageSpecialData);
+ ((PageHeader) page)->pd_special = pd_special;
+ HeapPageGetSpecial(page)->pd_xid_base = 0;
+ HeapPageGetSpecial(page)->pd_multi_base = 0;
+ }
+
/* Page is completely empty, so just reset it quickly */
((PageHeader) page)->pd_upper = pd_special;
}
else
{
+ bool addspecial = false;
+
/* Need to compact the page the hard way */
if (totallen > (Size) (pd_special - pd_lower))
ereport(ERROR,
@@ -795,7 +961,15 @@ PageRepairFragmentation(Page page)
errmsg("corrupted item lengths: total %u, available space %u",
(unsigned int) totallen, pd_special - pd_lower)));
- compactify_tuples(itemidbase, nstorage, page, presorted);
+ /*
+ * Try to add special area to the heap page if it has enough of free
+ * space.
+ */
+ if (pd_special == PageGetPageSize(page) &&
+ (Size) (pd_special - pd_lower) - totallen >= sizeof(HeapPageSpecialData))
+ addspecial = true;
+
+ compactify_tuples(itemidbase, nstorage, page, presorted, addspecial);
}
/* Set hint bit for PageAddItemExtended */
@@ -985,6 +1159,9 @@ PageGetHeapFreeSpace(Page page)
{
Size space;
+ if (HeapPageIsDoubleXmax(page))
+ return 0;
+
space = PageGetFreeSpace(page);
if (space > 0)
{
@@ -1268,7 +1445,7 @@ PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
/* and compactify the tuple data */
if (nused > 0)
- compactify_tuples(itemidbase, nused, page, presorted);
+ compactify_tuples(itemidbase, nused, page, presorted, false);
else
phdr->pd_upper = pd_special;
}
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
index 0cc7a6d8ad0..04ca6ff16e7 100644
--- a/src/backend/utils/adt/enum.c
+++ b/src/backend/utils/adt/enum.c
@@ -76,7 +76,7 @@ check_safe_enum_use(HeapTuple enumval_tup)
* Usually, a row would get hinted as committed when it's read or loaded
* into syscache; but just in case not, let's check the xmin directly.
*/
- xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
+ xmin = HeapTupleGetXmin(enumval_tup);
if (!TransactionIdIsInProgress(xmin) &&
TransactionIdDidCommit(xmin))
return;
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 0273f883d4f..b6a2bbb7582 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -3340,6 +3340,7 @@ populate_record(TupleDesc tupdesc,
tuple.t_len = HeapTupleHeaderGetDatumLength(defaultval);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple);
tuple.t_data = defaultval;
/* Break down the tuple into fields */
@@ -3776,6 +3777,7 @@ populate_recordset_record(PopulateRecordsetState *state, JsObject *obj)
tuple.t_len = HeapTupleHeaderGetDatumLength(tuphead);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple);
tuple.t_data = tuphead;
tuplestore_puttuple(state->tuple_store, &tuple);
diff --git a/src/backend/utils/adt/lockfuncs.c b/src/backend/utils/adt/lockfuncs.c
index 023a004ac89..95aa922e8cc 100644
--- a/src/backend/utils/adt/lockfuncs.c
+++ b/src/backend/utils/adt/lockfuncs.c
@@ -77,9 +77,9 @@ VXIDGetDatum(BackendId bid, LocalTransactionId lxid)
* The representation is "<bid>/<lxid>", decimal and unsigned decimal
* respectively. Note that elog.c also knows how to format a vxid.
*/
- char vxidstr[32];
+ char vxidstr[64];
- snprintf(vxidstr, sizeof(vxidstr), "%d/%u", bid, lxid);
+ snprintf(vxidstr, sizeof(vxidstr), "%d/" XID_FMT, bid, lxid);
return CStringGetTextDatum(vxidstr);
}
@@ -292,7 +292,9 @@ pg_lock_status(PG_FUNCTION_ARGS)
break;
case LOCKTAG_TRANSACTION:
values[6] =
- TransactionIdGetDatum(instance->locktag.locktag_field1);
+ TransactionIdGetDatum(
+ (TransactionId)instance->locktag.locktag_field1 |
+ ((TransactionId)instance->locktag.locktag_field2 << 32));
nulls[1] = true;
nulls[2] = true;
nulls[3] = true;
@@ -304,7 +306,8 @@ pg_lock_status(PG_FUNCTION_ARGS)
break;
case LOCKTAG_VIRTUALTRANSACTION:
values[5] = VXIDGetDatum(instance->locktag.locktag_field1,
- instance->locktag.locktag_field2);
+ (TransactionId)instance->locktag.locktag_field2 |
+ ((TransactionId)instance->locktag.locktag_field3 << 32));
nulls[1] = true;
nulls[2] = true;
nulls[3] = true;
diff --git a/src/backend/utils/adt/numutils.c b/src/backend/utils/adt/numutils.c
index 898a9e3f9a1..5d0443fbe8a 100644
--- a/src/backend/utils/adt/numutils.c
+++ b/src/backend/utils/adt/numutils.c
@@ -606,3 +606,25 @@ pg_ultostr(char *str, uint32 value)
return str + len;
}
+
+/*
+ * pg_strtouint64
+ * Converts 'str' into an unsigned 64-bit integer.
+ *
+ * This has the identical API to strtoul(3), except that it will handle
+ * 64-bit ints even where "long" is narrower than that.
+ *
+ * For the moment it seems sufficient to assume that the platform has
+ * such a function somewhere; let's not roll our own.
+ */
+uint64
+pg_strtouint64(const char *str, char **endptr, int base)
+{
+#ifdef _MSC_VER /* MSVC only */
+ return _strtoui64(str, endptr, base);
+#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
+ return strtoull(str, endptr, base);
+#else
+ return strtoul(str, endptr, base);
+#endif
+}
diff --git a/src/backend/utils/adt/pgstatfuncs.c b/src/backend/utils/adt/pgstatfuncs.c
index 15cb17ace4f..983c99b7786 100644
--- a/src/backend/utils/adt/pgstatfuncs.c
+++ b/src/backend/utils/adt/pgstatfuncs.c
@@ -15,6 +15,7 @@
#include "postgres.h"
#include "access/htup_details.h"
+#include "access/xact.h"
#include "access/xlog.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_type.h"
diff --git a/src/backend/utils/adt/rowtypes.c b/src/backend/utils/adt/rowtypes.c
index db843a0fbf0..5e08d02d645 100644
--- a/src/backend/utils/adt/rowtypes.c
+++ b/src/backend/utils/adt/rowtypes.c
@@ -327,6 +327,7 @@ record_out(PG_FUNCTION_ARGS)
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple);
tuple.t_data = rec;
/*
@@ -694,6 +695,7 @@ record_send(PG_FUNCTION_ARGS)
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple);
tuple.t_data = rec;
/*
@@ -844,10 +846,12 @@ record_cmp(FunctionCallInfo fcinfo)
tuple1.t_len = HeapTupleHeaderGetDatumLength(record1);
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple1);
tuple1.t_data = record1;
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple2);
tuple2.t_data = record2;
/*
@@ -1089,10 +1093,12 @@ record_eq(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
+ HeapTupleSetZeroBase(&tuple1);
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
+ HeapTupleSetZeroBase(&tuple2);
/*
* We arrange to look up the needed comparison info just once per series
@@ -1351,10 +1357,12 @@ record_image_cmp(FunctionCallInfo fcinfo)
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
+ HeapTupleSetZeroBase(&tuple1);
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
+ HeapTupleSetZeroBase(&tuple2);
/*
* We arrange to look up the needed comparison info just once per series
@@ -1597,10 +1605,12 @@ record_image_eq(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple1.t_self));
tuple1.t_tableOid = InvalidOid;
tuple1.t_data = record1;
+ HeapTupleSetZeroBase(&tuple1);
tuple2.t_len = HeapTupleHeaderGetDatumLength(record2);
ItemPointerSetInvalid(&(tuple2.t_self));
tuple2.t_tableOid = InvalidOid;
tuple2.t_data = record2;
+ HeapTupleSetZeroBase(&tuple2);
/*
* We arrange to look up the needed comparison info just once per series
@@ -1800,6 +1810,7 @@ hash_record(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = record;
+ HeapTupleSetZeroBase(&tuple);
/*
* We arrange to look up the needed hashing info just once per series of
@@ -1921,6 +1932,7 @@ hash_record_extended(PG_FUNCTION_ARGS)
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
tuple.t_data = record;
+ HeapTupleSetZeroBase(&tuple);
/*
* We arrange to look up the needed hashing info just once per series of
diff --git a/src/backend/utils/adt/xid.c b/src/backend/utils/adt/xid.c
index e6633e9cffe..c357ef2d3e8 100644
--- a/src/backend/utils/adt/xid.c
+++ b/src/backend/utils/adt/xid.c
@@ -32,16 +32,16 @@ xidin(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
- PG_RETURN_TRANSACTIONID((TransactionId) strtoul(str, NULL, 0));
+ PG_RETURN_TRANSACTIONID((TransactionId) pg_strtouint64(str, NULL, 0));
}
Datum
xidout(PG_FUNCTION_ARGS)
{
TransactionId transactionId = PG_GETARG_TRANSACTIONID(0);
- char *result = (char *) palloc(16);
+ char *result = (char *) palloc(32);
- snprintf(result, 16, "%lu", (unsigned long) transactionId);
+ snprintf(result, 32, XID_FMT, transactionId);
PG_RETURN_CSTRING(result);
}
@@ -52,8 +52,12 @@ Datum
xidrecv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ uint32 lo, hi;
+
+ lo = (uint32) pq_getmsgint(buf, sizeof(TransactionId));
+ hi = (uint32) pq_getmsgint(buf, sizeof(TransactionId));
- PG_RETURN_TRANSACTIONID((TransactionId) pq_getmsgint(buf, sizeof(TransactionId)));
+ PG_RETURN_TRANSACTIONID((uint64) lo + ((uint64) hi << 32));
}
/*
@@ -64,9 +68,14 @@ xidsend(PG_FUNCTION_ARGS)
{
TransactionId arg1 = PG_GETARG_TRANSACTIONID(0);
StringInfoData buf;
+ uint32 lo, hi;
+
+ lo = (uint32) (arg1 & 0xFFFFFFFF);
+ hi = (uint32) (arg1 >> 32);
pq_begintypsend(&buf);
- pq_sendint32(&buf, arg1);
+ pq_sendint(&buf, lo, sizeof(lo));
+ pq_sendint(&buf, hi, sizeof(hi));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
@@ -105,9 +114,9 @@ xid_age(PG_FUNCTION_ARGS)
/* Permanent XIDs are always infinitely old */
if (!TransactionIdIsNormal(xid))
- PG_RETURN_INT32(INT_MAX);
+ PG_RETURN_INT64(PG_INT8_MAX);
- PG_RETURN_INT32((int32) (now - xid));
+ PG_RETURN_INT64((int64) (now - xid));
}
/*
@@ -120,9 +129,9 @@ mxid_age(PG_FUNCTION_ARGS)
MultiXactId now = ReadNextMultiXactId();
if (!MultiXactIdIsValid(xid))
- PG_RETURN_INT32(INT_MAX);
+ PG_RETURN_INT64(PG_INT8_MAX);
- PG_RETURN_INT32((int32) (now - xid));
+ PG_RETURN_INT64((int64) (now - xid));
}
/*
diff --git a/src/backend/utils/adt/xid8funcs.c b/src/backend/utils/adt/xid8funcs.c
index 60e57876de6..a508d74d2f8 100644
--- a/src/backend/utils/adt/xid8funcs.c
+++ b/src/backend/utils/adt/xid8funcs.c
@@ -79,8 +79,7 @@ typedef struct
* It is an ERROR if the xid is in the future. Otherwise, returns true if
* the transaction is still new enough that we can determine whether it
* committed and false otherwise. If *extracted_xid is not NULL, it is set
- * to the low 32 bits of the transaction ID (i.e. the actual XID, without the
- * epoch).
+ * to the actual transaction ID.
*
* The caller must hold XactTruncationLock since it's dealing with arbitrary
* XIDs, and must continue to hold it until it's done with any clog lookups
@@ -89,15 +88,10 @@ typedef struct
static bool
TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
- uint32 xid_epoch = EpochFromFullTransactionId(fxid);
TransactionId xid = XidFromFullTransactionId(fxid);
- uint32 now_epoch;
- TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
now_fullxid = ReadNextFullTransactionId();
- now_epoch_next_xid = XidFromFullTransactionId(now_fullxid);
- now_epoch = EpochFromFullTransactionId(now_fullxid);
if (extracted_xid != NULL)
*extracted_xid = xid;
@@ -127,48 +121,15 @@ TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
Assert(LWLockHeldByMe(XactTruncationLock));
/*
- * If the transaction ID has wrapped around, it's definitely too old to
- * determine the commit status. Otherwise, we can compare it to
- * ShmemVariableCache->oldestClogXid to determine whether the relevant
- * CLOG entry is guaranteed to still exist.
+ * We compare xid to ShmemVariableCache->oldestClogXid to determine whether
+ * the relevant CLOG entry is guaranteed to still exist.
*/
- if (xid_epoch + 1 < now_epoch
- || (xid_epoch + 1 == now_epoch && xid < now_epoch_next_xid)
- || TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
+ if (TransactionIdPrecedes(xid, ShmemVariableCache->oldestClogXid))
return false;
return true;
}
-/*
- * 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 FullTransactionId
-widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
-{
- TransactionId next_xid = XidFromFullTransactionId(next_fxid);
- uint32 epoch = EpochFromFullTransactionId(next_fxid);
-
- /* Special transaction ID. */
- if (!TransactionIdIsNormal(xid))
- return FullTransactionIdFromEpochAndXid(0, 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--;
-
- return FullTransactionIdFromEpochAndXid(epoch, xid);
-}
-
/*
* txid comparator for qsort/bsearch
*/
@@ -397,7 +358,6 @@ pg_current_snapshot(PG_FUNCTION_ARGS)
uint32 nxip,
i;
Snapshot cur;
- FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
@@ -415,11 +375,11 @@ pg_current_snapshot(PG_FUNCTION_ARGS)
snap = palloc(PG_SNAPSHOT_SIZE(nxip));
/* fill */
- snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
- snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
+ snap->xmin = FullTransactionIdFromXid(cur->xmin);
+ snap->xmax = FullTransactionIdFromXid(cur->xmax);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
- snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
+ snap->xip[i] = FullTransactionIdFromXid(cur->xip[i]);
/*
* We want them guaranteed to be in ascending order. This also removes
@@ -655,8 +615,7 @@ pg_snapshot_xip(PG_FUNCTION_ARGS)
* Report the status of a recent transaction ID, or null for wrapped,
* truncated away or otherwise too old XIDs.
*
- * The passed epoch-qualified xid is treated as a normal xid, not a
- * multixact id.
+ * The passed xid is treated as a normal xid, not a multixact id.
*
* If it points to a committed subxact the result is the subxact status even
* though the parent xact may still be in progress or may have aborted.
diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c
index eb830880893..5ad5cc707e2 100644
--- a/src/backend/utils/cache/catcache.c
+++ b/src/backend/utils/cache/catcache.c
@@ -1845,6 +1845,7 @@ CatalogCacheCreateEntry(CatCache *cache, HeapTuple ntp, Datum *arguments,
memcpy((char *) ct->tuple.t_data,
(const char *) dtp->t_data,
dtp->t_len);
+ HeapTupleCopyBase(&ct->tuple, dtp);
MemoryContextSwitchTo(oldcxt);
if (dtp != ntp)
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 2e760e8a3bd..0f4d37ab942 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -2284,8 +2284,9 @@ RelationReloadIndexInfo(Relation relation)
relation->rd_index->indislive = index->indislive;
/* Copy xmin too, as that is needed to make sense of indcheckxmin */
- HeapTupleHeaderSetXmin(relation->rd_indextuple->t_data,
- HeapTupleHeaderGetXmin(tuple->t_data));
+ relation->rd_indextuple->t_xid_base = tuple->t_xid_base;
+ HeapTupleSetXmin(relation->rd_indextuple,
+ HeapTupleGetXmin(tuple));
ReleaseSysCache(tuple);
}
diff --git a/src/backend/utils/error/csvlog.c b/src/backend/utils/error/csvlog.c
index 89f78b447d9..8bf5213d7e1 100644
--- a/src/backend/utils/error/csvlog.c
+++ b/src/backend/utils/error/csvlog.c
@@ -153,11 +153,11 @@ write_csvlog(ErrorData *edata)
/* Virtual transaction id */
/* keep VXID format in sync with lockfuncs.c */
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
- appendStringInfo(&buf, "%d/%u", MyProc->backendId, MyProc->lxid);
+ appendStringInfo(&buf, "%d/" XID_FMT, MyProc->backendId, MyProc->lxid);
appendStringInfoChar(&buf, ',');
/* Transaction id */
- appendStringInfo(&buf, "%u", GetTopTransactionIdIfAny());
+ appendStringInfo(&buf, XID_FMT, GetTopTransactionIdIfAny());
appendStringInfoChar(&buf, ',');
/* Error severity */
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index 7402696986b..ab6a0f8c09c 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -2754,12 +2754,12 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
{
char strfbuf[128];
- snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/%u",
+ snprintf(strfbuf, sizeof(strfbuf) - 1, "%d/" XID_FMT,
MyProc->backendId, MyProc->lxid);
appendStringInfo(buf, "%*s", padding, strfbuf);
}
else
- appendStringInfo(buf, "%d/%u", MyProc->backendId, MyProc->lxid);
+ appendStringInfo(buf, "%d/" XID_FMT, MyProc->backendId, MyProc->lxid);
}
else if (padding != 0)
appendStringInfoSpaces(buf,
@@ -2767,9 +2767,9 @@ log_line_prefix(StringInfo buf, ErrorData *edata)
break;
case 'x':
if (padding != 0)
- appendStringInfo(buf, "%*u", padding, GetTopTransactionIdIfAny());
+ appendStringInfo(buf, "%*" INT64_MODIFIER "u", padding, GetTopTransactionIdIfAny());
else
- appendStringInfo(buf, "%u", GetTopTransactionIdIfAny());
+ appendStringInfo(buf, XID_FMT, GetTopTransactionIdIfAny());
break;
case 'e':
if (padding != 0)
diff --git a/src/backend/utils/error/jsonlog.c b/src/backend/utils/error/jsonlog.c
index 843641c865f..7ef41813d00 100644
--- a/src/backend/utils/error/jsonlog.c
+++ b/src/backend/utils/error/jsonlog.c
@@ -198,11 +198,11 @@ write_jsonlog(ErrorData *edata)
/* Virtual transaction id */
/* keep VXID format in sync with lockfuncs.c */
if (MyProc != NULL && MyProc->backendId != InvalidBackendId)
- appendJSONKeyValueFmt(&buf, "vxid", true, "%d/%u", MyProc->backendId,
+ appendJSONKeyValueFmt(&buf, "vxid", true, "%d/" XID_FMT, MyProc->backendId,
MyProc->lxid);
/* Transaction id */
- appendJSONKeyValueFmt(&buf, "txid", false, "%u",
+ appendJSONKeyValueFmt(&buf, "txid", false, XID_FMT,
GetTopTransactionIdIfAny());
/* Error severity */
diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c
index b2e72b3243f..478447e4d4d 100644
--- a/src/backend/utils/fmgr/fmgr.c
+++ b/src/backend/utils/fmgr/fmgr.c
@@ -540,7 +540,7 @@ lookup_C_func(HeapTuple procedureTuple)
NULL);
if (entry == NULL)
return NULL; /* no such entry */
- if (entry->fn_xmin == HeapTupleHeaderGetRawXmin(procedureTuple->t_data) &&
+ if (entry->fn_xmin == HeapTupleGetRawXmin(procedureTuple) &&
ItemPointerEquals(&entry->fn_tid, &procedureTuple->t_self))
return entry; /* OK */
return NULL; /* entry is out of date */
@@ -576,7 +576,7 @@ record_C_func(HeapTuple procedureTuple,
HASH_ENTER,
&found);
/* OID is already filled in */
- entry->fn_xmin = HeapTupleHeaderGetRawXmin(procedureTuple->t_data);
+ entry->fn_xmin = HeapTupleGetRawXmin(procedureTuple);
entry->fn_tid = procedureTuple->t_self;
entry->user_fn = user_fn;
entry->inforec = inforec;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index fea143834e3..4d997a10b11 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2649,74 +2649,6 @@ static struct config_int ConfigureNamesInt[] =
NULL, NULL, NULL
},
- {
- {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Minimum age at which VACUUM should freeze a table row."),
- NULL
- },
- &vacuum_freeze_min_age,
- 50000000, 0, 1000000000,
- NULL, NULL, NULL
- },
-
- {
- {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
- NULL
- },
- &vacuum_freeze_table_age,
- 150000000, 0, 2000000000,
- NULL, NULL, NULL
- },
-
- {
- {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
- NULL
- },
- &vacuum_multixact_freeze_min_age,
- 5000000, 0, 1000000000,
- NULL, NULL, NULL
- },
-
- {
- {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
- NULL
- },
- &vacuum_multixact_freeze_table_age,
- 150000000, 0, 2000000000,
- NULL, NULL, NULL
- },
-
- {
- {"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_PRIMARY,
- gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
- NULL
- },
- &vacuum_defer_cleanup_age,
- 0, 0, 1000000, /* see ComputeXidHorizons */
- NULL, NULL, NULL
- },
- {
- {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
- NULL
- },
- &vacuum_failsafe_age,
- 1600000000, 0, 2100000000,
- NULL, NULL, NULL
- },
- {
- {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
- gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
- NULL
- },
- &vacuum_multixact_failsafe_age,
- 1600000000, 0, 2100000000,
- NULL, NULL, NULL
- },
-
/*
* See also CheckRequiredParameterValues() if this parameter changes
*/
@@ -3315,28 +3247,6 @@ static struct config_int ConfigureNamesInt[] =
50, 0, INT_MAX,
NULL, NULL, NULL
},
- {
- /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
- {"autovacuum_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
- gettext_noop("Age at which to autovacuum a table to prevent transaction ID wraparound."),
- NULL
- },
- &autovacuum_freeze_max_age,
-
- /* see vacuum_failsafe_age if you change the upper-limit value. */
- 200000000, 100000, 2000000000,
- NULL, NULL, NULL
- },
- {
- /* see multixact.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
- {"autovacuum_multixact_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
- gettext_noop("Multixact age at which to autovacuum a table to prevent multixact wraparound."),
- NULL
- },
- &autovacuum_multixact_freeze_max_age,
- 400000000, 10000, 2000000000,
- NULL, NULL, NULL
- },
{
/* see max_connections */
{"autovacuum_max_workers", PGC_POSTMASTER, AUTOVACUUM,
@@ -3883,6 +3793,100 @@ static struct config_real ConfigureNamesReal[] =
static struct config_int64 ConfigureNamesInt64[] =
{
+ {
+ {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Minimum age at which VACUUM should freeze a table row."),
+ NULL
+ },
+ &vacuum_freeze_min_age,
+ INT64CONST(50000000), INT64CONST(0), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Age at which VACUUM should scan whole table to freeze tuples."),
+ NULL
+ },
+ &vacuum_freeze_table_age,
+ INT64CONST(150000000), INT64CONST(0), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_multixact_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Minimum age at which VACUUM should freeze a MultiXactId in a table row."),
+ NULL
+ },
+ &vacuum_multixact_freeze_min_age,
+ INT64CONST(5000000), INT64CONST(0), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_multixact_freeze_table_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Multixact age at which VACUUM should scan whole table to freeze tuples."),
+ NULL
+ },
+ &vacuum_multixact_freeze_table_age,
+ INT64CONST(150000000), INT64CONST(0), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_defer_cleanup_age", PGC_SIGHUP, REPLICATION_PRIMARY,
+ gettext_noop("Number of transactions by which VACUUM and HOT cleanup should be deferred, if any."),
+ NULL
+ },
+ &vacuum_defer_cleanup_age,
+ INT64CONST(0), INT64CONST(0), INT64CONST(1000000),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
+ NULL
+ },
+ &vacuum_failsafe_age,
+ INT64CONST(1600000000), INT64CONST(0), INT64CONST(2100000000),
+ NULL, NULL, NULL
+ },
+
+ {
+ {"vacuum_multixact_failsafe_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
+ gettext_noop("Multixact age at which VACUUM should trigger failsafe to avoid a wraparound outage."),
+ NULL
+ },
+ &vacuum_multixact_failsafe_age,
+ INT64CONST(1600000000), INT64CONST(0), INT64CONST(2100000000),
+ NULL, NULL, NULL
+ },
+
+ {
+ /* see varsup.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
+ {"autovacuum_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Age at which to autovacuum a table to prevent transaction ID wraparound."),
+ NULL
+ },
+ &autovacuum_freeze_max_age,
+
+ /* see vacuum_failsafe_age if you change the upper-limit value. */
+ INT64CONST(10000000000), INT64CONST(100000), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
+ {
+ /* see multixact.c for why this is PGC_POSTMASTER not PGC_SIGHUP */
+ {"autovacuum_multixact_freeze_max_age", PGC_POSTMASTER, AUTOVACUUM,
+ gettext_noop("Multixact age at which to autovacuum a table to prevent multixact wraparound."),
+ NULL
+ },
+ &autovacuum_multixact_freeze_max_age,
+ INT64CONST(20000000000), INT64CONST(10000), INT64CONST(0x7FFFFFFFFFFFFFFF),
+ NULL, NULL, NULL
+ },
+
/* End-of-list marker */
{
{NULL, 0, 0, NULL, NULL}, NULL, 0.0, 0.0, 0.0, NULL, NULL, NULL
diff --git a/src/backend/utils/misc/help_config.c b/src/backend/utils/misc/help_config.c
index 61c83f35901..8c7c50a3ef0 100644
--- a/src/backend/utils/misc/help_config.c
+++ b/src/backend/utils/misc/help_config.c
@@ -33,6 +33,7 @@ typedef union
struct config_bool _bool;
struct config_real real;
struct config_int integer;
+ struct config_int64 integer8;
struct config_string string;
struct config_enum _enum;
} mixedStruct;
@@ -107,7 +108,12 @@ printMixedStruct(mixedStruct *structToPrint)
structToPrint->integer.min,
structToPrint->integer.max);
break;
-
+ case PGC_INT64:
+ printf("INT64\t" INT64_FORMAT "\t" INT64_FORMAT "\t" INT64_FORMAT "\t",
+ structToPrint->integer8.reset_val,
+ structToPrint->integer8.min,
+ structToPrint->integer8.max);
+ break;
case PGC_REAL:
printf("REAL\t%g\t%g\t%g\t",
structToPrint->real.reset_val,
diff --git a/src/backend/utils/misc/pg_controldata.c b/src/backend/utils/misc/pg_controldata.c
index 781f8b87580..54b719ef3d9 100644
--- a/src/backend/utils/misc/pg_controldata.c
+++ b/src/backend/utils/misc/pg_controldata.c
@@ -164,8 +164,7 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
values[5] = BoolGetDatum(ControlFile->checkPointCopy.fullPageWrites);
nulls[5] = false;
- values[6] = CStringGetTextDatum(psprintf("%u:%u",
- EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
+ values[6] = CStringGetTextDatum(psprintf(XID_FMT,
XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid)));
nulls[6] = false;
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 817d5f53246..2b6c2e6e44e 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -640,9 +640,9 @@
#autovacuum_vacuum_insert_scale_factor = 0.2 # fraction of inserts over table
# size before insert vacuum
#autovacuum_analyze_scale_factor = 0.1 # fraction of table size before analyze
-#autovacuum_freeze_max_age = 200000000 # maximum XID age before forced vacuum
+#autovacuum_freeze_max_age = 10000000000 # maximum XID age before forced vacuum
# (change requires restart)
-#autovacuum_multixact_freeze_max_age = 400000000 # maximum multixact age
+#autovacuum_multixact_freeze_max_age = 20000000000 # maximum multixact age
# before forced vacuum
# (change requires restart)
#autovacuum_vacuum_cost_delay = 2ms # default vacuum cost delay for
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index a3f22d7357f..4bdde51e473 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -4078,11 +4078,13 @@ static void
writetup_cluster(Tuplesortstate *state, LogicalTape *tape, SortTuple *stup)
{
HeapTuple tuple = (HeapTuple) stup->tuple;
- unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + sizeof(int);
+ unsigned int tuplen = tuple->t_len + sizeof(ItemPointerData) + 2 * sizeof(TransactionId) + sizeof(int);
/* We need to store t_self, but not other fields of HeapTupleData */
LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
LogicalTapeWrite(tape, &tuple->t_self, sizeof(ItemPointerData));
+ LogicalTapeWrite(tape, &tuple->t_xid_base, sizeof(TransactionId));
+ LogicalTapeWrite(tape, &tuple->t_multi_base, sizeof(TransactionId));
LogicalTapeWrite(tape, tuple->t_data, tuple->t_len);
if (state->randomAccess) /* need trailing length word? */
LogicalTapeWrite(tape, &tuplen, sizeof(tuplen));
@@ -4098,7 +4100,7 @@ static void
readtup_cluster(Tuplesortstate *state, SortTuple *stup,
LogicalTape *tape, unsigned int tuplen)
{
- unsigned int t_len = tuplen - sizeof(ItemPointerData) - sizeof(int);
+ unsigned int t_len = tuplen - sizeof(ItemPointerData) - 2 * sizeof(TransactionId) - sizeof(int);
HeapTuple tuple = (HeapTuple) readtup_alloc(state,
t_len + HEAPTUPLESIZE);
@@ -4106,6 +4108,8 @@ readtup_cluster(Tuplesortstate *state, SortTuple *stup,
tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
tuple->t_len = t_len;
LogicalTapeReadExact(tape, &tuple->t_self, sizeof(ItemPointerData));
+ LogicalTapeReadExact(tape, &tuple->t_xid_base, sizeof(TransactionId));
+ LogicalTapeReadExact(tape, &tuple->t_multi_base, sizeof(TransactionId));
/* We don't currently bother to reconstruct t_tableOid */
tuple->t_tableOid = InvalidOid;
/* Read in the tuple body */
diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c
index 6613dc05340..ca3ac89db74 100644
--- a/src/backend/utils/time/combocid.c
+++ b/src/backend/utils/time/combocid.c
@@ -101,12 +101,13 @@ static CommandId GetRealCmax(CommandId combocid);
*/
CommandId
-HeapTupleHeaderGetCmin(HeapTupleHeader tup)
+HeapTupleGetCmin(HeapTuple tuple)
{
+ HeapTupleHeader tup = tuple->t_data;
CommandId cid = HeapTupleHeaderGetRawCommandId(tup);
Assert(!(tup->t_infomask & HEAP_MOVED));
- Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tup)));
+ Assert(TransactionIdIsCurrentTransactionId(HeapTupleGetXmin(tuple)));
if (tup->t_infomask & HEAP_COMBOCID)
return GetRealCmin(cid);
@@ -115,8 +116,9 @@ HeapTupleHeaderGetCmin(HeapTupleHeader tup)
}
CommandId
-HeapTupleHeaderGetCmax(HeapTupleHeader tup)
+HeapTupleGetCmax(HeapTuple tuple)
{
+ HeapTupleHeader tup = tuple->t_data;
CommandId cid = HeapTupleHeaderGetRawCommandId(tup);
Assert(!(tup->t_infomask & HEAP_MOVED));
@@ -128,7 +130,7 @@ HeapTupleHeaderGetCmax(HeapTupleHeader tup)
* things too much.
*/
Assert(CritSectionCount > 0 ||
- TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tup)));
+ TransactionIdIsCurrentTransactionId(HeapTupleGetUpdateXidAny(tuple)));
if (tup->t_infomask & HEAP_COMBOCID)
return GetRealCmax(cid);
@@ -150,7 +152,7 @@ HeapTupleHeaderGetCmax(HeapTupleHeader tup)
* changes the tuple in shared buffers.
*/
void
-HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+HeapTupleHeaderAdjustCmax(HeapTuple tup,
CommandId *cmax,
bool *iscombo)
{
@@ -160,10 +162,10 @@ HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
* Test for HeapTupleHeaderXminCommitted() first, because it's cheaper
* than a TransactionIdIsCurrentTransactionId call.
*/
- if (!HeapTupleHeaderXminCommitted(tup) &&
- TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmin(tup)))
+ if (!HeapTupleHeaderXminCommitted(tup->t_data) &&
+ TransactionIdIsCurrentTransactionId(HeapTupleGetRawXmin(tup)))
{
- CommandId cmin = HeapTupleHeaderGetCmin(tup);
+ CommandId cmin = HeapTupleGetCmin(tup);
*cmax = GetComboCommandId(cmin, *cmax);
*iscombo = true;
diff --git a/src/backend/utils/time/snapmgr.c b/src/backend/utils/time/snapmgr.c
index a0b703a5195..d0b6446605c 100644
--- a/src/backend/utils/time/snapmgr.c
+++ b/src/backend/utils/time/snapmgr.c
@@ -1172,8 +1172,9 @@ ExportSnapshot(Snapshot snapshot)
* Generate file path for the snapshot. We start numbering of snapshots
* inside the transaction from 1.
*/
- snprintf(path, sizeof(path), SNAPSHOT_EXPORT_DIR "/%08X-%08X-%d",
- MyProc->backendId, MyProc->lxid, list_length(exportedSnapshots) + 1);
+ snprintf(path, sizeof(path), SNAPSHOT_EXPORT_DIR "/%08X-%08X%08X-%d",
+ MyProc->backendId, (uint32) (MyProc->lxid >> 32),
+ (uint32) MyProc->lxid, list_length(exportedSnapshots) + 1);
/*
* Copy the snapshot into TopTransactionContext, add it to the
@@ -1200,14 +1201,14 @@ ExportSnapshot(Snapshot snapshot)
*/
initStringInfo(&buf);
- appendStringInfo(&buf, "vxid:%d/%u\n", MyProc->backendId, MyProc->lxid);
+ appendStringInfo(&buf, "vxid:%d/" XID_FMT "\n", MyProc->backendId, MyProc->lxid);
appendStringInfo(&buf, "pid:%d\n", MyProcPid);
appendStringInfo(&buf, "dbid:%u\n", MyDatabaseId);
appendStringInfo(&buf, "iso:%d\n", XactIsoLevel);
appendStringInfo(&buf, "ro:%d\n", XactReadOnly);
- appendStringInfo(&buf, "xmin:%u\n", snapshot->xmin);
- appendStringInfo(&buf, "xmax:%u\n", snapshot->xmax);
+ appendStringInfo(&buf, "xmin:" XID_FMT "\n", snapshot->xmin);
+ appendStringInfo(&buf, "xmax:" XID_FMT "\n", snapshot->xmax);
/*
* We must include our own top transaction ID in the top-xid data, since
@@ -1224,9 +1225,9 @@ ExportSnapshot(Snapshot snapshot)
TransactionIdPrecedes(topXid, snapshot->xmax)) ? 1 : 0;
appendStringInfo(&buf, "xcnt:%d\n", snapshot->xcnt + addTopXid);
for (i = 0; i < snapshot->xcnt; i++)
- appendStringInfo(&buf, "xip:%u\n", snapshot->xip[i]);
+ appendStringInfo(&buf, "xip:" XID_FMT "\n", snapshot->xip[i]);
if (addTopXid)
- appendStringInfo(&buf, "xip:%u\n", topXid);
+ appendStringInfo(&buf, "xip:" XID_FMT "\n", topXid);
/*
* Similarly, we add our subcommitted child XIDs to the subxid data. Here,
@@ -1240,9 +1241,9 @@ ExportSnapshot(Snapshot snapshot)
appendStringInfoString(&buf, "sof:0\n");
appendStringInfo(&buf, "sxcnt:%d\n", snapshot->subxcnt + nchildren);
for (i = 0; i < snapshot->subxcnt; i++)
- appendStringInfo(&buf, "sxp:%u\n", snapshot->subxip[i]);
+ appendStringInfo(&buf, "sxp:" XID_FMT "\n", snapshot->subxip[i]);
for (i = 0; i < nchildren; i++)
- appendStringInfo(&buf, "sxp:%u\n", children[i]);
+ appendStringInfo(&buf, "sxp:" XID_FMT "\n", children[i]);
}
appendStringInfo(&buf, "rec:%u\n", snapshot->takenDuringRecovery);
@@ -1345,7 +1346,7 @@ parseXidFromText(const char *prefix, char **s, const char *filename)
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid snapshot data in file \"%s\"", filename)));
ptr += prefixlen;
- if (sscanf(ptr, "%u", &val) != 1)
+ if (sscanf(ptr, XID_FMT, &val) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid snapshot data in file \"%s\"", filename)));
@@ -1370,7 +1371,7 @@ parseVxidFromText(const char *prefix, char **s, const char *filename,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid snapshot data in file \"%s\"", filename)));
ptr += prefixlen;
- if (sscanf(ptr, "%d/%u", &vxid->backendId, &vxid->localTransactionId) != 2)
+ if (sscanf(ptr, "%d/" XID_FMT, &vxid->backendId, &vxid->localTransactionId) != 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid snapshot data in file \"%s\"", filename)));
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 0a2dba7d18e..1b68593462f 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -148,6 +148,9 @@ static bool data_checksums = false;
static char *xlog_dir = NULL;
static char *str_wal_segment_size_mb = NULL;
static int wal_segment_size_mb;
+static TransactionId start_xid = 0;
+static MultiXactId start_mx_id = 0;
+static MultiXactOffset start_mx_offset = 0;
/* internal vars */
@@ -1408,10 +1411,13 @@ bootstrap_template1(void)
unsetenv("PGCLIENTENCODING");
snprintf(cmd, sizeof(cmd),
- "\"%s\" --boot -X %d %s %s %s %s",
+ "\"%s\" --boot -X %d %s %s " XID_FMT " %s " XID_FMT " %s " XID_FMT " %s %s %s",
backend_exec,
wal_segment_size_mb * (1024 * 1024),
data_checksums ? "-k" : "",
+ "-Z", start_xid,
+ "-m", start_mx_id,
+ "-o", start_mx_offset,
boot_options, extra_options,
debug ? "-d 5" : "");
@@ -2186,13 +2192,21 @@ usage(const char *progname)
printf(_(" -W, --pwprompt prompt for a password for the new superuser\n"));
printf(_(" -X, --waldir=WALDIR location for the write-ahead log directory\n"));
printf(_(" --wal-segsize=SIZE size of WAL segments, in megabytes\n"));
+ printf(_(" -x, --xid=START_XID specify start xid value in decimal format for new db instance to test 64-bit xids,\n"
+ " default value is 0, max value is 2^62-1\n"));
printf(_("\nLess commonly used options:\n"));
printf(_(" -d, --debug generate lots of debugging output\n"));
printf(_(" --discard-caches set debug_discard_caches=1\n"));
printf(_(" -L DIRECTORY where to find the input files\n"));
+ printf(_(" -m, --multixact-id=START_MX_ID\n"
+ " specify start multixact id value in decimal format for new db instance\n"
+ " to test 64-bit xids, default value is 0, max value is 2^62-1\n"));
printf(_(" -n, --no-clean do not clean up after errors\n"));
printf(_(" -N, --no-sync do not wait for changes to be written safely to disk\n"));
printf(_(" --no-instructions do not print instructions for next steps\n"));
+ printf(_(" -o, --multixact-offset=START_MX_OFFSET\n"
+ " specify start multixact offset value in decimal format for new db instance\n"
+ " to test 64-bit xids, default value is 0, max value is 2^62-1\n"));
printf(_(" -s, --show show internal settings\n"));
printf(_(" -S, --sync-only only sync database files to disk, then exit\n"));
printf(_("\nOther options:\n"));
@@ -2869,6 +2883,10 @@ main(int argc, char *argv[])
{"data-checksums", no_argument, NULL, 'k'},
{"allow-group-access", no_argument, NULL, 'g'},
{"discard-caches", no_argument, NULL, 14},
+ {"no-data-checksums", no_argument, NULL, 15},
+ {"xid", required_argument, NULL, 'x'},
+ {"multixact-id", required_argument, NULL, 'm'},
+ {"multixact-offset", required_argument, NULL, 'o'},
{NULL, 0, NULL, 0}
};
@@ -2910,7 +2928,7 @@ main(int argc, char *argv[])
/* process command-line options */
- while ((c = getopt_long(argc, argv, "A:dD:E:gkL:nNsST:U:WX:", long_options, &option_index)) != -1)
+ while ((c = getopt_long(argc, argv, "A:dD:E:gkL:m:nNo:sST:U:Wx:X:", long_options, &option_index)) != -1)
{
switch (c)
{
@@ -2949,6 +2967,20 @@ main(int argc, char *argv[])
debug = true;
printf(_("Running in debug mode.\n"));
break;
+ case 'm':
+ if (sscanf(optarg, XID_FMT, &start_mx_id) != 1)
+ {
+ fprintf(stderr, "%s: invalid decimal START_MX_ID value\n",
+ progname);
+ exit(1);
+ }
+ if (!StartMultiXactIdIsValid(start_mx_id))
+ {
+ fprintf(stderr, "%s: out-of-range START_MX_ID value (the value must be less than 2^62)\n",
+ progname);
+ exit(1);
+ }
+ break;
case 'n':
noclean = true;
printf(_("Running in no-clean mode. Mistakes will not be cleaned up.\n"));
@@ -2956,6 +2988,20 @@ main(int argc, char *argv[])
case 'N':
do_sync = false;
break;
+ case 'o':
+ if (sscanf(optarg, XID_FMT, &start_mx_offset) != 1)
+ {
+ fprintf(stderr, "%s: invalid decimal START_MX_OFFSET value\n",
+ progname);
+ exit(1);
+ }
+ if (!StartMultiXactOffsetIsValid(start_mx_offset))
+ {
+ fprintf(stderr, "%s: out-of-range START_MX_OFFSET value (the value must be less than 2^62)\n",
+ progname);
+ exit(1);
+ }
+ break;
case 'S':
sync_only = true;
break;
@@ -3015,6 +3061,23 @@ main(int argc, char *argv[])
extra_options,
"-c debug_discard_caches=1");
break;
+ case 15:
+ data_checksums = false;
+ break;
+ case 'x':
+ if (sscanf(optarg, XID_FMT, &start_xid) != 1)
+ {
+ fprintf(stderr, "%s: invalid decimal START_XID value\n",
+ progname);
+ exit(1);
+ }
+ if (!StartTransactionIdIsValid(start_xid))
+ {
+ fprintf(stderr, "%s: out-of-range START_XID value (the value must be less than 2^62)\n",
+ progname);
+ exit(1);
+ }
+ break;
default:
/* getopt_long already emitted a complaint */
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
diff --git a/src/bin/pg_amcheck/t/004_verify_heapam.pl b/src/bin/pg_amcheck/t/004_verify_heapam.pl
index 84e75862769..c11c8326649 100644
--- a/src/bin/pg_amcheck/t/004_verify_heapam.pl
+++ b/src/bin/pg_amcheck/t/004_verify_heapam.pl
@@ -10,6 +10,8 @@ use PostgreSQL::Test::Utils;
use Fcntl qw(:seek);
use Test::More;
+use Data::Dumper;
+
# This regression test demonstrates that the pg_amcheck binary correctly
# identifies specific kinds of corruption within pages. To test this, we need
# a mechanism to create corrupt pages with predictable, repeatable corruption.
@@ -86,6 +88,60 @@ use Test::More;
use constant HEAPTUPLE_PACK_CODE => 'LLLSSSSSCCLLCCCCCCCCCCllLL';
use constant HEAPTUPLE_PACK_LENGTH => 58; # Total size
+use constant HEAPPAGE_SPECIAL_PACK_CODE => 'QQ';
+use constant HEAPPAGE_SPECIAL_PACK_LENGTH => 16;
+use constant HEAPPAGE_SIZE => 8192;
+
+# Some #define constants from access/htup_details.h for use while corrupting.
+use constant HEAP_HASNULL => 0x0001;
+use constant HEAP_XMAX_LOCK_ONLY => 0x0080;
+use constant HEAP_XMIN_COMMITTED => 0x0100;
+use constant HEAP_XMIN_INVALID => 0x0200;
+use constant HEAP_XMAX_COMMITTED => 0x0400;
+use constant HEAP_XMAX_INVALID => 0x0800;
+use constant HEAP_NATTS_MASK => 0x07FF;
+use constant HEAP_XMAX_IS_MULTI => 0x1000;
+use constant HEAP_KEYS_UPDATED => 0x2000;
+
+use constant FIRST_NORMAL_TRANSACTION_ID => 3;
+
+# Read page special data
+sub read_special_data
+{
+ my ($fh, $offset) = @_;
+ my ($buffer, %special);
+ $offset -= $offset % HEAPPAGE_SIZE;
+ $offset += HEAPPAGE_SIZE - HEAPPAGE_SPECIAL_PACK_LENGTH;
+ seek($fh, $offset, SEEK_SET)
+ or BAIL_OUT("seek failed: $!");
+ defined(sysread($fh, $buffer, HEAPPAGE_SPECIAL_PACK_LENGTH))
+ or BAIL_OUT("sysread failed: $!");
+
+ @_ = unpack(HEAPPAGE_SPECIAL_PACK_CODE, $buffer);
+ %special = (
+ pd_xid_base => shift,
+ pd_multi_base => shift);
+ return \%special;
+}
+
+# Write page special data
+sub write_special_data
+{
+ my ($fh, $offset, $special) = @_;
+
+ $offset -= $offset % HEAPPAGE_SIZE;
+ $offset += HEAPPAGE_SIZE - HEAPPAGE_SPECIAL_PACK_LENGTH;
+
+ my $buffer = pack(
+ HEAPPAGE_SPECIAL_PACK_CODE,
+ $special->{pd_xid_base}, $special->{pd_multi_base});
+
+ seek($fh, $offset, SEEK_SET)
+ or BAIL_OUT("seek failed: $!");
+ defined(syswrite($fh, $buffer, HEAPPAGE_SPECIAL_PACK_LENGTH))
+ or BAIL_OUT("syswrite failed: $!");
+ return;
+}
# Read a tuple of our table from a heap page.
#
@@ -97,7 +153,7 @@ use constant HEAPTUPLE_PACK_LENGTH => 58; # Total size
#
sub read_tuple
{
- my ($fh, $offset) = @_;
+ my ($fh, $offset, $raw) = @_;
my ($buffer, %tup);
seek($fh, $offset, SEEK_SET)
or BAIL_OUT("seek failed: $!");
@@ -134,6 +190,18 @@ sub read_tuple
c_va_toastrelid => shift);
# Stitch together the text for column 'b'
$tup{b} = join('', map { chr($tup{"b_body$_"}) } (1 .. 7));
+
+ if (!$raw)
+ {
+ my $special = read_special_data($fh, $offset);
+
+ $tup{t_xmin} += $special->{pd_xid_base};
+ my $is_multi = $tup{t_infomask} & HEAP_XMAX_IS_MULTI;
+ $tup{t_xmax} += !$is_multi ?
+ $special->{pd_xid_base} :
+ $special->{pd_multi_base};
+ }
+
return \%tup;
}
@@ -149,7 +217,32 @@ sub read_tuple
#
sub write_tuple
{
- my ($fh, $offset, $tup) = @_;
+ my ($fh, $offset, $tup, $raw) = @_;
+ if (!$raw)
+ {
+ my $special = read_special_data($fh, $offset);
+
+ my $xmin = $tup->{t_xmin} - $special->{pd_xid_base};
+ die "tuple x_min $tup->{t_xmin} is too smal for pd_xid_base $special->{pd_xid_base}"
+ if $xmin < 3;
+ $tup->{t_xmin} = $xmin;
+
+ if (($tup->{t_infomask} & HEAP_XMAX_IS_MULTI) == 0)
+ {
+ my $xmax = $tup->{t_xmax} - $special->{pd_xid_base};
+ die "tuple x_max $tup->{t_xmax} is too smal for pd_xid_base $special->{pd_xid_base}"
+ if $xmax < 3;
+ $tup->{t_xmax} = $xmax;
+ }
+ else
+ {
+ my $xmax = $tup->{t_xmax} - $special->{pd_multi_base};
+ die "tuple multi x_max $tup->{t_xmax} is too smal for pd_multi_base $special->{pd_multi_base}"
+ if $xmax < 3;
+ $tup->{t_xmax} = $xmax;
+ }
+ }
+
my $buffer = pack(
HEAPTUPLE_PACK_CODE,
$tup->{t_xmin}, $tup->{t_xmax},
@@ -172,6 +265,41 @@ sub write_tuple
return;
}
+# move pd_xid_base and pd_multi_base to more suitable position for tests.
+sub fixup_page
+{
+ my ($fh, $page, $xid_base, $multi_base, $lp_off) = @_;
+ my $offset = $page * HEAPPAGE_SIZE;
+ my $special = read_special_data($fh, $offset);
+
+ die "xid_base $xid_base should be lesser than existed $special->{pd_xid_base}"
+ if ($xid_base > $special->{pd_xid_base});
+ die "multi_base $multi_base should be lesser than existed $special->{pd_multi_base}"
+ if ($multi_base > $special->{pd_multi_base} && $special->{pd_multi_base} != 0);
+ return if ($xid_base == $special->{pd_xid_base} &&
+ $multi_base == $special->{pd_multi_base});
+
+ my $xid_delta = $special->{pd_xid_base} - $xid_base;
+ my $multi_delta = $special->{pd_multi_base} - $multi_base;
+
+ for my $off (@$lp_off)
+ {
+ # change only tuples on this page.
+ next if ($off < $offset && $off > $offset + HEAPPAGE_SIZE);
+
+ my $tup = read_tuple($fh, $off, 1);
+ $tup->{t_xmin} += $xid_delta;
+ my $is_multi = $tup->{t_infomask} & HEAP_XMAX_IS_MULTI;
+ $tup->{t_xmax} += !$is_multi ? $xid_delta : $multi_delta;
+ write_tuple($fh, $off, $tup, 1);
+ }
+
+ $special->{pd_xid_base} = $xid_base;
+ $special->{pd_multi_base} = $multi_base;
+
+ write_special_data($fh, $offset, $special);
+}
+
# Set umask so test directories and files are created with default permissions
umask(0077);
@@ -234,6 +362,10 @@ my $relfrozenxid = $node->safe_psql('postgres',
q(select relfrozenxid from pg_class where relname = 'test'));
my $datfrozenxid = $node->safe_psql('postgres',
q(select datfrozenxid from pg_database where datname = 'postgres'));
+my $datminmxid = $node->safe_psql('postgres',
+ q(select datminmxid from pg_database where datname = 'postgres'));
+my $txid_current = $node->safe_psql('postgres',
+ q(select txid_current()));
# Sanity check that our 'test' table has a relfrozenxid newer than the
# datfrozenxid for the database, and that the datfrozenxid is greater than the
@@ -292,6 +424,11 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
# Determine endianness of current platform from the 1-byte varlena header
$ENDIANNESS = $tup->{b_header} == 0x11 ? "little" : "big";
}
+
+# Set 64bit xid bases a bit in the past therefore we can set xmin/xmax a bit
+# in the past
+fixup_page($file, 0, $datfrozenxid - 100, $datminmxid - 100, \@lp_off);
+
close($file)
or BAIL_OUT("close failed: $!");
$node->start;
@@ -310,17 +447,6 @@ $node->command_ok([ 'pg_amcheck', '-p', $port, 'postgres' ],
$node->stop;
-# Some #define constants from access/htup_details.h for use while corrupting.
-use constant HEAP_HASNULL => 0x0001;
-use constant HEAP_XMAX_LOCK_ONLY => 0x0080;
-use constant HEAP_XMIN_COMMITTED => 0x0100;
-use constant HEAP_XMIN_INVALID => 0x0200;
-use constant HEAP_XMAX_COMMITTED => 0x0400;
-use constant HEAP_XMAX_INVALID => 0x0800;
-use constant HEAP_NATTS_MASK => 0x07FF;
-use constant HEAP_XMAX_IS_MULTI => 0x1000;
-use constant HEAP_KEYS_UPDATED => 0x2000;
-
# Helper function to generate a regular expression matching the header we
# expect verify_heapam() to return given which fields we expect to be non-null.
sub header
@@ -364,39 +490,39 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
# Expected corruption report
push @expected,
- qr/${header}xmin $xmin precedes relation freeze threshold 0:\d+/;
+ qr/${header}xmin $xmin precedes relation freeze threshold \d+/;
}
if ($offnum == 2)
{
# Corruptly set xmin < datfrozenxid
- my $xmin = 3;
+ my $xmin = $datfrozenxid - 10;
$tup->{t_xmin} = $xmin;
$tup->{t_infomask} &= ~HEAP_XMIN_COMMITTED;
$tup->{t_infomask} &= ~HEAP_XMIN_INVALID;
push @expected,
- qr/${$header}xmin $xmin precedes oldest valid transaction ID 0:\d+/;
+ qr/${$header}xmin $xmin precedes oldest valid transaction ID \d+/;
}
elsif ($offnum == 3)
{
- # Corruptly set xmin < datfrozenxid, further back, noting circularity
- # of xid comparison. For a new cluster with epoch = 0, the corrupt
- # xmin will be interpreted as in the future
- $tup->{t_xmin} = 4026531839;
+ # Corruptly set xmin > next transaction id.
+ my $xmin = $relfrozenxid + 1000000;
+ $tup->{t_xmin} = $xmin;
$tup->{t_infomask} &= ~HEAP_XMIN_COMMITTED;
$tup->{t_infomask} &= ~HEAP_XMIN_INVALID;
push @expected,
- qr/${$header}xmin 4026531839 equals or exceeds next valid transaction ID 0:\d+/;
+ qr/${$header}xmin $xmin equals or exceeds next valid transaction ID \d+/;
}
elsif ($offnum == 4)
{
- # Corruptly set xmax < relminmxid;
- $tup->{t_xmax} = 4026531839;
+ # Corruptly set xmax > next transaction id.
+ my $xmax = $relfrozenxid + 1000000;
+ $tup->{t_xmax} = $xmax;
$tup->{t_infomask} &= ~HEAP_XMAX_INVALID;
push @expected,
- qr/${$header}xmax 4026531839 equals or exceeds next valid transaction ID 0:\d+/;
+ qr/${$header}xmax $xmax equals or exceeds next valid transaction ID \d+/;
}
elsif ($offnum == 5)
{
@@ -499,20 +625,22 @@ for (my $tupidx = 0; $tupidx < ROWCOUNT; $tupidx++)
# Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI
$tup->{t_infomask} |= HEAP_XMAX_COMMITTED;
$tup->{t_infomask} |= HEAP_XMAX_IS_MULTI;
- $tup->{t_xmax} = 4;
+ my $xmax = $datminmxid + 1000000;
+ $tup->{t_xmax} = $xmax;
push @expected,
- qr/${header}multitransaction ID 4 equals or exceeds next valid multitransaction ID 1/;
+ qr/${header}multitransaction ID $xmax equals or exceeds next valid multitransaction ID \d+/;
}
elsif ($offnum == 15) # Last offnum must equal ROWCOUNT
{
# Set both HEAP_XMAX_COMMITTED and HEAP_XMAX_IS_MULTI
$tup->{t_infomask} |= HEAP_XMAX_COMMITTED;
$tup->{t_infomask} |= HEAP_XMAX_IS_MULTI;
- $tup->{t_xmax} = 4000000000;
+ my $xmax = $datminmxid - 10;
+ $tup->{t_xmax} = $xmax;
push @expected,
- qr/${header}multitransaction ID 4000000000 precedes relation minimum multitransaction ID threshold 1/;
+ qr/${header}multitransaction ID $xmax precedes relation minimum multitransaction ID threshold \d+/;
}
write_tuple($file, $offset, $tup);
}
diff --git a/src/bin/pg_controldata/pg_controldata.c b/src/bin/pg_controldata/pg_controldata.c
index f911f98d946..997df822e8e 100644
--- a/src/bin/pg_controldata/pg_controldata.c
+++ b/src/bin/pg_controldata/pg_controldata.c
@@ -247,28 +247,27 @@ main(int argc, char *argv[])
ControlFile->checkPointCopy.PrevTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile->checkPointCopy.fullPageWrites ? _("on") : _("off"));
- printf(_("Latest checkpoint's NextXID: %u:%u\n"),
- EpochFromFullTransactionId(ControlFile->checkPointCopy.nextXid),
+ printf(_("Latest checkpoint's NextXID: " XID_FMT "\n"),
XidFromFullTransactionId(ControlFile->checkPointCopy.nextXid));
printf(_("Latest checkpoint's NextOID: %u\n"),
ControlFile->checkPointCopy.nextOid);
- printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
+ printf(_("Latest checkpoint's NextMultiXactId: " XID_FMT "\n"),
ControlFile->checkPointCopy.nextMulti);
- printf(_("Latest checkpoint's NextMultiOffset: %u\n"),
+ printf(_("Latest checkpoint's NextMultiOffset: " XID_FMT "\n"),
ControlFile->checkPointCopy.nextMultiOffset);
- printf(_("Latest checkpoint's oldestXID: %u\n"),
+ printf(_("Latest checkpoint's oldestXID: " XID_FMT "\n"),
ControlFile->checkPointCopy.oldestXid);
printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
ControlFile->checkPointCopy.oldestXidDB);
- printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
+ printf(_("Latest checkpoint's oldestActiveXID: " XID_FMT "\n"),
ControlFile->checkPointCopy.oldestActiveXid);
- printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
+ printf(_("Latest checkpoint's oldestMultiXid: " XID_FMT "\n"),
ControlFile->checkPointCopy.oldestMulti);
printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
ControlFile->checkPointCopy.oldestMultiDB);
- printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
+ printf(_("Latest checkpoint's oldestCommitTsXid:" XID_FMT "\n"),
ControlFile->checkPointCopy.oldestCommitTsXid);
- printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
+ printf(_("Latest checkpoint's newestCommitTsXid:" XID_FMT "\n"),
ControlFile->checkPointCopy.newestCommitTsXid);
printf(_("Time of latest checkpoint: %s\n"),
ckpttime_str);
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 7c2f1d30447..80881b6278c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -314,6 +314,7 @@ static void appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
static char *get_synchronized_snapshot(Archive *fout);
static void setupDumpWorker(Archive *AHX);
static TableInfo *getRootTableInfo(const TableInfo *tbinfo);
+static uint64 pg_strtouint64(const char *str, char **endptr, int base);
int
@@ -2773,7 +2774,7 @@ dumpDatabase(Archive *fout)
*datistemplate,
*datconnlimit,
*tablespace;
- uint32 frozenxid,
+ uint64 frozenxid,
minmxid;
char *qdatname;
@@ -2822,8 +2823,8 @@ dumpDatabase(Archive *fout)
encoding = PQgetvalue(res, 0, i_encoding);
collate = PQgetvalue(res, 0, i_collate);
ctype = PQgetvalue(res, 0, i_ctype);
- frozenxid = atooid(PQgetvalue(res, 0, i_frozenxid));
- minmxid = atooid(PQgetvalue(res, 0, i_minmxid));
+ frozenxid = pg_strtouint64(PQgetvalue(res, 0, i_frozenxid), NULL, 0);
+ minmxid = pg_strtouint64(PQgetvalue(res, 0, i_minmxid), NULL, 0);
dbdacl.acl = PQgetvalue(res, 0, i_datacl);
dbdacl.acldefault = PQgetvalue(res, 0, i_acldefault);
datistemplate = PQgetvalue(res, 0, i_datistemplate);
@@ -3008,7 +3009,7 @@ dumpDatabase(Archive *fout)
{
appendPQExpBufferStr(creaQry, "\n-- For binary upgrade, set datfrozenxid and datminmxid.\n");
appendPQExpBuffer(creaQry, "UPDATE pg_catalog.pg_database\n"
- "SET datfrozenxid = '%u', datminmxid = '%u'\n"
+ "SET datfrozenxid = '" XID_FMT "', datminmxid = '" XID_FMT "'\n"
"WHERE datname = ",
frozenxid, minmxid);
appendStringLiteralAH(creaQry, datname, fout);
@@ -3058,10 +3059,10 @@ dumpDatabase(Archive *fout)
appendPQExpBufferStr(loOutQry, "\n-- For binary upgrade, set pg_largeobject relfrozenxid and relminmxid\n");
appendPQExpBuffer(loOutQry, "UPDATE pg_catalog.pg_class\n"
- "SET relfrozenxid = '%u', relminmxid = '%u'\n"
+ "SET relfrozenxid = '%s', relminmxid = '%s'\n"
"WHERE oid = %u;\n",
- atooid(PQgetvalue(lo_res, 0, i_relfrozenxid)),
- atooid(PQgetvalue(lo_res, 0, i_relminmxid)),
+ (PQgetvalue(lo_res, 0, i_relfrozenxid)),
+ (PQgetvalue(lo_res, 0, i_relminmxid)),
LargeObjectRelationId);
ArchiveEntry(fout, nilCatalogId, createDumpId(),
ARCHIVE_OPTS(.tag = "pg_largeobject",
@@ -6242,11 +6243,11 @@ getTables(Archive *fout, int *numTables)
tblinfo[i].relreplident = *(PQgetvalue(res, i, i_relreplident));
tblinfo[i].rowsec = (strcmp(PQgetvalue(res, i, i_relrowsec), "t") == 0);
tblinfo[i].forcerowsec = (strcmp(PQgetvalue(res, i, i_relforcerowsec), "t") == 0);
- tblinfo[i].frozenxid = atooid(PQgetvalue(res, i, i_relfrozenxid));
- tblinfo[i].toast_frozenxid = atooid(PQgetvalue(res, i, i_toastfrozenxid));
+ tblinfo[i].frozenxid = pg_strtouint64(PQgetvalue(res, i, i_relfrozenxid), NULL, 0);
+ tblinfo[i].toast_frozenxid = pg_strtouint64(PQgetvalue(res, i, i_toastfrozenxid), NULL, 0);
tblinfo[i].toast_oid = atooid(PQgetvalue(res, i, i_toastoid));
- tblinfo[i].minmxid = atooid(PQgetvalue(res, i, i_relminmxid));
- tblinfo[i].toast_minmxid = atooid(PQgetvalue(res, i, i_toastminmxid));
+ tblinfo[i].minmxid = pg_strtouint64(PQgetvalue(res, i, i_relminmxid), NULL, 0);
+ tblinfo[i].toast_minmxid = pg_strtouint64(PQgetvalue(res, i, i_toastminmxid), NULL, 0);
tblinfo[i].reloptions = pg_strdup(PQgetvalue(res, i, i_reloptions));
if (PQgetisnull(res, i, i_checkoption))
tblinfo[i].checkoption = NULL;
@@ -15371,7 +15372,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
{
appendPQExpBufferStr(q, "\n-- For binary upgrade, set heap's relfrozenxid and relminmxid\n");
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
- "SET relfrozenxid = '%u', relminmxid = '%u'\n"
+ "SET relfrozenxid = '" XID_FMT "', relminmxid = '" XID_FMT "'\n"
"WHERE oid = ",
tbinfo->frozenxid, tbinfo->minmxid);
appendStringLiteralAH(q, qualrelname, fout);
@@ -15385,7 +15386,7 @@ dumpTableSchema(Archive *fout, const TableInfo *tbinfo)
*/
appendPQExpBufferStr(q, "\n-- For binary upgrade, set toast's relfrozenxid and relminmxid\n");
appendPQExpBuffer(q, "UPDATE pg_catalog.pg_class\n"
- "SET relfrozenxid = '%u', relminmxid = '%u'\n"
+ "SET relfrozenxid = '" XID_FMT "', relminmxid = '" XID_FMT "'\n"
"WHERE oid = '%u';\n",
tbinfo->toast_frozenxid,
tbinfo->toast_minmxid, tbinfo->toast_oid);
@@ -17946,3 +17947,27 @@ appendReloptionsArrayAH(PQExpBuffer buffer, const char *reloptions,
if (!res)
pg_log_warning("could not parse %s array", "reloptions");
}
+
+/*
+ * Copied from src/backend/utils/adt/numutils.c
+ *
+ * pg_strtouint64
+ * Converts 'str' into an unsigned 64-bit integer.
+ *
+ * This has the identical API to strtoul(3), except that it will handle
+ * 64-bit ints even where "long" is narrower than that.
+ *
+ * For the moment it seems sufficient to assume that the platform has
+ * such a function somewhere; let's not roll our own.
+ */
+static uint64
+pg_strtouint64(const char *str, char **endptr, int base)
+{
+#ifdef _MSC_VER /* MSVC only */
+ return _strtoui64(str, endptr, base);
+#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
+ return strtoull(str, endptr, base);
+#else
+ return strtoul(str, endptr, base);
+#endif
+}
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index 066a129ee5b..42da3aee822 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -298,11 +298,11 @@ typedef struct _tableInfo
bool rowsec; /* is row security enabled? */
bool forcerowsec; /* is row security forced? */
bool hasoids; /* does it have OIDs? */
- uint32 frozenxid; /* table's relfrozenxid */
- uint32 minmxid; /* table's relminmxid */
+ uint64 frozenxid; /* table's relfrozenxid */
+ uint64 minmxid; /* table's relminmxid */
Oid toast_oid; /* toast table's OID, or 0 if none */
- uint32 toast_frozenxid; /* toast table's relfrozenxid, if any */
- uint32 toast_minmxid; /* toast table's relminmxid */
+ uint64 toast_frozenxid; /* toast table's relfrozenxid, if any */
+ uint64 toast_minmxid; /* toast table's relminmxid */
int ncheck; /* # of CHECK expressions */
Oid reltype; /* OID of table's composite type, if any */
Oid reloftype; /* underlying type for typed table */
diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 1eb4509fcac..0bfe1a14615 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -63,7 +63,6 @@ static ControlFileData ControlFile; /* pg_control values */
static XLogSegNo newXlogSegNo; /* new XLOG segment # */
static bool guessed = false; /* T if we had to guess at any values */
static const char *progname;
-static uint32 set_xid_epoch = (uint32) -1;
static TransactionId set_oldest_xid = 0;
static TransactionId set_xid = 0;
static TransactionId set_oldest_commit_ts_xid = 0;
@@ -87,6 +86,7 @@ static void KillExistingXLOG(void);
static void KillExistingArchiveStatus(void);
static void WriteEmptyXLOG(void);
static void usage(void);
+static uint64 str2uint64(const char *str, char **endptr, int base);
int
@@ -95,7 +95,6 @@ main(int argc, char *argv[])
static struct option long_options[] = {
{"commit-timestamp-ids", required_argument, NULL, 'c'},
{"pgdata", required_argument, NULL, 'D'},
- {"epoch", required_argument, NULL, 'e'},
{"force", no_argument, NULL, 'f'},
{"next-wal-file", required_argument, NULL, 'l'},
{"multixact-ids", required_argument, NULL, 'm'},
@@ -137,7 +136,7 @@ main(int argc, char *argv[])
}
- while ((c = getopt_long(argc, argv, "c:D:e:fl:m:no:O:u:x:", long_options, NULL)) != -1)
+ while ((c = getopt_long(argc, argv, "c:D:fl:m:no:O:u:x:", long_options, NULL)) != -1)
{
switch (c)
{
@@ -153,27 +152,9 @@ main(int argc, char *argv[])
noupdate = true;
break;
- case 'e':
- errno = 0;
- set_xid_epoch = strtoul(optarg, &endptr, 0);
- if (endptr == optarg || *endptr != '\0' || errno != 0)
- {
- /*------
- translator: the second %s is a command line argument (-e, etc) */
- pg_log_error("invalid argument for option %s", "-e");
- fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
- exit(1);
- }
- if (set_xid_epoch == -1)
- {
- pg_log_error("transaction ID epoch (-e) must not be -1");
- exit(1);
- }
- break;
-
case 'u':
errno = 0;
- set_oldest_xid = strtoul(optarg, &endptr, 0);
+ set_oldest_xid = str2uint64(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-u");
@@ -182,14 +163,14 @@ main(int argc, char *argv[])
}
if (!TransactionIdIsNormal(set_oldest_xid))
{
- pg_log_error("oldest transaction ID (-u) must be greater than or equal to %u", FirstNormalTransactionId);
+ pg_log_error("oldest transaction ID (-u) must be greater than or equal to " XID_FMT, FirstNormalTransactionId);
exit(1);
}
break;
case 'x':
errno = 0;
- set_xid = strtoul(optarg, &endptr, 0);
+ set_xid = str2uint64(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-x");
@@ -198,21 +179,21 @@ main(int argc, char *argv[])
}
if (!TransactionIdIsNormal(set_xid))
{
- pg_log_error("transaction ID (-x) must be greater than or equal to %u", FirstNormalTransactionId);
+ pg_log_error("transaction ID (-x) must be greater than or equal to " XID_FMT, FirstNormalTransactionId);
exit(1);
}
break;
case 'c':
errno = 0;
- set_oldest_commit_ts_xid = strtoul(optarg, &endptr, 0);
+ set_oldest_commit_ts_xid = str2uint64(optarg, &endptr, 0);
if (endptr == optarg || *endptr != ',' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-c");
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
exit(1);
}
- set_newest_commit_ts_xid = strtoul(endptr + 1, &endptr2, 0);
+ set_newest_commit_ts_xid = str2uint64(endptr + 1, &endptr2, 0);
if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-c");
@@ -253,7 +234,7 @@ main(int argc, char *argv[])
case 'm':
errno = 0;
- set_mxid = strtoul(optarg, &endptr, 0);
+ set_mxid = str2uint64(optarg, &endptr, 0);
if (endptr == optarg || *endptr != ',' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-m");
@@ -261,7 +242,7 @@ main(int argc, char *argv[])
exit(1);
}
- set_oldestmxid = strtoul(endptr + 1, &endptr2, 0);
+ set_oldestmxid = str2uint64(endptr + 1, &endptr2, 0);
if (endptr2 == endptr + 1 || *endptr2 != '\0' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-m");
@@ -287,7 +268,7 @@ main(int argc, char *argv[])
case 'O':
errno = 0;
- set_mxoff = strtoul(optarg, &endptr, 0);
+ set_mxoff = str2uint64(optarg, &endptr, 0);
if (endptr == optarg || *endptr != '\0' || errno != 0)
{
pg_log_error("invalid argument for option %s", "-O");
@@ -448,11 +429,6 @@ main(int argc, char *argv[])
* Adjust fields if required by switches. (Do this now so that printout,
* if any, includes these values.)
*/
- if (set_xid_epoch != -1)
- ControlFile.checkPointCopy.nextXid =
- FullTransactionIdFromEpochAndXid(set_xid_epoch,
- XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
-
if (set_oldest_xid != 0)
{
ControlFile.checkPointCopy.oldestXid = set_oldest_xid;
@@ -460,9 +436,7 @@ main(int argc, char *argv[])
}
if (set_xid != 0)
- ControlFile.checkPointCopy.nextXid =
- FullTransactionIdFromEpochAndXid(EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
- set_xid);
+ ControlFile.checkPointCopy.nextXid = FullTransactionIdFromXid(set_xid);
if (set_oldest_commit_ts_xid != 0)
ControlFile.checkPointCopy.oldestCommitTsXid = set_oldest_commit_ts_xid;
@@ -702,7 +676,7 @@ GuessControlValues(void)
ControlFile.checkPointCopy.PrevTimeLineID = 1;
ControlFile.checkPointCopy.fullPageWrites = false;
ControlFile.checkPointCopy.nextXid =
- FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
+ FullTransactionIdFromXid(FirstNormalTransactionId);
ControlFile.checkPointCopy.nextOid = FirstGenbkiObjectId;
ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
ControlFile.checkPointCopy.nextMultiOffset = 0;
@@ -753,10 +727,17 @@ GuessControlValues(void)
*
* NB: this display should be just those fields that will not be
* reset by RewriteControlFile().
+ *
+ * Special macros help to make translatable strings.
*/
static void
PrintControlValues(bool guessed)
{
+ char xid_str[32];
+
+#define FORMAT_XID(val) \
+ (snprintf(xid_str, sizeof(xid_str), XID_FMT, (val)), xid_str)
+
if (guessed)
printf(_("Guessed pg_control values:\n\n"));
else
@@ -772,29 +753,28 @@ PrintControlValues(bool guessed)
ControlFile.checkPointCopy.ThisTimeLineID);
printf(_("Latest checkpoint's full_page_writes: %s\n"),
ControlFile.checkPointCopy.fullPageWrites ? _("on") : _("off"));
- printf(_("Latest checkpoint's NextXID: %u:%u\n"),
- EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid),
- XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
+ printf(_("Latest checkpoint's NextXID: %s\n"),
+ FORMAT_XID(XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid)));
printf(_("Latest checkpoint's NextOID: %u\n"),
ControlFile.checkPointCopy.nextOid);
- printf(_("Latest checkpoint's NextMultiXactId: %u\n"),
- ControlFile.checkPointCopy.nextMulti);
- printf(_("Latest checkpoint's NextMultiOffset: %u\n"),
+ printf(_("Latest checkpoint's NextMultiXactId: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.nextMulti));
+ printf(_("Latest checkpoint's NextMultiOffset: " INT64_FORMAT "\n"),
ControlFile.checkPointCopy.nextMultiOffset);
- printf(_("Latest checkpoint's oldestXID: %u\n"),
- ControlFile.checkPointCopy.oldestXid);
+ printf(_("Latest checkpoint's oldestXID: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestXid));
printf(_("Latest checkpoint's oldestXID's DB: %u\n"),
ControlFile.checkPointCopy.oldestXidDB);
- printf(_("Latest checkpoint's oldestActiveXID: %u\n"),
- ControlFile.checkPointCopy.oldestActiveXid);
- printf(_("Latest checkpoint's oldestMultiXid: %u\n"),
- ControlFile.checkPointCopy.oldestMulti);
+ printf(_("Latest checkpoint's oldestActiveXID: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestActiveXid));
+ printf(_("Latest checkpoint's oldestMultiXid: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestMulti));
printf(_("Latest checkpoint's oldestMulti's DB: %u\n"),
ControlFile.checkPointCopy.oldestMultiDB);
- printf(_("Latest checkpoint's oldestCommitTsXid:%u\n"),
- ControlFile.checkPointCopy.oldestCommitTsXid);
- printf(_("Latest checkpoint's newestCommitTsXid:%u\n"),
- ControlFile.checkPointCopy.newestCommitTsXid);
+ printf(_("Latest checkpoint's oldestCommitTsXid:%s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestCommitTsXid));
+ printf(_("Latest checkpoint's newestCommitTsXid:%s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.newestCommitTsXid));
printf(_("Maximum data alignment: %u\n"),
ControlFile.maxAlign);
/* we don't print floatFormat since can't say much useful about it */
@@ -830,6 +810,7 @@ PrintControlValues(bool guessed)
static void
PrintNewControlValues(void)
{
+ char xid_str[32];
char fname[MAXFNAMELEN];
/* This will be always printed in order to keep format same. */
@@ -841,18 +822,18 @@ PrintNewControlValues(void)
if (set_mxid != 0)
{
- printf(_("NextMultiXactId: %u\n"),
- ControlFile.checkPointCopy.nextMulti);
- printf(_("OldestMultiXid: %u\n"),
- ControlFile.checkPointCopy.oldestMulti);
+ printf(_("NextMultiXactId: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.nextMulti));
+ printf(_("OldestMultiXid: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestMulti));
printf(_("OldestMulti's DB: %u\n"),
ControlFile.checkPointCopy.oldestMultiDB);
}
if (set_mxoff != -1)
{
- printf(_("NextMultiOffset: %u\n"),
- ControlFile.checkPointCopy.nextMultiOffset);
+ printf(_("NextMultiOffset: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.nextMultiOffset));
}
if (set_oid != 0)
@@ -863,29 +844,23 @@ PrintNewControlValues(void)
if (set_xid != 0)
{
- printf(_("NextXID: %u\n"),
- XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
- printf(_("OldestXID: %u\n"),
- ControlFile.checkPointCopy.oldestXid);
+ printf(_("NextXID: %s\n"),
+ FORMAT_XID(XidFromFullTransactionId(ControlFile.checkPointCopy.nextXid)));
+ printf(_("OldestXID: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestXid));
printf(_("OldestXID's DB: %u\n"),
ControlFile.checkPointCopy.oldestXidDB);
}
- if (set_xid_epoch != -1)
- {
- printf(_("NextXID epoch: %u\n"),
- EpochFromFullTransactionId(ControlFile.checkPointCopy.nextXid));
- }
-
if (set_oldest_commit_ts_xid != 0)
{
- printf(_("oldestCommitTsXid: %u\n"),
- ControlFile.checkPointCopy.oldestCommitTsXid);
+ printf(_("oldestCommitTsXid: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.oldestCommitTsXid));
}
if (set_newest_commit_ts_xid != 0)
{
- printf(_("newestCommitTsXid: %u\n"),
- ControlFile.checkPointCopy.newestCommitTsXid);
+ printf(_("newestCommitTsXid: %s\n"),
+ FORMAT_XID(ControlFile.checkPointCopy.newestCommitTsXid));
}
if (set_wal_segsize != 0)
@@ -1228,7 +1203,6 @@ usage(void)
" set oldest and newest transactions bearing\n"
" commit timestamp (zero means no change)\n"));
printf(_(" [-D, --pgdata=]DATADIR data directory\n"));
- printf(_(" -e, --epoch=XIDEPOCH set next transaction ID epoch\n"));
printf(_(" -f, --force force update to be done\n"));
printf(_(" -l, --next-wal-file=WALFILE set minimum starting location for new WAL\n"));
printf(_(" -m, --multixact-ids=MXID,MXID set next and oldest multitransaction ID\n"));
@@ -1243,3 +1217,20 @@ usage(void)
printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
}
+
+/*
+ * str2uint64()
+ *
+ * convert string to 64-bit unsigned int
+ */
+static uint64
+str2uint64(const char *str, char **endptr, int base)
+{
+#ifdef _MSC_VER /* MSVC only */
+ return _strtoui64(str, endptr, base);
+#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
+ return strtoull(str, endptr, base);
+#else
+ return strtoul(str, endptr, base);
+#endif
+}
diff --git a/src/bin/pg_upgrade/Makefile b/src/bin/pg_upgrade/Makefile
index 44d06be5a61..77bf5fc955d 100644
--- a/src/bin/pg_upgrade/Makefile
+++ b/src/bin/pg_upgrade/Makefile
@@ -20,6 +20,7 @@ OBJS = \
parallel.o \
pg_upgrade.o \
relfilenode.o \
+ segresize.o \
server.o \
tablespace.o \
util.o \
diff --git a/src/bin/pg_upgrade/check.c b/src/bin/pg_upgrade/check.c
index 3d218c2ad24..df6d0f5fc9b 100644
--- a/src/bin/pg_upgrade/check.c
+++ b/src/bin/pg_upgrade/check.c
@@ -31,6 +31,7 @@ static void check_for_pg_role_prefix(ClusterInfo *cluster);
static void check_for_new_tablespace_dir(ClusterInfo *new_cluster);
static void check_for_user_defined_encoding_conversions(ClusterInfo *cluster);
static char *get_canonical_locale_name(int category, const char *locale);
+static void check_for_32bit_xid_usage(ClusterInfo *cluster);
/*
@@ -159,6 +160,17 @@ check_and_dump_old_cluster(bool live_check)
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 903)
old_9_3_check_for_line_data_type_usage(&old_cluster);
+ /* Prepare for 64bit xid */
+ if (!ALREADY_64bit_XID(old_cluster))
+ {
+ /* Check if 32-bit xid type is used in tables */
+ check_for_32bit_xid_usage(&old_cluster);
+ /* Check indexes to be upgraded */
+ invalidate_spgist_indexes(&old_cluster, true);
+ invalidate_gin_indexes(&old_cluster, true);
+ invalidate_external_indexes(&old_cluster, true);
+ }
+
/*
* While not a check option, we do this now because this is the only time
* the old server is running.
@@ -233,6 +245,17 @@ issue_warnings_and_set_wal_level(void)
if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906)
old_9_6_invalidate_hash_indexes(&new_cluster, false);
+ /* Raindex for 64bit xid */
+ if (!ALREADY_64bit_XID(old_cluster))
+ {
+ /* Check if 32-bit xid type is used in tables */
+ check_for_32bit_xid_usage(&old_cluster);
+ /* Check indexes to be upgraded */
+ invalidate_spgist_indexes(&old_cluster, true);
+ invalidate_gin_indexes(&old_cluster, true);
+ invalidate_external_indexes(&old_cluster, true);
+ }
+
report_extension_updates(&new_cluster);
stop_postmaster(false);
@@ -1323,3 +1346,94 @@ get_canonical_locale_name(int category, const char *locale)
return res;
}
+
+/*
+ * check_for_32bit_xid_usage()
+ *
+ * Postgres Pro Enterprise changes xid storage format to 64-bit. Check if
+ * xid type is used in tables.
+ */
+static void
+check_for_32bit_xid_usage(ClusterInfo *cluster)
+{
+ int dbnum;
+ FILE *script = NULL;
+ bool found = false;
+ char output_path[MAXPGPATH];
+
+ prep_status("Checking for incompatible \"xid\" data type");
+
+ snprintf(output_path, sizeof(output_path), "tables_using_xid.txt");
+
+ for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
+ {
+ PGresult *res;
+ bool db_used = false;
+ int ntups;
+ int rowno;
+ int i_nspname,
+ i_relname,
+ i_attname;
+ DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
+ PGconn *conn = connectToServer(cluster, active_db->db_name);
+
+ /*
+ * While several relkinds don't store any data, e.g. views, they can
+ * be used to define data types of other columns, so we check all
+ * relkinds.
+ */
+ res = executeQueryOrDie(conn,
+ "SELECT n.nspname, c.relname, a.attname "
+ "FROM pg_catalog.pg_class c, "
+ " pg_catalog.pg_namespace n, "
+ " pg_catalog.pg_attribute a "
+ "WHERE c.oid = a.attrelid AND "
+ " a.attnum >= 1 AND "
+ " a.atttypid = 'pg_catalog.xid'::pg_catalog.regtype AND "
+ " c.relnamespace = n.oid AND "
+ /* exclude possible orphaned temp tables */
+ " n.nspname !~ '^pg_temp_' AND "
+ " n.nspname NOT IN ('pg_catalog', 'information_schema')");
+
+ ntups = PQntuples(res);
+ i_nspname = PQfnumber(res, "nspname");
+ i_relname = PQfnumber(res, "relname");
+ i_attname = PQfnumber(res, "attname");
+ for (rowno = 0; rowno < ntups; rowno++)
+ {
+ found = true;
+ if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
+ pg_fatal("could not open file \"%s\": %s\n",
+ output_path, strerror(errno));
+ if (!db_used)
+ {
+ fprintf(script, "Database: %s\n", active_db->db_name);
+ db_used = true;
+ }
+ fprintf(script, " %s.%s.%s\n",
+ PQgetvalue(res, rowno, i_nspname),
+ PQgetvalue(res, rowno, i_relname),
+ PQgetvalue(res, rowno, i_attname));
+ }
+
+ PQclear(res);
+
+ PQfinish(conn);
+ }
+
+ if (script)
+ fclose(script);
+
+ if (found)
+ {
+ pg_log(PG_REPORT, "fatal\n");
+ pg_fatal("Your installation contains the \"xid\" data type in user tables.\n"
+ "The internal format of \"xid\" changed in Postgres Pro Enterprise so this cluster\n"
+ "cannot currently be upgraded. Note that even dropped attributes cause a problem.\n"
+ "You can remove the problem tables and restart the upgrade.\n"
+ "A list of the problem columns is in the file:\n"
+ " %s\n\n", output_path);
+ }
+ else
+ check_ok();
+}
\ No newline at end of file
diff --git a/src/bin/pg_upgrade/controldata.c b/src/bin/pg_upgrade/controldata.c
index 41b8f69b8cb..09e6efddd3f 100644
--- a/src/bin/pg_upgrade/controldata.c
+++ b/src/bin/pg_upgrade/controldata.c
@@ -269,9 +269,11 @@ get_control_data(ClusterInfo *cluster, bool live_check)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.chkpnt_nxtepoch = str2uint(p);
+ cluster->controldata.chkpnt_nxtxid = str2uint64(p);
/*
+ * Try to read 32-bit XID format 'epoch:xid'.
+ *
* Delimiter changed from '/' to ':' in 9.6. We don't test for
* the catalog version of the change because the catalog version
* is pulled from pg_controldata too, and it isn't worth adding an
@@ -284,11 +286,19 @@ get_control_data(ClusterInfo *cluster, bool live_check)
else
p = NULL;
- if (p == NULL || strlen(p) <= 1)
- pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+ if (p)
+ {
+ /* Read lowest 32 bits of xid, epoch was read previously */
+ if (strlen(p) <= 1)
+ pg_fatal("%d: controldata retrieval problem\n", __LINE__);
+
+ p++; /* remove '/' or ':' char */
+
+ Assert((cluster->controldata.chkpnt_nxtxid >> 32) == 0);
+ cluster->controldata.chkpnt_nxtxid <<= 32;
+ cluster->controldata.chkpnt_nxtxid |= str2uint(p);
+ }
- p++; /* remove '/' or ':' char */
- cluster->controldata.chkpnt_nxtxid = str2uint(p);
got_xid = true;
}
else if ((p = strstr(bufin, "Latest checkpoint's NextOID:")) != NULL)
@@ -310,7 +320,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.chkpnt_nxtmulti = str2uint(p);
+ cluster->controldata.chkpnt_nxtmulti = str2uint64(p);
got_multi = true;
}
else if ((p = strstr(bufin, "Latest checkpoint's oldestXID:")) != NULL)
@@ -321,7 +331,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.chkpnt_oldstxid = str2uint(p);
+ cluster->controldata.chkpnt_oldstxid = str2uint64(p);
got_oldestxid = true;
}
else if ((p = strstr(bufin, "Latest checkpoint's oldestMultiXid:")) != NULL)
@@ -332,7 +342,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.chkpnt_oldstMulti = str2uint(p);
+ cluster->controldata.chkpnt_oldstMulti = str2uint64(p);
got_oldestmulti = true;
}
else if ((p = strstr(bufin, "Latest checkpoint's NextMultiOffset:")) != NULL)
@@ -343,7 +353,7 @@ get_control_data(ClusterInfo *cluster, bool live_check)
pg_fatal("%d: controldata retrieval problem\n", __LINE__);
p++; /* remove ':' char */
- cluster->controldata.chkpnt_nxtmxoff = str2uint(p);
+ cluster->controldata.chkpnt_nxtmxoff = str2uint64(p);
got_mxoff = true;
}
else if ((p = strstr(bufin, "First log segment after reset:")) != NULL)
diff --git a/src/bin/pg_upgrade/file.c b/src/bin/pg_upgrade/file.c
index b84868c7510..4ae6a40ae97 100644
--- a/src/bin/pg_upgrade/file.c
+++ b/src/bin/pg_upgrade/file.c
@@ -25,6 +25,7 @@
#include "storage/bufpage.h"
#include "storage/checksum.h"
#include "storage/checksum_impl.h"
+#include "storage/fsm_internals.h"
/*
@@ -152,6 +153,112 @@ linkFile(const char *src, const char *dst,
schemaName, relName, src, dst, strerror(errno));
}
+/* Context for file rewriting */
+typedef struct FileRewriteContext
+{
+ const char *fromfile;
+ const char *tofile;
+ const char *schemaName;
+ const char *relName;
+ int src_fd;
+ int dst_fd;
+ ssize_t src_filesize;
+ ssize_t totalBytesRead;
+ BlockNumber last_blkno;
+ bool old_lastblk;
+} FileRewriteContext;
+
+/* Initialize context for file rewriting */
+static void
+rewriteFileInit(FileRewriteContext *cxt,
+ const char *fromfile, const char *tofile,
+ const char *schemaName, const char *relName)
+{
+ struct stat statbuf;
+
+ cxt->fromfile = fromfile;
+ cxt->tofile = tofile;
+ cxt->schemaName = schemaName;
+ cxt->relName = relName;
+ cxt->totalBytesRead = 0;
+ cxt->last_blkno = InvalidBlockNumber;
+ cxt->old_lastblk = false;
+
+ /* Open old and new files */
+ if ((cxt->src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
+ pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s\n",
+ schemaName, relName, fromfile, strerror(errno));
+
+ if (fstat(cxt->src_fd, &statbuf) != 0)
+ pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %s\n",
+ schemaName, relName, fromfile, strerror(errno));
+
+ if ((cxt->dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
+ pg_file_create_mode)) < 0)
+ pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %s\n",
+ schemaName, relName, tofile, strerror(errno));
+
+ /* Save old file size */
+ cxt->src_filesize = statbuf.st_size;
+}
+
+/* Clean up file rewriting context */
+static void
+rewriteFileCleanup(FileRewriteContext *cxt)
+{
+ close(cxt->dst_fd);
+ close(cxt->src_fd);
+}
+
+/* Read old page of the rewritten file */
+static ssize_t
+rewriteFileReadPage(FileRewriteContext *cxt, Page page)
+{
+ ssize_t bytesRead;
+
+ if (cxt->totalBytesRead >= cxt->src_filesize)
+ return 0;
+
+ if ((bytesRead = read(cxt->src_fd, page, BLCKSZ)) != BLCKSZ)
+ {
+ if (bytesRead < 0)
+ pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s\n",
+ cxt->schemaName, cxt->relName, cxt->fromfile, strerror(errno));
+ else
+ pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"\n",
+ cxt->schemaName, cxt->relName, cxt->fromfile);
+ }
+
+ cxt->totalBytesRead += BLCKSZ;
+ cxt->old_lastblk = (cxt->totalBytesRead == cxt->src_filesize);
+
+ return bytesRead;
+}
+
+/* Write new page of the rewritten file */
+static void
+rewriteFileWritePage(FileRewriteContext *cxt, Page page, BlockNumber blkno)
+{
+ /* Set new checksum for page, if enabled */
+ if (new_cluster.controldata.data_checksum_version != 0)
+ ((PageHeader) page)->pd_checksum = pg_checksum_page(page, blkno);
+
+ /* Write page */
+ errno = 0;
+
+ if ((blkno != (cxt->last_blkno == InvalidBlockNumber ? 0 : cxt->last_blkno + 1) &&
+ lseek(cxt->dst_fd, (off_t) BLCKSZ * blkno, SEEK_SET) != (off_t) BLCKSZ * blkno) ||
+ write(cxt->dst_fd, page, BLCKSZ) != BLCKSZ)
+ {
+ /* if write didn't set errno, assume problem is no disk space */
+ if (errno == 0)
+ errno = ENOSPC;
+ pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s\n",
+ cxt->schemaName, cxt->relName, cxt->tofile, strerror(errno));
+ }
+
+ cxt->last_blkno = blkno;
+}
/*
* rewriteVisibilityMap()
@@ -171,36 +278,19 @@ linkFile(const char *src, const char *dst,
*/
void
rewriteVisibilityMap(const char *fromfile, const char *tofile,
- const char *schemaName, const char *relName)
+ const char *schemaName, const char *relName)
{
- int src_fd;
- int dst_fd;
+ FileRewriteContext cxt;
PGAlignedBlock buffer;
PGAlignedBlock new_vmbuf;
- ssize_t totalBytesRead = 0;
- ssize_t src_filesize;
int rewriteVmBytesPerPage;
BlockNumber new_blkno = 0;
- struct stat statbuf;
+ ssize_t bytesRead;
/* Compute number of old-format bytes per new page */
rewriteVmBytesPerPage = (BLCKSZ - SizeOfPageHeaderData) / 2;
- if ((src_fd = open(fromfile, O_RDONLY | PG_BINARY, 0)) < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not open file \"%s\": %s\n",
- schemaName, relName, fromfile, strerror(errno));
-
- if (fstat(src_fd, &statbuf) != 0)
- pg_fatal("error while copying relation \"%s.%s\": could not stat file \"%s\": %s\n",
- schemaName, relName, fromfile, strerror(errno));
-
- if ((dst_fd = open(tofile, O_RDWR | O_CREAT | O_EXCL | PG_BINARY,
- pg_file_create_mode)) < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not create file \"%s\": %s\n",
- schemaName, relName, tofile, strerror(errno));
-
- /* Save old file size */
- src_filesize = statbuf.st_size;
+ rewriteFileInit(&cxt, fromfile, tofile, schemaName, relName);
/*
* Turn each visibility map page into 2 pages one by one. Each new page
@@ -208,27 +298,12 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
* last page is empty, we skip it, mostly to avoid turning one-page
* visibility maps for small relations into two pages needlessly.
*/
- while (totalBytesRead < src_filesize)
+ while ((bytesRead = rewriteFileReadPage(&cxt, buffer.data)) > 0)
{
- ssize_t bytesRead;
char *old_cur;
char *old_break;
char *old_blkend;
PageHeaderData pageheader;
- bool old_lastblk;
-
- if ((bytesRead = read(src_fd, buffer.data, BLCKSZ)) != BLCKSZ)
- {
- if (bytesRead < 0)
- pg_fatal("error while copying relation \"%s.%s\": could not read file \"%s\": %s\n",
- schemaName, relName, fromfile, strerror(errno));
- else
- pg_fatal("error while copying relation \"%s.%s\": partial page found in file \"%s\"\n",
- schemaName, relName, fromfile);
- }
-
- totalBytesRead += BLCKSZ;
- old_lastblk = (totalBytesRead == src_filesize);
/* Save the page header data */
memcpy(&pageheader, buffer.data, SizeOfPageHeaderData);
@@ -253,7 +328,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
memcpy(new_vmbuf.data, &pageheader, SizeOfPageHeaderData);
/* Rewriting the last part of the last old page? */
- old_lastpart = old_lastblk && (old_break == old_blkend);
+ old_lastpart = cxt.old_lastblk && (old_break == old_blkend);
new_cur = new_vmbuf.data + SizeOfPageHeaderData;
@@ -287,20 +362,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
if (old_lastpart && empty)
break;
- /* Set new checksum for visibility map page, if enabled */
- if (new_cluster.controldata.data_checksum_version != 0)
- ((PageHeader) new_vmbuf.data)->pd_checksum =
- pg_checksum_page(new_vmbuf.data, new_blkno);
-
- errno = 0;
- if (write(dst_fd, new_vmbuf.data, BLCKSZ) != BLCKSZ)
- {
- /* if write didn't set errno, assume problem is no disk space */
- if (errno == 0)
- errno = ENOSPC;
- pg_fatal("error while copying relation \"%s.%s\": could not write file \"%s\": %s\n",
- schemaName, relName, tofile, strerror(errno));
- }
+ rewriteFileWritePage(&cxt, new_vmbuf.data, new_blkno);
/* Advance for next new page */
old_break += rewriteVmBytesPerPage;
@@ -308,9 +370,7 @@ rewriteVisibilityMap(const char *fromfile, const char *tofile,
}
}
- /* Clean up */
- close(dst_fd);
- close(src_fd);
+ rewriteFileCleanup(&cxt);
}
void
@@ -371,4 +431,4 @@ check_hard_link(void)
strerror(errno));
unlink(new_link_file);
-}
+}
\ No newline at end of file
diff --git a/src/bin/pg_upgrade/pg_upgrade.c b/src/bin/pg_upgrade/pg_upgrade.c
index 07defacd673..bbf83c3af87 100644
--- a/src/bin/pg_upgrade/pg_upgrade.c
+++ b/src/bin/pg_upgrade/pg_upgrade.c
@@ -42,6 +42,9 @@
#include <langinfo.h>
#endif
+#include "access/multixact.h"
+#include "access/transam.h"
+#include "access/xlog_internal.h"
#include "catalog/pg_class_d.h"
#include "common/file_perm.h"
#include "common/logging.h"
@@ -261,7 +264,6 @@ setup(char *argv0, bool *live_check)
}
}
-
static void
prepare_new_cluster(void)
{
@@ -404,11 +406,10 @@ create_new_objects(void)
check_ok();
/*
- * We don't have minmxids for databases or relations in pre-9.3 clusters,
- * so set those after we have restored the schema.
+ * Refix datfrozenxid and datminmxid
*/
- if (GET_MAJOR_VERSION(old_cluster.major_version) <= 902)
- set_frozenxids(true);
+ if (ALREADY_64bit_XID(old_cluster) != ALREADY_64bit_XID(new_cluster))
+ set_frozenxids(false);
/* update new_cluster info now that we have objects in the databases */
get_db_and_rel_infos(&new_cluster);
@@ -462,18 +463,36 @@ copy_subdir_files(const char *old_subdir, const char *new_subdir)
static void
copy_xact_xlog_xid(void)
{
- /*
- * Copy old commit logs to new data dir. pg_clog has been renamed to
- * pg_xact in post-10 clusters.
- */
- copy_subdir_files(GET_MAJOR_VERSION(old_cluster.major_version) <= 906 ?
- "pg_clog" : "pg_xact",
- GET_MAJOR_VERSION(new_cluster.major_version) <= 906 ?
- "pg_clog" : "pg_xact");
+ TransactionId next_xid;
+
+#define GetClogDirName(cluster) \
+ GET_MAJOR_VERSION(cluster.major_version) <= 906 ? "pg_clog" : "pg_xact"
+
+ /* Set next xid to 2^32 if we're upgrading from 32 bit postgres */
+ next_xid = ALREADY_64bit_XID(old_cluster) == ALREADY_64bit_XID(new_cluster) ?
+ old_cluster.controldata.chkpnt_nxtxid :
+ FirstUpgradedTransactionId;
+
+ if (ALREADY_64bit_XID(old_cluster) == ALREADY_64bit_XID(new_cluster))
+ {
+ /*
+ * Copy old commit logs to new data dir. pg_clog has been renamed to
+ * pg_xact in post-10 clusters.
+ */
+ copy_subdir_files(GetClogDirName(old_cluster), GetClogDirName(new_cluster));
+ }
+ else
+ {
+ /* Convert commit logs and copy to the new data dir */
+ prep_status("Transforming commit log segments");
+ convert_clog(psprintf("%s/%s", old_cluster.pgdata, GetClogDirName(old_cluster)),
+ psprintf("%s/%s", new_cluster.pgdata, GetClogDirName(new_cluster)));
+ check_ok();
+ }
prep_status("Setting oldest XID for new cluster");
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
- "\"%s/pg_resetwal\" -f -u %u \"%s\"",
+ "\"%s/pg_resetwal\" -f -u " XID_FMT " \"%s\"",
new_cluster.bindir, old_cluster.controldata.chkpnt_oldstxid,
new_cluster.pgdata);
check_ok();
@@ -481,19 +500,21 @@ copy_xact_xlog_xid(void)
/* set the next transaction id and epoch of the new cluster */
prep_status("Setting next transaction ID and epoch for new cluster");
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
- "\"%s/pg_resetwal\" -f -x %u \"%s\"",
- new_cluster.bindir, old_cluster.controldata.chkpnt_nxtxid,
+ "\"%s/pg_resetwal\" -f -x " XID_FMT " \"%s\"",
+ new_cluster.bindir, next_xid,
new_cluster.pgdata);
+#ifdef NOT_USED
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
"\"%s/pg_resetwal\" -f -e %u \"%s\"",
new_cluster.bindir, old_cluster.controldata.chkpnt_nxtepoch,
new_cluster.pgdata);
+#endif
/* must reset commit timestamp limits also */
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
- "\"%s/pg_resetwal\" -f -c %u,%u \"%s\"",
+ "\"%s/pg_resetwal\" -f -c " XID_FMT "," XID_FMT " \"%s\"",
new_cluster.bindir,
- old_cluster.controldata.chkpnt_nxtxid,
- old_cluster.controldata.chkpnt_nxtxid,
+ next_xid,
+ next_xid,
new_cluster.pgdata);
check_ok();
@@ -506,8 +527,45 @@ copy_xact_xlog_xid(void)
if (old_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER &&
new_cluster.controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER)
{
- copy_subdir_files("pg_multixact/offsets", "pg_multixact/offsets");
- copy_subdir_files("pg_multixact/members", "pg_multixact/members");
+ uint64 oldest_mxid = old_cluster.controldata.chkpnt_oldstMulti;
+ uint64 next_mxid = old_cluster.controldata.chkpnt_nxtmulti;
+ uint64 next_mxoff = old_cluster.controldata.chkpnt_nxtmxoff;
+
+ if (ALREADY_64bit_XID(old_cluster))
+ {
+ copy_subdir_files("pg_multixact/offsets", "pg_multixact/offsets");
+ copy_subdir_files("pg_multixact/members", "pg_multixact/members");
+ }
+ else
+ {
+ MultiXactOffset oldest_mxoff;
+
+ remove_new_subdir("pg_multixact/offsets", false);
+ oldest_mxoff = convert_multixact_offsets("pg_multixact/offsets", "pg_multixact/offsets");
+
+ remove_new_subdir("pg_multixact/members", false);
+ convert_multixact_members("pg_multixact/members", "pg_multixact/members", oldest_mxoff);
+
+ /*
+ * Handle wraparound if we're upgrading from 32 bit postgres.
+ * Invalid 0 mxids/offsets are skipped, so 1 becomes 2^32.
+ */
+ if (oldest_mxoff)
+ {
+ if (next_mxid < oldest_mxid)
+ next_mxid += ((uint64) 1 << 32) - FirstMultiXactId;
+
+ if (next_mxoff < oldest_mxoff)
+ next_mxoff += ((uint64) 1 << 32) - 1;
+
+ /* Offsets and members were rewritten, oldest_mxoff = 1 */
+ next_mxoff -= oldest_mxoff - 1;
+ oldest_mxoff = 1;
+
+ /* Save converted next_mxid for possible usage in set_frozenxids() */
+ old_cluster.controldata.chkpnt_nxtmulti = next_mxid;
+ }
+ }
prep_status("Setting next multixact ID and offset for new cluster");
@@ -516,11 +574,9 @@ copy_xact_xlog_xid(void)
* counters here and the oldest multi present on system.
*/
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
- "\"%s/pg_resetwal\" -O %u -m %u,%u \"%s\"",
+ "\"%s/pg_resetwal\" -O " XID_FMT " -m " XID_FMT "," XID_FMT " \"%s\"",
new_cluster.bindir,
- old_cluster.controldata.chkpnt_nxtmxoff,
- old_cluster.controldata.chkpnt_nxtmulti,
- old_cluster.controldata.chkpnt_oldstMulti,
+ next_mxoff, next_mxid, oldest_mxid,
new_cluster.pgdata);
check_ok();
}
@@ -544,7 +600,7 @@ copy_xact_xlog_xid(void)
* next=MaxMultiXactId, but multixact.c can cope with that just fine.
*/
exec_prog(UTILITY_LOG_FILE, NULL, true, true,
- "\"%s/pg_resetwal\" -m %u,%u \"%s\"",
+ "\"%s/pg_resetwal\" -m " XID_FMT "," XID_FMT " \"%s\"",
new_cluster.bindir,
old_cluster.controldata.chkpnt_nxtmulti + 1,
old_cluster.controldata.chkpnt_nxtmulti,
@@ -594,6 +650,8 @@ set_frozenxids(bool minmxid_only)
int ntups;
int i_datname;
int i_datallowconn;
+ TransactionId frozen_xid;
+ MultiXactId minmxid;
if (!minmxid_only)
prep_status("Setting frozenxid and minmxid counters in new cluster");
@@ -602,18 +660,24 @@ set_frozenxids(bool minmxid_only)
conn_template1 = connectToServer(&new_cluster, "template1");
+ frozen_xid = ALREADY_64bit_XID(old_cluster) == ALREADY_64bit_XID(new_cluster) ?
+ old_cluster.controldata.chkpnt_nxtxid :
+ FirstNormalTransactionId;
+
+ minmxid = old_cluster.controldata.chkpnt_nxtmulti;
+
if (!minmxid_only)
/* set pg_database.datfrozenxid */
PQclear(executeQueryOrDie(conn_template1,
"UPDATE pg_catalog.pg_database "
- "SET datfrozenxid = '%u'",
- old_cluster.controldata.chkpnt_nxtxid));
+ "SET datfrozenxid = '" XID_FMT "'",
+ frozen_xid));
/* set pg_database.datminmxid */
PQclear(executeQueryOrDie(conn_template1,
"UPDATE pg_catalog.pg_database "
- "SET datminmxid = '%u'",
- old_cluster.controldata.chkpnt_nxtmulti));
+ "SET datminmxid = '" XID_FMT "'",
+ minmxid));
/* get database names */
dbres = executeQueryOrDie(conn_template1,
@@ -647,24 +711,24 @@ set_frozenxids(bool minmxid_only)
/* set pg_class.relfrozenxid */
PQclear(executeQueryOrDie(conn,
"UPDATE pg_catalog.pg_class "
- "SET relfrozenxid = '%u' "
+ "SET relfrozenxid = '" XID_FMT "' "
/* only heap, materialized view, and TOAST are vacuumed */
"WHERE relkind IN ("
CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_TOASTVALUE) ")",
- old_cluster.controldata.chkpnt_nxtxid));
+ frozen_xid));
/* set pg_class.relminmxid */
PQclear(executeQueryOrDie(conn,
"UPDATE pg_catalog.pg_class "
- "SET relminmxid = '%u' "
+ "SET relminmxid = '" XID_FMT "' "
/* only heap, materialized view, and TOAST are vacuumed */
"WHERE relkind IN ("
CppAsString2(RELKIND_RELATION) ", "
CppAsString2(RELKIND_MATVIEW) ", "
CppAsString2(RELKIND_TOASTVALUE) ")",
- old_cluster.controldata.chkpnt_nxtmulti));
+ minmxid));
PQfinish(conn);
/* Reset datallowconn flag */
diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index da6770d0f83..1701d7bb672 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -21,6 +21,7 @@
#define MESSAGE_WIDTH 60
#define GET_MAJOR_VERSION(v) ((v) / 100)
+#define ALREADY_64bit_XID(cluster) (GET_MAJOR_VERSION((cluster).major_version) >= 1500)
/* contains both global db information and CREATE DATABASE commands */
#define GLOBALS_DUMP_FILE "pg_upgrade_dump_globals.sql"
@@ -184,13 +185,13 @@ typedef struct
uint32 ctrl_ver;
uint32 cat_ver;
char nextxlogfile[25];
- uint32 chkpnt_nxtxid;
- uint32 chkpnt_nxtepoch;
+ uint64 chkpnt_nxtxid;
+ uint32 chkpnt_nxtepoch; /* for 32bit xids only */
uint32 chkpnt_nxtoid;
- uint32 chkpnt_nxtmulti;
- uint32 chkpnt_nxtmxoff;
- uint32 chkpnt_oldstMulti;
- uint32 chkpnt_oldstxid;
+ uint64 chkpnt_nxtmulti;
+ uint64 chkpnt_nxtmxoff;
+ uint64 chkpnt_oldstMulti;
+ uint64 chkpnt_oldstxid;
uint32 align;
uint32 blocksz;
uint32 largesz;
@@ -418,6 +419,7 @@ void end_progress_output(void);
void prep_status(const char *fmt,...) pg_attribute_printf(1, 2);
void check_ok(void);
unsigned int str2uint(const char *str);
+uint64 str2uint64(const char *str);
/* version.c */
@@ -436,6 +438,10 @@ void old_9_6_invalidate_hash_indexes(ClusterInfo *cluster,
void old_11_check_for_sql_identifier_data_type_usage(ClusterInfo *cluster);
void report_extension_updates(ClusterInfo *cluster);
+void invalidate_spgist_indexes(ClusterInfo *cluster, bool check_mode);
+void invalidate_gin_indexes(ClusterInfo *cluster, bool check_mode);
+void invalidate_external_indexes(ClusterInfo *cluster, bool check_mode);
+
/* parallel.c */
void parallel_exec_prog(const char *log_file, const char *opt_log_file,
const char *fmt,...) pg_attribute_printf(3, 4);
@@ -443,3 +449,9 @@ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr
char *old_pgdata, char *new_pgdata,
char *old_tablespace);
bool reap_child(bool wait_for_child);
+
+/* segresize.c */
+void convert_clog(const char *olddir, const char *newdir);
+MultiXactOffset convert_multixact_offsets(const char *olddir, const char *newdir);
+void convert_multixact_members(const char *olddir, const char *newdir,
+ MultiXactOffset oldest_mxoff);
\ No newline at end of file
diff --git a/src/bin/pg_upgrade/segresize.c b/src/bin/pg_upgrade/segresize.c
new file mode 100644
index 00000000000..dc3eb1f5915
--- /dev/null
+++ b/src/bin/pg_upgrade/segresize.c
@@ -0,0 +1,571 @@
+/*
+ * segresize.c
+ * Segment resize utility
+ *
+ * Copyright (c) 2015-2016, Postgres Professional
+ * src/bin/pg_upgrade/segresize.c
+ */
+
+
+#include "postgres_fe.h"
+
+#include "pg_upgrade.h"
+#include "access/multixact.h"
+#include "access/transam.h"
+
+
+#define OldFileName(path, seg) \
+ psprintf("%s/%04X", path, seg)
+
+#define NewFileName(path, seg) \
+ psprintf("%s/%04X%08X", path, \
+ (uint32) ((seg) >> 32), (uint32) ((seg) & (int64)0xFFFFFFFF))
+
+#define SLRU_PAGES_PER_SEGMENT_OLD 32
+#define SLRU_PAGES_PER_SEGMENT_NEW 2048 /* XXX SLRU_PAGES_PER_SEGMENT */
+
+
+static FILE*
+create_target_file(const char *dir, int64 segno, char **pfn)
+{
+ char *fn;
+ FILE *filedesc;
+
+ fn = NewFileName(dir, segno);
+ filedesc = fopen(fn, "wb");
+
+ if (!filedesc)
+ pg_fatal("Cannot create file: %s", fn);
+
+ if (pfn)
+ {
+ if (*pfn)
+ pfree(*pfn);
+ *pfn = fn;
+ }
+ else
+ pfree(fn);
+
+ return filedesc;
+}
+
+typedef struct SLRUSegmentState
+{
+ const char *dir;
+ char *fn;
+ FILE *file;
+ uint64 segno;
+ uint64 pageno;
+ bool leading_gap;
+} SLRUSegmentState;
+
+static void
+close_segment(SLRUSegmentState *state)
+{
+ if (state->file != NULL)
+ {
+ fclose(state->file);
+ state->file = NULL;
+ }
+
+ if (state->fn)
+ {
+ pfree(state->fn);
+ state->fn = NULL;
+ }
+}
+
+static int
+read_old_segment_page(SLRUSegmentState *state, void *buf, bool *is_empty)
+{
+ size_t len;
+
+ /* Open next segment file, if needed */
+ if (!state->fn)
+ {
+ if (!state->segno)
+ state->leading_gap = true;
+ state->fn = OldFileName(state->dir, (uint32) state->segno);
+ state->file = fopen(state->fn, "rb");
+
+ /* Set position to the needed page */
+ if (state->file && state->pageno > 0)
+ {
+ if (fseek(state->file, state->pageno * BLCKSZ, SEEK_SET))
+ {
+ fclose(state->file);
+ state->file = NULL;
+ }
+ }
+ }
+
+ if (state->file)
+ {
+ /* Segment file do exists, read page from it */
+ state->leading_gap = false;
+
+ len = fread(buf, sizeof(char), BLCKSZ, state->file);
+
+ /* Are we done or was there an error? */
+ if (len <= 0)
+ {
+ if (ferror(state->file))
+ pg_fatal("Error reading file: %s", state->fn);
+
+ if (feof(state->file))
+ {
+ *is_empty = true;
+ len = -1;
+ fclose(state->file);
+ state->file = NULL;
+ }
+ }
+ else
+ *is_empty = false;
+ }
+ else if (!state->leading_gap)
+ {
+ /* We reached the last segment */
+ len = -1;
+ *is_empty = true;
+ }
+ else
+ {
+ /* Skip few first segments if they were frozen and removed */
+ len = BLCKSZ;
+ *is_empty = true;
+ }
+
+ state->pageno++;
+
+ if (state->pageno >= SLRU_PAGES_PER_SEGMENT_OLD)
+ {
+ /* Start new segment */
+ state->segno++;
+ state->pageno = 0;
+ close_segment(state);
+ }
+
+ return (int) len;
+}
+
+static void
+write_new_segment_page(SLRUSegmentState *state, void *buf, bool is_empty)
+{
+ /*
+ * Create a new segment file if we still didn't. Creation is
+ * postponed until the first non-empty page is found. This helps
+ * not to create completely empty segments.
+ */
+ if (!state->file && !is_empty)
+ {
+ state->file = create_target_file(state->dir, state->segno, &state->fn);
+
+ /* Write zeroes to the previously skipped prefix */
+ if (state->pageno > 0)
+ {
+ char zerobuf[BLCKSZ] = {0};
+
+ for (int64 i = 0; i < state->pageno; i++)
+ {
+ if (fwrite(zerobuf, sizeof(char), BLCKSZ, state->file) != BLCKSZ)
+ pg_fatal("Could not write file: %s", state->fn);
+ }
+ }
+ }
+
+ /* Write page to the new segment (if it was created) */
+ if (state->file)
+ {
+ if (is_empty)
+ memset(buf, 0, BLCKSZ);
+
+ if (fwrite(buf, sizeof(char), BLCKSZ, state->file) != BLCKSZ)
+ pg_fatal("Could not write file: %s", state->fn);
+ }
+
+ state->pageno++;
+
+ /*
+ * Did we reach the maximum page number? Then close segment file
+ * and create a new one on the next iteration
+ */
+ if (state->pageno >= SLRU_PAGES_PER_SEGMENT_NEW)
+ {
+ state->segno++;
+ state->pageno = 0;
+ close_segment(state);
+ }
+}
+
+#define CLOG_BITS_PER_XACT 2
+#define CLOG_XACTS_PER_BYTE 4
+#define CLOG_XACTS_PER_PAGE (BLCKSZ * CLOG_XACTS_PER_BYTE)
+
+#define MaxTransactionIdOld ((TransactionId) 0xFFFFFFFF)
+
+/*
+ * Convert pg_xact segments.
+ */
+void
+convert_clog(const char *old_subdir, const char *new_subdir)
+{
+ SLRUSegmentState oldseg = {0};
+ SLRUSegmentState newseg = {0};
+ TransactionId oldest_xid = old_cluster.controldata.chkpnt_oldstxid;
+ TransactionId next_xid = old_cluster.controldata.chkpnt_nxtxid;
+ TransactionId xid;
+ uint64 pageno;
+ char buf[BLCKSZ] = {0};
+
+ oldseg.dir = old_subdir;
+ newseg.dir = new_subdir;
+
+ pageno = oldest_xid / CLOG_XACTS_PER_PAGE;
+
+ oldseg.segno = pageno / SLRU_PAGES_PER_SEGMENT_OLD;
+ oldseg.pageno = pageno % SLRU_PAGES_PER_SEGMENT_OLD;
+
+ newseg.segno = pageno / SLRU_PAGES_PER_SEGMENT_NEW;
+ newseg.pageno = pageno % SLRU_PAGES_PER_SEGMENT_NEW;
+
+ if (next_xid < oldest_xid)
+ next_xid += FirstUpgradedTransactionId; /* wraparound */
+
+ /* Copy xid flags reading only needed segment pages */
+ for (xid = oldest_xid & ~(CLOG_XACTS_PER_PAGE - 1);
+ xid <= ((next_xid - 1) & ~(CLOG_XACTS_PER_PAGE - 1));
+ xid += CLOG_XACTS_PER_PAGE)
+ {
+ bool is_empty;
+ int len;
+
+ /* Handle possible segment wraparound */
+ if (oldseg.segno > MaxTransactionIdOld / CLOG_XACTS_PER_PAGE / SLRU_PAGES_PER_SEGMENT_OLD) {
+ pageno = (MaxTransactionIdOld + 1) / CLOG_XACTS_PER_PAGE;
+
+ Assert(oldseg.segno == pageno / SLRU_PAGES_PER_SEGMENT_OLD);
+ Assert(!oldseg.pageno);
+ Assert(!oldseg.file && !oldseg.fn);
+ oldseg.segno = 0;
+
+ Assert(newseg.segno == pageno / SLRU_PAGES_PER_SEGMENT_NEW);
+ Assert(!newseg.pageno);
+ Assert(!newseg.file);
+ newseg.segno = 0;
+ }
+
+ len = read_old_segment_page(&oldseg, buf, &is_empty);
+
+ /*
+ * Ignore read errors, copy all existing segment pages in the
+ * interesting xid range.
+ */
+ is_empty |= len <= 0;
+
+ if (!is_empty && len < BLCKSZ)
+ memset(&buf[len], 0, BLCKSZ - len);
+
+ write_new_segment_page(&newseg, buf, is_empty);
+ }
+
+ /* Release resources */
+ close_segment(&oldseg);
+ close_segment(&newseg);
+}
+
+typedef uint32 MultiXactIdOld;
+typedef uint64 MultiXactIdNew;
+
+typedef uint32 MultiXactOffsetOld;
+typedef uint64 MultiXactOffsetNew;
+
+#define MaxMultiXactIdOld ((MultiXactIdOld) 0xFFFFFFFF)
+#define MaxMultiXactOffsetOld ((MultiXactOffsetOld) 0xFFFFFFFF)
+
+#define MXACT_OFFSETS_PER_BLOCK_OLD (BLCKSZ / sizeof(MultiXactOffsetOld))
+#define MXACT_OFFSETS_PER_BLOCK_NEW (BLCKSZ / sizeof(MultiXactOffsetNew))
+
+/*
+ * Convert pg_multixact/offsets segments and return oldest mxid offset.
+ */
+MultiXactOffsetNew
+convert_multixact_offsets(const char *old_subdir, const char *new_subdir)
+{
+ SLRUSegmentState oldseg = {0};
+ SLRUSegmentState newseg = {0};
+ MultiXactOffsetOld oldbuf[MXACT_OFFSETS_PER_BLOCK_OLD] = {0};
+ MultiXactOffsetNew newbuf[MXACT_OFFSETS_PER_BLOCK_NEW] = {0};
+ MultiXactOffsetOld oldest_mxoff = 0;
+ MultiXactId oldest_mxid = old_cluster.controldata.chkpnt_oldstMulti;
+ MultiXactId next_mxid = old_cluster.controldata.chkpnt_nxtmulti;
+ MultiXactId mxid;
+ uint64 old_entry;
+ uint64 new_entry;
+ bool oldest_mxoff_known = false;
+
+ oldseg.dir = psprintf("%s/%s", old_cluster.pgdata, old_subdir);
+ newseg.dir = psprintf("%s/%s", new_cluster.pgdata, new_subdir);
+
+ old_entry = oldest_mxid % MXACT_OFFSETS_PER_BLOCK_OLD;
+ oldseg.pageno = oldest_mxid / MXACT_OFFSETS_PER_BLOCK_OLD;
+ oldseg.segno = oldseg.pageno / SLRU_PAGES_PER_SEGMENT_OLD;
+ oldseg.pageno %= SLRU_PAGES_PER_SEGMENT_OLD;
+
+ new_entry = oldest_mxid % MXACT_OFFSETS_PER_BLOCK_NEW;
+ newseg.pageno = oldest_mxid / MXACT_OFFSETS_PER_BLOCK_NEW;
+ newseg.segno = newseg.pageno / SLRU_PAGES_PER_SEGMENT_NEW;
+ newseg.pageno %= SLRU_PAGES_PER_SEGMENT_NEW;
+
+ if (next_mxid < oldest_mxid)
+ next_mxid += (uint64) 1 << 32; /* wraparound */
+
+ prep_status("Converting old %s to new format", old_subdir);
+
+ /* Copy mxid offsets reading only needed segment pages */
+ for (mxid = oldest_mxid; mxid < next_mxid; old_entry = 0)
+ {
+ int oldlen;
+ bool is_empty;
+
+ /* Handle possible segment wraparound */
+ if (oldseg.segno > MaxMultiXactIdOld / MXACT_OFFSETS_PER_BLOCK_OLD / SLRU_PAGES_PER_SEGMENT_OLD) /* 0xFFFF */
+ oldseg.segno = 0;
+
+ oldlen = read_old_segment_page(&oldseg, oldbuf, &is_empty);
+
+ if (oldlen <= 0 || is_empty)
+ {
+ char pageno_str[32];
+
+ snprintf(pageno_str, sizeof(pageno_str), UINT64_FORMAT, oldseg.pageno);
+ pg_fatal("Cannot read page %s from segment: %s\n",
+ pageno_str, oldseg.fn);
+ }
+
+ if (oldlen < BLCKSZ)
+ memset((char *) oldbuf + oldlen, 0, BLCKSZ - oldlen);
+
+ /* Save oldest mxid offset */
+ if (!oldest_mxoff_known)
+ {
+ oldest_mxoff = oldbuf[old_entry];
+ oldest_mxoff_known = true;
+ }
+
+ /* Skip wrapped-around invalid MultiXactIds */
+ if (mxid == (MultiXactId) 1 << 32)
+ {
+ Assert(oldseg.segno == 0);
+ Assert(oldseg.pageno == 1);
+ Assert(old_entry == 0);
+ mxid += FirstMultiXactId;
+ old_entry = FirstMultiXactId;
+ }
+
+ /* Copy entries to the new page */
+ for (; mxid < next_mxid && old_entry < MXACT_OFFSETS_PER_BLOCK_OLD;
+ mxid++, old_entry++)
+ {
+ MultiXactOffsetNew mxoff = oldbuf[old_entry];
+
+ /* Handle possible offset wraparound (1 becomes 2^32) */
+ if (mxoff < oldest_mxoff)
+ mxoff += ((uint64) 1 << 32) - 1;
+
+ /* Subtract oldest_mxoff, so new offsets will start from 1 */
+ newbuf[new_entry++] = mxoff - oldest_mxoff + 1;
+
+ if (new_entry >= MXACT_OFFSETS_PER_BLOCK_NEW)
+ {
+ /* Write new page */
+ write_new_segment_page(&newseg, newbuf, false);
+ new_entry = 0;
+ }
+ }
+ }
+
+ /* Write the last incomplete page */
+ if (new_entry > 0 || oldest_mxid == next_mxid)
+ {
+ memset(&newbuf[new_entry], 0,
+ sizeof(newbuf[0]) * (MXACT_OFFSETS_PER_BLOCK_NEW - new_entry));
+ write_new_segment_page(&newseg, newbuf, false);
+ }
+
+ /* Use next_mxoff as oldest_mxoff, if oldest_mxid == next_mxid */
+ if (!oldest_mxoff_known)
+ {
+ Assert(oldest_mxid == next_mxid);
+ oldest_mxoff = (MultiXactOffsetNew) old_cluster.controldata.chkpnt_nxtmxoff;
+ }
+
+ /* Release resources */
+ close_segment(&oldseg);
+ close_segment(&newseg);
+
+ pfree((char *) oldseg.dir);
+ pfree((char *) newseg.dir);
+
+ check_ok();
+
+ return oldest_mxoff;
+}
+
+typedef uint32 TransactionIdOld;
+typedef uint64 TransactionIdNew;
+
+#define MXACT_MEMBERS_FLAG_BYTES 1
+
+#define MXACT_MEMBERS_PER_GROUP_OLD 4
+#define MXACT_MEMBERS_GROUP_SIZE_OLD (MXACT_MEMBERS_PER_GROUP_OLD * (sizeof(TransactionIdOld) + MXACT_MEMBERS_FLAG_BYTES))
+#define MXACT_MEMBER_GROUPS_PER_PAGE_OLD (BLCKSZ / MXACT_MEMBERS_GROUP_SIZE_OLD)
+#define MXACT_MEMBERS_PER_PAGE_OLD (MXACT_MEMBERS_PER_GROUP_OLD * MXACT_MEMBER_GROUPS_PER_PAGE_OLD)
+#define MXACT_MEMBER_FLAG_BYTES_PER_GROUP_OLD MXACT_MEMBERS_FLAG_BYTES * MXACT_MEMBERS_PER_GROUP_OLD
+
+#define MXACT_MEMBERS_PER_GROUP_NEW 8
+#define MXACT_MEMBERS_GROUP_SIZE_NEW (MXACT_MEMBERS_PER_GROUP_NEW * (sizeof(TransactionIdNew) + MXACT_MEMBERS_FLAG_BYTES))
+#define MXACT_MEMBER_GROUPS_PER_PAGE_NEW (BLCKSZ / MXACT_MEMBERS_GROUP_SIZE_NEW)
+
+/*
+ * Convert pg_multixact/members segments, offsets will start from 1.
+ */
+void
+convert_multixact_members(const char *old_subdir, const char *new_subdir,
+ MultiXactOffset oldest_mxoff)
+{
+ MultiXactOffsetNew next_mxoff = (MultiXactOffsetNew) old_cluster.controldata.chkpnt_nxtmxoff;
+ MultiXactOffsetNew mxoff;
+ SLRUSegmentState oldseg = { 0 };
+ SLRUSegmentState newseg = { 0 };
+ char oldbuf[BLCKSZ] = { 0 };
+ char newbuf[BLCKSZ] = { 0 };
+ int newgroup;
+ int newmember;
+ char *newflag = newbuf;
+ TransactionIdNew *newxid = (TransactionIdNew *)(newflag + MXACT_MEMBERS_FLAG_BYTES * MXACT_MEMBERS_PER_GROUP_NEW);
+ int newidx;
+ int oldidx;
+
+ oldseg.dir = psprintf("%s/%s", old_cluster.pgdata, old_subdir);
+ newseg.dir = psprintf("%s/%s", new_cluster.pgdata, new_subdir);
+
+ prep_status("Converting old %s to new format", old_subdir);
+
+ if (next_mxoff < oldest_mxoff)
+ next_mxoff += (uint64) 1 << 32;
+
+ /* Initialize old starting position */
+ oldidx = oldest_mxoff % MXACT_MEMBERS_PER_PAGE_OLD;
+ oldseg.pageno = oldest_mxoff / MXACT_MEMBERS_PER_PAGE_OLD;
+ oldseg.segno = oldseg.pageno / SLRU_PAGES_PER_SEGMENT_OLD;
+ oldseg.pageno %= SLRU_PAGES_PER_SEGMENT_OLD;
+
+ /* Initialize new starting position (skip invalid zero offset) */
+ newgroup = 0;
+ newidx = 1;
+ newmember = 1;
+ newflag++;
+ newxid++;
+
+ /* Iterate through the original directory */
+ for (mxoff = oldest_mxoff; mxoff < next_mxoff; oldidx = 0)
+ {
+ bool old_is_empty;
+ int oldlen = read_old_segment_page(&oldseg, oldbuf, &old_is_empty);
+ int ngroups;
+ int oldgroup;
+ int oldmember;
+
+ if (old_is_empty || oldlen <= 0)
+ {
+ char pageno_str[32];
+
+ snprintf(pageno_str, sizeof(pageno_str), UINT64_FORMAT, oldseg.pageno);
+ pg_fatal("Cannot read page %s from segment: %s\n",
+ pageno_str, oldseg.fn);
+ }
+
+ if (oldlen < BLCKSZ)
+ {
+ memset(oldbuf + oldlen, 0, BLCKSZ - oldlen);
+ oldlen = BLCKSZ;
+ }
+
+ ngroups = oldlen / MXACT_MEMBERS_GROUP_SIZE_OLD;
+
+ /* Iterate through old member groups */
+ for (oldgroup = oldidx / MXACT_MEMBERS_PER_GROUP_OLD,
+ oldmember = oldidx % MXACT_MEMBERS_PER_GROUP_OLD;
+ oldgroup < ngroups && mxoff < next_mxoff;
+ oldgroup++, oldmember = 0)
+ {
+ char *oldflag = (char *) oldbuf + oldgroup * MXACT_MEMBERS_GROUP_SIZE_OLD;
+ TransactionIdOld *oldxid = (TransactionIdOld *)(oldflag + MXACT_MEMBER_FLAG_BYTES_PER_GROUP_OLD);
+
+ oldxid += oldmember;
+ oldflag += oldmember;
+
+ /* Iterate through old members */
+ for (int j = 0;
+ j < MXACT_MEMBERS_PER_GROUP_OLD && mxoff < next_mxoff;
+ j++)
+ {
+ /* Copy member's xid and flags to the new page */
+ *newflag++ = *oldflag++;
+ *newxid++ = (TransactionIdNew) *oldxid++;
+
+ newidx++;
+ oldidx++;
+ mxoff++;
+
+ if (++newmember >= MXACT_MEMBERS_PER_GROUP_NEW)
+ {
+ /* Start next member group */
+ newmember = 0;
+
+ if (++newgroup >= MXACT_MEMBER_GROUPS_PER_PAGE_NEW)
+ {
+ /* Write current page and start new */
+ newgroup = 0;
+ newidx = 0;
+ write_new_segment_page(&newseg, newbuf, false);
+ memset(newbuf, 0, BLCKSZ);
+ }
+
+ newflag = (char *) newbuf + newgroup * MXACT_MEMBERS_GROUP_SIZE_NEW;
+ newxid = (TransactionIdNew *)(newflag + MXACT_MEMBERS_FLAG_BYTES * MXACT_MEMBERS_PER_GROUP_NEW);
+ }
+
+ /* Handle offset wraparound */
+ if (mxoff > MaxMultiXactOffsetOld)
+ {
+ Assert(mxoff == (uint64) 1 << 32);
+ Assert(oldseg.segno == MaxMultiXactOffsetOld / MXACT_MEMBERS_PER_PAGE_OLD / SLRU_PAGES_PER_SEGMENT_OLD);
+ Assert(oldseg.pageno == MaxMultiXactOffsetOld / MXACT_MEMBERS_PER_PAGE_OLD % SLRU_PAGES_PER_SEGMENT_OLD);
+ Assert(oldmember == MaxMultiXactOffsetOld % MXACT_MEMBERS_PER_PAGE_OLD);
+
+ /* Switch to segment 0000 */
+ close_segment(&oldseg);
+ oldseg.segno = 0;
+ oldseg.pageno = 0;
+
+ oldidx = 1; /* skip invalid zero mxid offset */
+ }
+ }
+ }
+ }
+
+ /* Write last page, unless it is empty */
+ if (newflag > (char *) newbuf || oldest_mxoff == next_mxoff)
+ write_new_segment_page(&newseg, newbuf, false);
+
+ /* Release resources */
+ close_segment(&oldseg);
+ close_segment(&newseg);
+
+ pfree((char *) oldseg.dir);
+ pfree((char *) newseg.dir);
+
+ check_ok();
+}
diff --git a/src/bin/pg_upgrade/test.sh b/src/bin/pg_upgrade/test.sh
index ef328b3062f..f46b4241155 100644
--- a/src/bin/pg_upgrade/test.sh
+++ b/src/bin/pg_upgrade/test.sh
@@ -24,7 +24,7 @@ standard_initdb() {
# without increasing test runtime, run these tests with a custom setting.
# Also, specify "-A trust" explicitly to suppress initdb's warning.
# --allow-group-access and --wal-segsize have been added in v11.
- "$1" -N --wal-segsize 1 --allow-group-access -A trust
+ "$1" -N --wal-segsize 1 --allow-group-access -A trust -x 21000000000
if [ -n "$TEMP_CONFIG" -a -r "$TEMP_CONFIG" ]
then
cat "$TEMP_CONFIG" >> "$PGDATA/postgresql.conf"
@@ -195,6 +195,12 @@ if "$MAKE" -C "$oldsrc" installcheck-parallel; then
fi
fi
+ psql -X -d regression << EOF
+ CREATE TABLE t1 (id SERIAL NOT NULL PRIMARY KEY, plt text, pln NUMERIC(8, 4));
+ INSERT INTO t1 (plt, pln) SELECT md5(random()::text), random() * 9999 FROM generate_series(1, 1000);
+EOF
+ psql -X -d regression -c"SELECT relfrozenxid, relminmxid FROM pg_class WHERE relname = 't1';" > "$temp_root"/old_xids.txt
+
pg_dumpall $extra_dump_options --no-sync \
-f "$temp_root"/dump1.sql || pg_dumpall1_status=$?
@@ -255,6 +261,17 @@ esac
pg_ctl start -l "$logdir/postmaster2.log" -o "$POSTMASTER_OPTS" -w
+psql -X -d regression -c"SELECT relfrozenxid, relminmxid FROM pg_class WHERE relname = 't1';" > "$temp_root"/new_xids.txt
+
+if diff -u "$temp_root"/new_xids.txt "$temp_root"/old_xids.txt > "$temp_root"/xids.diff; then
+ rm "$temp_root"/xids.diff
+ echo "xids are identical, PASSED"
+else
+ echo "Files $temp_root/new_xids.txt and $temp_root/old_xids.txt differ"
+ echo "See $temp_root/xids.diff"
+ exit 1
+fi
+
pg_dumpall $extra_dump_options --no-sync \
-f "$temp_root"/dump2.sql || pg_dumpall2_status=$?
pg_ctl -m fast stop
diff --git a/src/bin/pg_upgrade/util.c b/src/bin/pg_upgrade/util.c
index d98deb7f24e..02839bec0e2 100644
--- a/src/bin/pg_upgrade/util.c
+++ b/src/bin/pg_upgrade/util.c
@@ -241,3 +241,20 @@ str2uint(const char *str)
{
return strtoul(str, NULL, 10);
}
+
+/*
+ * str2uint64()
+ *
+ * convert string to 64-bit unsigned int
+ */
+uint64
+str2uint64(const char *str)
+{
+#ifdef _MSC_VER /* MSVC only */
+ return _strtoui64(str, NULL, 10);
+#elif defined(HAVE_STRTOULL) && SIZEOF_LONG < 8
+ return strtoull(str, NULL, 10);
+#else
+ return strtoul(str, NULL, 10);
+#endif
+}
diff --git a/src/bin/pg_upgrade/version.c b/src/bin/pg_upgrade/version.c
index c694558c3d6..d3a04373ad0 100644
--- a/src/bin/pg_upgrade/version.c
+++ b/src/bin/pg_upgrade/version.c
@@ -9,6 +9,7 @@
#include "postgres_fe.h"
+#include "access/transam.h"
#include "catalog/pg_class_d.h"
#include "fe_utils/string_utils.h"
#include "pg_upgrade.h"
@@ -238,19 +239,21 @@ old_9_6_check_for_unknown_data_type_usage(ClusterInfo *cluster)
}
/*
- * old_9_6_invalidate_hash_indexes()
- * 9.6 -> 10
- * Hash index binary format has changed from 9.6->10.0
+ * invalidate_indexes()
+ * Invalidates all indexes satisfying given predicate.
*/
-void
-old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
+static void
+invalidate_indexes(ClusterInfo *cluster, bool check_mode,
+ const char *name, const char *pred)
{
int dbnum;
FILE *script = NULL;
bool found = false;
- char *output_path = "reindex_hash.sql";
+ char output_path[MAXPGPATH];
+
+ snprintf(output_path, sizeof(output_path), "reindex_%s.sql", name);
- prep_status("Checking for hash indexes");
+ prep_status("Checking for %s indexes", name);
for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
{
@@ -263,18 +266,28 @@ old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
DbInfo *active_db = &cluster->dbarr.dbs[dbnum];
PGconn *conn = connectToServer(cluster, active_db->db_name);
- /* find hash indexes */
- res = executeQueryOrDie(conn,
- "SELECT n.nspname, c.relname "
- "FROM pg_catalog.pg_class c, "
- " pg_catalog.pg_index i, "
- " pg_catalog.pg_am a, "
- " pg_catalog.pg_namespace n "
- "WHERE i.indexrelid = c.oid AND "
- " c.relam = a.oid AND "
- " c.relnamespace = n.oid AND "
- " a.amname = 'hash'"
- );
+
+ /*
+ * Find indexes satisfying predicate.
+ *
+ * System indexes (with oids < FirstNormalObjectId) are excluded from
+ * the search as they are recreated in the new cluster during initdb.
+ */
+ res = executeQueryOrDie(
+ conn,
+ "SELECT n.nspname, c.relname, i.indexrelid "
+ "FROM pg_catalog.pg_class c, "
+ " pg_catalog.pg_index i, "
+ " pg_catalog.pg_am a, "
+ " pg_catalog.pg_namespace n "
+ "WHERE i.indexrelid = c.oid AND "
+ " c.relam = a.oid AND "
+ " c.relnamespace = n.oid AND "
+ " i.indexrelid >= '%u'::pg_catalog.oid AND "
+ " %s "
+ "ORDER BY i.indexrelid ASC",
+ FirstNormalObjectId,
+ pred);
ntups = PQntuples(res);
i_nspname = PQfnumber(res, "nspname");
@@ -307,17 +320,26 @@ old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
if (!check_mode && db_used)
{
- /* mark hash indexes as invalid */
- PQclear(executeQueryOrDie(conn,
- "UPDATE pg_catalog.pg_index i "
- "SET indisvalid = false "
- "FROM pg_catalog.pg_class c, "
- " pg_catalog.pg_am a, "
- " pg_catalog.pg_namespace n "
- "WHERE i.indexrelid = c.oid AND "
- " c.relam = a.oid AND "
- " c.relnamespace = n.oid AND "
- " a.amname = 'hash'"));
+ /*
+ * Mark indexes satisfying predicate as invalid.
+ *
+ * System indexes (with oids < FirstNormalObjectId) are excluded
+ * from the search (see above).
+ */
+ PQclear(executeQueryOrDie(
+ conn,
+ "UPDATE pg_catalog.pg_index i "
+ "SET indisvalid = false "
+ "FROM pg_catalog.pg_class c, "
+ " pg_catalog.pg_am a, "
+ " pg_catalog.pg_namespace n "
+ "WHERE i.indexrelid = c.oid AND "
+ " c.relam = a.oid AND "
+ " c.relnamespace = n.oid AND "
+ " i.indexrelid >= '%u'::pg_catalog.oid AND "
+ " %s",
+ FirstNormalObjectId,
+ pred));
}
PQfinish(conn);
@@ -331,24 +353,37 @@ old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
report_status(PG_WARNING, "warning");
if (check_mode)
pg_log(PG_WARNING, "\n"
- "Your installation contains hash indexes. These indexes have different\n"
+ "Your installation contains %s indexes. These indexes have different\n"
"internal formats between your old and new clusters, so they must be\n"
"reindexed with the REINDEX command. After upgrading, you will be given\n"
- "REINDEX instructions.\n\n");
+ "REINDEX instructions.\n\n",
+ name);
else
pg_log(PG_WARNING, "\n"
- "Your installation contains hash indexes. These indexes have different\n"
+ "Your installation contains %s indexes. These indexes have different\n"
"internal formats between your old and new clusters, so they must be\n"
"reindexed with the REINDEX command. The file\n"
" %s\n"
"when executed by psql by the database superuser will recreate all invalid\n"
"indexes; until then, none of these indexes will be used.\n\n",
+ name,
output_path);
}
else
check_ok();
}
+/*
+ * old_9_6_invalidate_hash_indexes()
+ * 9.6 -> 10
+ * Hash index binary format has changed from 9.6->10.0
+ */
+void
+old_9_6_invalidate_hash_indexes(ClusterInfo *cluster, bool check_mode)
+{
+ invalidate_indexes(cluster, check_mode, "hash", "a.amname = 'hash'");
+}
+
/*
* old_11_check_for_sql_identifier_data_type_usage()
* 11 -> 12
@@ -458,3 +493,36 @@ report_extension_updates(ClusterInfo *cluster)
else
check_ok();
}
+
+/*
+ * invalidate_spgist_indexes()
+ * 32bit -> 64bit
+ * SP-GIST contains xids.
+ */
+void
+invalidate_spgist_indexes(ClusterInfo *cluster, bool check_mode)
+{
+ invalidate_indexes(cluster, check_mode, "spgist", "a.amname = 'spgist'");
+}
+
+/*
+ * invalidate_gin_indexes()
+ * 32bit -> 64bit
+ * Gin indexes contains xids in deleted pages.
+ */
+void
+invalidate_gin_indexes(ClusterInfo *cluster, bool check_mode)
+{
+ invalidate_indexes(cluster, check_mode, "gin", "a.amname = 'gin'");
+}
+
+/*
+ * invalidate_external_indexes()
+ * Generate script to REINDEX non standard external indexes (like RUM etc)
+ */
+void
+invalidate_external_indexes(ClusterInfo *cluster, bool check_mode)
+{
+ invalidate_indexes(cluster, check_mode, "external",
+ "NOT a.amname IN ('btree', 'hash', 'gist', 'gin', 'spgist', 'brin')");
+}
diff --git a/src/bin/pg_verifybackup/t/003_corruption.pl b/src/bin/pg_verifybackup/t/003_corruption.pl
index 53be2efd87d..9681d6d800c 100644
--- a/src/bin/pg_verifybackup/t/003_corruption.pl
+++ b/src/bin/pg_verifybackup/t/003_corruption.pl
@@ -176,7 +176,7 @@ sub mutilate_extra_tablespace_file
sub mutilate_missing_file
{
my ($backup_path) = @_;
- my $pathname = "$backup_path/pg_xact/0000";
+ my $pathname = "$backup_path/pg_xact/0000000048C0";
unlink($pathname) || die "$pathname: $!";
return;
}
diff --git a/src/bin/pg_waldump/pg_waldump.c b/src/bin/pg_waldump/pg_waldump.c
index a6251e1a961..5e143a4a8ea 100644
--- a/src/bin/pg_waldump/pg_waldump.c
+++ b/src/bin/pg_waldump/pg_waldump.c
@@ -490,7 +490,7 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
XLogDumpRecordLen(record, &rec_len, &fpi_len);
- printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, ",
+ printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: " XID_FMT ", lsn: %X/%08X, prev %X/%08X, ",
desc->rm_name,
rec_len, XLogRecGetTotalLen(record),
XLogRecGetXid(record),
@@ -950,7 +950,7 @@ main(int argc, char **argv)
}
break;
case 'x':
- if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
+ if (sscanf(optarg, XID_FMT, &config.filter_by_xid) != 1)
{
pg_log_error("could not parse \"%s\" as a transaction ID",
optarg);
diff --git a/src/include/access/clog.h b/src/include/access/clog.h
index 543f2e2643a..73bc172309a 100644
--- a/src/include/access/clog.h
+++ b/src/include/access/clog.h
@@ -31,7 +31,7 @@ typedef int XidStatus;
typedef struct xl_clog_truncate
{
- int pageno;
+ int64 pageno;
TransactionId oldestXact;
Oid oldestXactDb;
} xl_clog_truncate;
diff --git a/src/include/access/commit_ts.h b/src/include/access/commit_ts.h
index 7662f8e1a9c..d2cfb02e145 100644
--- a/src/include/access/commit_ts.h
+++ b/src/include/access/commit_ts.h
@@ -60,7 +60,7 @@ typedef struct xl_commit_ts_set
typedef struct xl_commit_ts_truncate
{
- int pageno;
+ int64 pageno;
TransactionId oldestXid;
} xl_commit_ts_truncate;
diff --git a/src/include/access/ginblock.h b/src/include/access/ginblock.h
index 9347f464f34..4db042c3196 100644
--- a/src/include/access/ginblock.h
+++ b/src/include/access/ginblock.h
@@ -133,8 +133,15 @@ typedef struct GinMetaPageData
* We should reclaim deleted page only once every transaction started before
* its deletion is over.
*/
-#define GinPageGetDeleteXid(page) ( ((PageHeader) (page))->pd_prune_xid )
-#define GinPageSetDeleteXid(page, xid) ( ((PageHeader) (page))->pd_prune_xid = xid)
+#define GinPageGetDeleteXid(page) ( \
+ (((PageHeader) (page))->pd_upper == BLCKSZ - sizeof(GinPageOpaqueData) - sizeof(TransactionId)) ? \
+ *((TransactionId *) ((char *) (page) + BLCKSZ - sizeof(GinPageOpaqueData) - sizeof(TransactionId))) : \
+ InvalidTransactionId )
+#define GinPageSetDeleteXid(page, xid) \
+ do { \
+ ((PageHeader) (page))->pd_upper = BLCKSZ - sizeof(GinPageOpaqueData) - sizeof(TransactionId); \
+ *((TransactionId *) ((char *) (page) + BLCKSZ - sizeof(GinPageOpaqueData) - sizeof(TransactionId))) = xid; \
+ } while (false)
extern bool GinPageIsRecyclable(Page page);
/*
diff --git a/src/include/access/gist.h b/src/include/access/gist.h
index a3337627b8f..41de3052fdd 100644
--- a/src/include/access/gist.h
+++ b/src/include/access/gist.h
@@ -223,7 +223,7 @@ GistPageGetDeleteXid(Page page)
return ((GISTDeletedPageContents *) PageGetContents(page))->deleteXid;
}
else
- return FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
+ return FullTransactionIdFromXid(FirstNormalTransactionId);
}
/*
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 0ad87730e17..ac9201c7e36 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -146,6 +146,10 @@ extern void ReleaseBulkInsertStatePin(BulkInsertState bistate);
extern void heap_insert(Relation relation, HeapTuple tup, CommandId cid,
int options, BulkInsertState bistate);
+extern bool heap_page_prepare_for_xid(Relation relation, Buffer buffer,
+ TransactionId xid, bool multi);
+extern bool rewrite_page_prepare_for_xid(Page page, TransactionId xid,
+ bool multi);
extern void heap_multi_insert(Relation relation, struct TupleTableSlot **slots,
int ntuples, CommandId cid, int options,
BulkInsertState bistate);
@@ -164,10 +168,10 @@ extern TM_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
Buffer *buffer, struct TM_FailureData *tmfd);
extern void heap_inplace_update(Relation relation, HeapTuple tuple);
-extern bool heap_freeze_tuple(HeapTupleHeader tuple,
+extern bool heap_freeze_tuple(HeapTuple tuple,
TransactionId relfrozenxid, TransactionId relminmxid,
TransactionId cutoff_xid, TransactionId cutoff_multi);
-extern bool heap_tuple_needs_freeze(HeapTupleHeader tuple, TransactionId cutoff_xid,
+extern bool heap_tuple_needs_freeze(HeapTuple tuple, TransactionId cutoff_xid,
MultiXactId cutoff_multi, Buffer buf);
extern bool heap_tuple_needs_eventual_freeze(HeapTupleHeader tuple);
@@ -187,11 +191,13 @@ extern int heap_page_prune(Relation relation, Buffer buffer,
TransactionId old_snap_xmin,
TimestampTz old_snap_ts_ts,
int *nnewlpdead,
- OffsetNumber *off_loc);
+ OffsetNumber *off_loc,
+ bool repairFragmentation);
extern void heap_page_prune_execute(Buffer buffer,
OffsetNumber *redirected, int nredirected,
OffsetNumber *nowdead, int ndead,
- OffsetNumber *nowunused, int nunused);
+ OffsetNumber *nowunused, int nunused,
+ bool repairFragmentation);
extern void heap_get_root_tuples(Page page, OffsetNumber *root_offsets);
/* in heap/vacuumlazy.c */
@@ -210,7 +216,7 @@ extern HTSV_Result HeapTupleSatisfiesVacuumHorizon(HeapTuple stup, Buffer buffer
TransactionId *dead_after);
extern void HeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,
uint16 infomask, TransactionId xid);
-extern bool HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple);
+extern bool HeapTupleIsOnlyLocked(HeapTuple htup);
extern bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);
extern bool HeapTupleIsSurelyDead(HeapTuple htup,
struct GlobalVisState *vistest);
diff --git a/src/include/access/heapam_xlog.h b/src/include/access/heapam_xlog.h
index 5c47fdcec80..c5a227e364d 100644
--- a/src/include/access/heapam_xlog.h
+++ b/src/include/access/heapam_xlog.h
@@ -59,6 +59,8 @@
#define XLOG_HEAP2_LOCK_UPDATED 0x60
#define XLOG_HEAP2_NEW_CID 0x70
+#define XLOG_HEAP3_BASE_SHIFT 0x00
+
/*
* xl_heap_insert/xl_heap_multi_insert flag values, 8 bits are available.
*/
@@ -389,7 +391,16 @@ typedef struct xl_heap_rewrite_mapping
XLogRecPtr start_lsn; /* Insert LSN at begin of rewrite */
} xl_heap_rewrite_mapping;
-extern void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple,
+/* shift the base of xids on heap page */
+typedef struct xl_heap_base_shift
+{
+ int64 delta; /* delta value to shift the base */
+ bool multi; /* true to shift multixact base */
+} xl_heap_base_shift;
+
+#define SizeOfHeapBaseShift (offsetof(xl_heap_base_shift, multi) + sizeof(bool))
+
+extern void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTuple tuple,
TransactionId *latestRemovedXid);
extern void heap_redo(XLogReaderState *record);
@@ -399,19 +410,24 @@ extern void heap_mask(char *pagedata, BlockNumber blkno);
extern void heap2_redo(XLogReaderState *record);
extern void heap2_desc(StringInfo buf, XLogReaderState *record);
extern const char *heap2_identify(uint8 info);
+extern void heap3_redo(XLogReaderState *record);
+extern void heap3_desc(StringInfo buf, XLogReaderState *record);
+extern const char *heap3_identify(uint8 info);
extern void heap_xlog_logical_rewrite(XLogReaderState *r);
extern XLogRecPtr log_heap_freeze(Relation reln, Buffer buffer,
TransactionId cutoff_xid, xl_heap_freeze_tuple *tuples,
int ntuples);
-extern bool heap_prepare_freeze_tuple(HeapTupleHeader tuple,
+extern bool heap_prepare_freeze_tuple(HeapTuple htup,
TransactionId relfrozenxid,
TransactionId relminmxid,
TransactionId cutoff_xid,
TransactionId cutoff_multi,
xl_heap_freeze_tuple *frz,
bool *totally_frozen);
-extern void heap_execute_freeze_tuple(HeapTupleHeader tuple,
+extern void heap_execute_freeze_tuple(HeapTuple tuple,
+ xl_heap_freeze_tuple *xlrec_tp);
+extern void heap_execute_freeze_tuple_page(Page page, HeapTupleHeader tuple,
xl_heap_freeze_tuple *xlrec_tp);
extern XLogRecPtr log_heap_visible(RelFileNode rnode, Buffer heap_buffer,
Buffer vm_buffer, TransactionId cutoff_xid, uint8 flags);
diff --git a/src/include/access/heaptoast.h b/src/include/access/heaptoast.h
index a75699054af..8c599ad7d90 100644
--- a/src/include/access/heaptoast.h
+++ b/src/include/access/heaptoast.h
@@ -22,7 +22,7 @@
*/
#define MaximumBytesPerTuple(tuplesPerPage) \
MAXALIGN_DOWN((BLCKSZ - \
- MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData))) \
+ MAXALIGN(SizeOfPageHeaderData + (tuplesPerPage) * sizeof(ItemIdData)) - MAXALIGN(sizeof(HeapPageSpecialData))) \
/ (tuplesPerPage))
/*
diff --git a/src/include/access/htup.h b/src/include/access/htup.h
index a4bc7256ed5..6d2d82d1570 100644
--- a/src/include/access/htup.h
+++ b/src/include/access/htup.h
@@ -54,6 +54,10 @@ typedef MinimalTupleData *MinimalTuple;
* this can't be told apart from case #1 by inspection; code setting up
* or destroying this representation has to know what it's doing.
*
+ * t_xid_base and t_multi_base are base values for calculation of transaction
+ * identifiers from t_xmin and t_xmax in heap tuple header. Normally they
+ * are copied from page header or another tuple.
+ *
* t_len should always be valid, except in the pointer-to-nothing case.
* t_self and t_tableOid should be valid if the HeapTupleData points to
* a disk buffer, or if it represents a copy of a tuple on disk. They
@@ -62,9 +66,11 @@ typedef MinimalTupleData *MinimalTuple;
typedef struct HeapTupleData
{
uint32 t_len; /* length of *t_data */
+ TransactionId t_xid_base; /* base value for normal transaction ids */
+ TransactionId t_multi_base; /* base value for mutlixact */
ItemPointerData t_self; /* SelfItemPointer */
Oid t_tableOid; /* table the tuple came from */
-#define FIELDNO_HEAPTUPLEDATA_DATA 3
+#define FIELDNO_HEAPTUPLEDATA_DATA 5
HeapTupleHeader t_data; /* -> tuple header and data */
} HeapTupleData;
@@ -78,12 +84,12 @@ typedef HeapTupleData *HeapTuple;
#define HeapTupleIsValid(tuple) PointerIsValid(tuple)
/* HeapTupleHeader functions implemented in utils/time/combocid.c */
-extern CommandId HeapTupleHeaderGetCmin(HeapTupleHeader tup);
-extern CommandId HeapTupleHeaderGetCmax(HeapTupleHeader tup);
-extern void HeapTupleHeaderAdjustCmax(HeapTupleHeader tup,
+extern CommandId HeapTupleGetCmin(HeapTuple tup);
+extern CommandId HeapTupleGetCmax(HeapTuple tup);
+extern void HeapTupleHeaderAdjustCmax(HeapTuple tup,
CommandId *cmax, bool *iscombo);
/* Prototype for HeapTupleHeader accessors in heapam.c */
-extern TransactionId HeapTupleGetUpdateXid(HeapTupleHeader tuple);
+extern TransactionId HeapTupleGetUpdateXid(HeapTuple tuple);
#endif /* HTUP_H */
diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h
index b2d52ed16c8..e2d77de1436 100644
--- a/src/include/access/htup_details.h
+++ b/src/include/access/htup_details.h
@@ -120,13 +120,13 @@
typedef struct HeapTupleFields
{
- TransactionId t_xmin; /* inserting xact ID */
- TransactionId t_xmax; /* deleting or locking xact ID */
+ ShortTransactionId t_xmin; /* inserting xact ID */
+ ShortTransactionId t_xmax; /* deleting or locking xact ID */
union
{
CommandId t_cid; /* inserting or deleting command ID, or both */
- TransactionId t_xvac; /* old-style VACUUM FULL xact ID */
+ ShortTransactionId t_xvac; /* old-style VACUUM FULL xact ID */
} t_field3;
} HeapTupleFields;
@@ -222,7 +222,7 @@ struct HeapTupleHeaderData
* HEAP_XMAX_LOCK_ONLY bit is set; or, for pg_upgrade's sake, if the Xmax is
* not a multi and the EXCL_LOCK bit is set.
*
- * See also HeapTupleHeaderIsOnlyLocked, which also checks for a possible
+ * See also HeapTupleIsOnlyLocked, which also checks for a possible
* aborted updater transaction.
*
* Beware of multiple evaluations of the argument.
@@ -297,6 +297,63 @@ struct HeapTupleHeaderData
* macros evaluate their other argument only once.
*/
+/*
+ * Copy base values for xid and multixacts from page to heap tuple. Should be
+ * called each time tuple is read from page. Otherwise, it would be impossible
+ * to correctly read tuple xmin and xmax.
+ */
+#define HeapTupleCopyBaseFromPage(tup, page) \
+{ \
+ (tup)->t_xid_base = HeapPageGetSpecial(page)->pd_xid_base; \
+ (tup)->t_multi_base = HeapPageGetSpecial(page)->pd_multi_base; \
+}
+
+/*
+ * Copy base values for xid and multixacts from one heap tuple to heap tuple.
+ * Should be called on tuple copy or making desc tuple on the base on src tuple
+ * saving visibility information.
+ */
+#define HeapTupleCopyBase(dest, src) \
+{ \
+ (dest)->t_xid_base = (src)->t_xid_base; \
+ (dest)->t_multi_base = (src)->t_multi_base; \
+}
+
+/*
+ * Set base values for tuple xids/multixacts to zero. Used when visibility
+ * infromation is negligible or will be set later.
+ */
+#define HeapTupleSetZeroBase(tup) \
+{ \
+ (tup)->t_xid_base = InvalidTransactionId; \
+ (tup)->t_multi_base = InvalidTransactionId; \
+}
+
+/*
+ * Macros for accessing "double xmax". On pg_upgraded instances, it might
+ * happend that we can't fit new special area to the page. But we still
+ * might neep to write xmax of tuples for updates and deletes. The trick is
+ * that we actually don't need xmin field. After pg_upgrade (wich implies
+ * restart) no insertions went to this page yet (otherwise special area could
+ * fit). So, if tuple is visible (othewise it would be deleted), then it's
+ * visible for everybody. Thus, t_xmin isn't needed. Therefore, we can use
+ * both t_xmin and t_xmax to store 64-bit xmax.
+ *
+ * See heap_convert.c for details.
+ */
+#define HeapTupleIsDoubleXmax(tup) \
+ ((tup)->t_xid_base == MaxTransactionId)
+
+#define HeapTupleHeaderGetDoubleXmax(tup) \
+ ((TransactionId)(tup)->t_choice.t_heap.t_xmax + \
+ ((TransactionId)(tup)->t_choice.t_heap.t_xmin << 32))
+
+#define HeapTupleHeaderSetDoubleXmax(tup, xid) \
+do { \
+ (tup)->t_choice.t_heap.t_xmax = (TransactionId) (xid) & 0xFFFFFFFF; \
+ (tup)->t_choice.t_heap.t_xmin = ((TransactionId) (xid) >> 32) & 0xFFFFFFFF; \
+} while (0)
+
/*
* HeapTupleHeaderGetRawXmin returns the "raw" xmin field, which is the xid
* originally used to insert the tuple. However, the tuple might actually
@@ -305,20 +362,29 @@ struct HeapTupleHeaderData
* the xmin to FrozenTransactionId, and that value may still be encountered
* on disk.
*/
-#define HeapTupleHeaderGetRawXmin(tup) \
+#define HeapTupleGetRawXmin(tup) \
( \
- (tup)->t_choice.t_heap.t_xmin \
+ HeapTupleIsDoubleXmax(tup) ? \
+ FrozenTransactionId : \
+ ShortTransactionIdToNormal((tup)->t_xid_base, (tup)->t_data->t_choice.t_heap.t_xmin) \
)
-#define HeapTupleHeaderGetXmin(tup) \
+#define HeapTupleGetXmin(tup) \
( \
- HeapTupleHeaderXminFrozen(tup) ? \
- FrozenTransactionId : HeapTupleHeaderGetRawXmin(tup) \
+ HeapTupleHeaderXminFrozen((tup)->t_data) ? \
+ FrozenTransactionId : HeapTupleGetRawXmin(tup) \
)
-#define HeapTupleHeaderSetXmin(tup, xid) \
+#define HeapTupleSetXmin(tup, xid) \
( \
- (tup)->t_choice.t_heap.t_xmin = (xid) \
+ AssertMacro(!HeapTupleIsDoubleXmax(tup)), \
+ (tup)->t_data->t_choice.t_heap.t_xmin = NormalTransactionIdToShort((tup)->t_xid_base, (xid)) \
+)
+
+#define HeapTupleHeaderSetXmin(page, tup, xid) \
+( \
+ AssertMacro(!HeapPageIsDoubleXmax(page)), \
+ (tup)->t_choice.t_heap.t_xmin = NormalTransactionIdToShort(HeapPageGetSpecial(page)->pd_xid_base, (xid)) \
)
#define HeapTupleHeaderXminCommitted(tup) \
@@ -362,30 +428,60 @@ struct HeapTupleHeaderData
* to resolve the MultiXactId if necessary. This might involve multixact I/O,
* so it should only be used if absolutely necessary.
*/
-#define HeapTupleHeaderGetUpdateXid(tup) \
+#define HeapTupleGetUpdateXidAny(tup) \
( \
- (!((tup)->t_infomask & HEAP_XMAX_INVALID) && \
- ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) && \
- !((tup)->t_infomask & HEAP_XMAX_LOCK_ONLY)) ? \
+ (!((tup)->t_data->t_infomask & HEAP_XMAX_INVALID) && \
+ ((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) && \
+ !((tup)->t_data->t_infomask & HEAP_XMAX_LOCK_ONLY)) ? \
HeapTupleGetUpdateXid(tup) \
: \
- HeapTupleHeaderGetRawXmax(tup) \
+ HeapTupleGetRawXmax(tup) \
)
-#define HeapTupleHeaderGetRawXmax(tup) \
+#define HeapTupleGetRawXmax(tup) \
( \
- (tup)->t_choice.t_heap.t_xmax \
+ HeapTupleIsDoubleXmax(tup) ? \
+ HeapTupleHeaderGetDoubleXmax((tup)->t_data) : \
+ ShortTransactionIdToNormal( \
+ ((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? (tup)->t_multi_base : (tup)->t_xid_base, \
+ (tup)->t_data->t_choice.t_heap.t_xmax) \
)
-#define HeapTupleHeaderSetXmax(tup, xid) \
+#define HeapTupleHeaderGetRawXmax(page, tup) \
( \
- (tup)->t_choice.t_heap.t_xmax = (xid) \
+ HeapPageIsDoubleXmax(page) ? \
+ HeapTupleHeaderGetDoubleXmax(tup) : \
+ ShortTransactionIdToNormal( \
+ ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) ? HeapPageGetSpecial(page)->pd_multi_base : HeapPageGetSpecial(page)->pd_xid_base, \
+ (tup)->t_choice.t_heap.t_xmax) \
)
+#define HeapTupleSetXmax(tup, xid) \
+do { \
+ if (HeapTupleIsDoubleXmax(tup)) \
+ HeapTupleHeaderSetDoubleXmax((tup)->t_data, (xid)); \
+ else \
+ (tup)->t_data->t_choice.t_heap.t_xmax = \
+ NormalTransactionIdToShort( \
+ ((tup)->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ? (tup)->t_multi_base : (tup)->t_xid_base, \
+ (xid)); \
+} while (0)
+
+#define HeapTupleHeaderSetXmax(page, tup, xid) \
+do { \
+ if (HeapPageIsDoubleXmax(tup)) \
+ HeapTupleHeaderSetDoubleXmax((tup), (xid)); \
+ else \
+ (tup)->t_choice.t_heap.t_xmax = \
+ NormalTransactionIdToShort( \
+ ((tup)->t_infomask & HEAP_XMAX_IS_MULTI) ? HeapPageGetSpecial(page)->pd_multi_base : HeapPageGetSpecial(page)->pd_xid_base, \
+ (xid)); \
+} while (0)
+
/*
* HeapTupleHeaderGetRawCommandId will give you what's in the header whether
- * it is useful or not. Most code should use HeapTupleHeaderGetCmin or
- * HeapTupleHeaderGetCmax instead, but note that those Assert that you can
+ * it is useful or not. Most code should use HeapTupleGetCmin or
+ * HeapTupleGetCmax instead, but note that those Assert that you can
* get a legitimate result, ie you are in the originating transaction!
*/
#define HeapTupleHeaderGetRawCommandId(tup) \
@@ -556,7 +652,7 @@ do { \
* ItemIds and tuples have different alignment requirements, don't assume that
* you can, say, fit 2 tuples of size MaxHeapTupleSize/2 on the same page.
*/
-#define MaxHeapTupleSize (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
+#define MaxHeapTupleSize (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)) - MAXALIGN(sizeof(HeapPageSpecialData)))
#define MinHeapTupleSize MAXALIGN(SizeofHeapTupleHeader)
/*
diff --git a/src/include/access/multixact.h b/src/include/access/multixact.h
index a5600a320ae..c474f572e20 100644
--- a/src/include/access/multixact.h
+++ b/src/include/access/multixact.h
@@ -18,16 +18,16 @@
/*
* The first two MultiXactId values are reserved to store the truncation Xid
- * and epoch of the first segment, so we start assigning multixact values from
+ * and base of the first segment, so we start assigning multixact values from
* 2.
*/
-#define InvalidMultiXactId ((MultiXactId) 0)
-#define FirstMultiXactId ((MultiXactId) 1)
-#define MaxMultiXactId ((MultiXactId) 0xFFFFFFFF)
+#define InvalidMultiXactId UINT64CONST(0)
+#define FirstMultiXactId UINT64CONST(1)
+#define MaxMultiXactId UINT64CONST(0xFFFFFFFFFFFFFFFF)
#define MultiXactIdIsValid(multi) ((multi) != InvalidMultiXactId)
-#define MaxMultiXactOffset ((MultiXactOffset) 0xFFFFFFFF)
+#define MaxMultiXactOffset UINT64CONST(0xFFFFFFFFFFFFFFFF)
/* Number of SLRU buffers to use for multixact */
#define NUM_MULTIXACTOFFSET_BUFFERS 8
@@ -114,9 +114,6 @@ extern bool MultiXactIdIsRunning(MultiXactId multi, bool isLockOnly);
extern void MultiXactIdSetOldestMember(void);
extern int GetMultiXactIdMembers(MultiXactId multi, MultiXactMember **xids,
bool allow_old, bool isLockOnly);
-extern bool MultiXactIdPrecedes(MultiXactId multi1, MultiXactId multi2);
-extern bool MultiXactIdPrecedesOrEquals(MultiXactId multi1,
- MultiXactId multi2);
extern int multixactoffsetssyncfiletag(const FileTag *ftag, char *path);
extern int multixactmemberssyncfiletag(const FileTag *ftag, char *path);
@@ -146,7 +143,6 @@ extern void MultiXactSetNextMXact(MultiXactId nextMulti,
extern void MultiXactAdvanceNextMXact(MultiXactId minMulti,
MultiXactOffset minMultiOffset);
extern void MultiXactAdvanceOldest(MultiXactId oldestMulti, Oid oldestMultiDB);
-extern int MultiXactMemberFreezeThreshold(void);
extern void multixact_twophase_recover(TransactionId xid, uint16 info,
void *recdata, uint32 len);
diff --git a/src/include/access/nbtree.h b/src/include/access/nbtree.h
index 9fec6fb1a8a..2d6157f269f 100644
--- a/src/include/access/nbtree.h
+++ b/src/include/access/nbtree.h
@@ -62,8 +62,10 @@ typedef uint16 BTCycleId;
typedef struct BTPageOpaqueData
{
BlockNumber btpo_prev; /* left sibling, or P_NONE if leftmost */
+ /* ... or next transaction ID (lower part) */
BlockNumber btpo_next; /* right sibling, or P_NONE if rightmost */
uint32 btpo_level; /* tree level --- zero for leaf pages */
+ /* ... or next transaction ID (lower part) */
uint16 btpo_flags; /* flag bits, see below */
BTCycleId btpo_cycleid; /* vacuum cycle ID of latest split */
} BTPageOpaqueData;
@@ -90,6 +92,14 @@ typedef BTPageOpaqueData *BTPageOpaque;
*/
#define MAX_BT_CYCLE_ID 0xFF7F
+/* Macros for access xact */
+#define BTP_GET_XACT(opaque) (((uint64) ((BTPageOpaque) opaque)->btpo_prev << 32) | \
+ (uint64) ((BTPageOpaque) opaque)->btpo_level)
+#define BTP_SET_XACT(opaque, xact) \
+do { \
+ ((BTPageOpaque) opaque)->btpo_prev = (uint32) (xact >> 32); \
+ ((BTPageOpaque) opaque)->btpo_level = (uint32) xact; \
+} while (0)
/*
* The Meta page is always the first page in the btree index.
diff --git a/src/include/access/rewriteheap.h b/src/include/access/rewriteheap.h
index aa5c48f219a..052789a12d7 100644
--- a/src/include/access/rewriteheap.h
+++ b/src/include/access/rewriteheap.h
@@ -51,7 +51,7 @@ typedef struct LogicalRewriteMappingData
* 6) xid of the xact performing the mapping
* ---
*/
-#define LOGICAL_REWRITE_FORMAT "map-%x-%x-%X_%X-%x-%x"
+#define LOGICAL_REWRITE_FORMAT "map-%x-%x-%X_%X-%x_%x-%x_%x"
void CheckPointLogicalRewriteHeap(void);
#endif /* REWRITE_HEAP_H */
diff --git a/src/include/access/rmgrlist.h b/src/include/access/rmgrlist.h
index 9a74721c97c..bf748ea6cba 100644
--- a/src/include/access/rmgrlist.h
+++ b/src/include/access/rmgrlist.h
@@ -47,3 +47,4 @@ PG_RMGR(RM_COMMIT_TS_ID, "CommitTs", commit_ts_redo, commit_ts_desc, commit_ts_i
PG_RMGR(RM_REPLORIGIN_ID, "ReplicationOrigin", replorigin_redo, replorigin_desc, replorigin_identify, NULL, NULL, NULL, NULL)
PG_RMGR(RM_GENERIC_ID, "Generic", generic_redo, generic_desc, generic_identify, NULL, NULL, generic_mask, NULL)
PG_RMGR(RM_LOGICALMSG_ID, "LogicalMessage", logicalmsg_redo, logicalmsg_desc, logicalmsg_identify, NULL, NULL, NULL, logicalmsg_decode)
+PG_RMGR(RM_HEAP3_ID, "Heap3", heap3_redo, heap3_desc, heap3_identify, NULL, NULL, heap_mask, NULL)
diff --git a/src/include/access/slru.h b/src/include/access/slru.h
index 130c41c8632..bd87d08bee0 100644
--- a/src/include/access/slru.h
+++ b/src/include/access/slru.h
@@ -31,7 +31,7 @@
* take no explicit notice of that fact in slru.c, except when comparing
* segment and page numbers in SimpleLruTruncate (see PagePrecedes()).
*/
-#define SLRU_PAGES_PER_SEGMENT 32
+#define SLRU_PAGES_PER_SEGMENT 2048
/*
* Page status codes. Note that these do not include the "dirty" bit.
@@ -64,7 +64,7 @@ typedef struct SlruSharedData
char **page_buffer;
SlruPageStatus *page_status;
bool *page_dirty;
- int *page_number;
+ int64 *page_number;
int *page_lru_count;
LWLockPadded *buffer_locks;
@@ -95,7 +95,7 @@ typedef struct SlruSharedData
* this is not critical data, since we use it only to avoid swapping out
* the latest page.
*/
- int latest_page_number;
+ int64 latest_page_number;
/* SLRU's index for statistics purposes (might not be unique) */
int slru_stats_idx;
@@ -127,7 +127,7 @@ typedef struct SlruCtlData
* the behavior of this callback has no functional implications.) Use
* SlruPagePrecedesUnitTests() in SLRUs meeting its criteria.
*/
- bool (*PagePrecedes) (int, int);
+ bool (*PagePrecedes) (int64, int64);
/*
* Dir is set during SimpleLruInit and does not change thereafter. Since
@@ -143,10 +143,10 @@ extern Size SimpleLruShmemSize(int nslots, int nlsns);
extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,
LWLock *ctllock, const char *subdir, int tranche_id,
SyncRequestHandler sync_handler);
-extern int SimpleLruZeroPage(SlruCtl ctl, int pageno);
-extern int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,
+extern int SimpleLruZeroPage(SlruCtl ctl, int64 pageno);
+extern int SimpleLruReadPage(SlruCtl ctl, int64 pageno, bool write_ok,
TransactionId xid);
-extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,
+extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int64 pageno,
TransactionId xid);
extern void SimpleLruWritePage(SlruCtl ctl, int slotno);
extern void SimpleLruWriteAll(SlruCtl ctl, bool allow_redirtied);
@@ -155,20 +155,20 @@ extern void SlruPagePrecedesUnitTests(SlruCtl ctl, int per_page);
#else
#define SlruPagePrecedesUnitTests(ctl, per_page) do {} while (0)
#endif
-extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);
-extern bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int pageno);
+extern void SimpleLruTruncate(SlruCtl ctl, int64 cutoffPage);
+extern bool SimpleLruDoesPhysicalPageExist(SlruCtl ctl, int64 pageno);
-typedef bool (*SlruScanCallback) (SlruCtl ctl, char *filename, int segpage,
- void *data);
+typedef bool (*SlruScanCallback) (SlruCtl ctl, char *filename, int64 segpage,
+ void *data);
extern bool SlruScanDirectory(SlruCtl ctl, SlruScanCallback callback, void *data);
-extern void SlruDeleteSegment(SlruCtl ctl, int segno);
+extern void SlruDeleteSegment(SlruCtl ctl, int64 segno);
extern int SlruSyncFileTag(SlruCtl ctl, const FileTag *ftag, char *path);
/* SlruScanDirectory public callbacks */
extern bool SlruScanDirCbReportPresence(SlruCtl ctl, char *filename,
- int segpage, void *data);
-extern bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int segpage,
+ int64 segpage, void *data);
+extern bool SlruScanDirCbDeleteAll(SlruCtl ctl, char *filename, int64 segpage,
void *data);
#endif /* SLRU_H */
diff --git a/src/include/access/tableam.h b/src/include/access/tableam.h
index bb365736b75..15c43e110b0 100644
--- a/src/include/access/tableam.h
+++ b/src/include/access/tableam.h
@@ -118,7 +118,7 @@ typedef enum TM_Result
* cmax is the outdating command's CID, but only when the failure code is
* TM_SelfModified (i.e., something in the current transaction outdated the
* tuple); otherwise cmax is zero. (We make this restriction because
- * HeapTupleHeaderGetCmax doesn't work for tuples outdated in other
+ * HeapTupleGetCmax doesn't work for tuples outdated in other
* transactions.)
*/
typedef struct TM_FailureData
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 338dfca5a0b..56750ee2bba 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -17,6 +17,10 @@
#include "access/xlogdefs.h"
+#ifndef FRONTEND
+#include "utils/elog.h"
+#endif
+
/* ----------------
* Special transaction ID values
*
@@ -28,11 +32,15 @@
* Note: if you need to change it, you must change pg_class.h as well.
* ----------------
*/
-#define InvalidTransactionId ((TransactionId) 0)
-#define BootstrapTransactionId ((TransactionId) 1)
-#define FrozenTransactionId ((TransactionId) 2)
-#define FirstNormalTransactionId ((TransactionId) 3)
-#define MaxTransactionId ((TransactionId) 0xFFFFFFFF)
+#define InvalidTransactionId UINT64CONST(0)
+#define BootstrapTransactionId UINT64CONST(1)
+#define FrozenTransactionId UINT64CONST(2)
+#define FirstNormalTransactionId UINT64CONST(3)
+#define MaxTransactionId UINT64CONST(0xFFFFFFFFFFFFFFFF)
+#define MaxShortTransactionId ((TransactionId) 0x7FFFFFFF)
+
+/* First TransactionId after upgrade from 32bit xid */
+#define FirstUpgradedTransactionId ((TransactionId) 1 << 32)
/* ----------------
* transaction ID manipulation macros
@@ -44,8 +52,37 @@
#define TransactionIdStore(xid, dest) (*(dest) = (xid))
#define StoreInvalidTransactionId(dest) (*(dest) = InvalidTransactionId)
-#define EpochFromFullTransactionId(x) ((uint32) ((x).value >> 32))
-#define XidFromFullTransactionId(x) ((uint32) (x).value)
+/*
+ * Convert short xid from/to full xid. Assertion should fail if we full xid
+ * doesn't fit to xid base.
+ */
+#define ShortTransactionIdToNormal(base, xid) \
+ (TransactionIdIsNormal(xid) ? (TransactionId)(xid) + (base) : (TransactionId)(xid))
+
+#ifdef USE_ASSERT_CHECKING
+static inline ShortTransactionId
+NormalTransactionIdToShort(TransactionId base, TransactionId xid)
+{
+ if (!TransactionIdIsNormal(xid))
+ return (ShortTransactionId)(xid);
+
+#ifndef FRONTEND
+ if (xid < base + FirstNormalTransactionId ||
+ xid > base + MaxShortTransactionId)
+ elog(PANIC, "Xid " XID_FMT " does not fit into valid range for base " XID_FMT, xid, base);
+#endif
+
+ return (ShortTransactionId)(xid - base);
+}
+#else
+#define NormalTransactionIdToShort(base, xid) \
+ (TransactionIdIsNormal(xid) ? (ShortTransactionId)( \
+ AssertMacro((xid) >= (base) + FirstNormalTransactionId), \
+ AssertMacro((xid) <= (base) + MaxShortTransactionId), \
+ (xid) - (base)) : (ShortTransactionId)(xid))
+#endif
+
+#define XidFromFullTransactionId(x) ((x).value)
#define U64FromFullTransactionId(x) ((x).value)
#define FullTransactionIdEquals(a, b) ((a).value == (b).value)
#define FullTransactionIdPrecedes(a, b) ((a).value < (b).value)
@@ -53,8 +90,8 @@
#define FullTransactionIdFollows(a, b) ((a).value > (b).value)
#define FullTransactionIdFollowsOrEquals(a, b) ((a).value >= (b).value)
#define FullTransactionIdIsValid(x) TransactionIdIsValid(XidFromFullTransactionId(x))
-#define InvalidFullTransactionId FullTransactionIdFromEpochAndXid(0, InvalidTransactionId)
-#define FirstNormalFullTransactionId FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId)
+#define InvalidFullTransactionId FullTransactionIdFromXid(InvalidTransactionId)
+#define FirstNormalFullTransactionId FullTransactionIdFromXid(FirstNormalTransactionId)
#define FullTransactionIdIsNormal(x) FullTransactionIdFollowsOrEquals(x, FirstNormalFullTransactionId)
/*
@@ -68,11 +105,11 @@ typedef struct FullTransactionId
} FullTransactionId;
static inline FullTransactionId
-FullTransactionIdFromEpochAndXid(uint32 epoch, TransactionId xid)
+FullTransactionIdFromXid(TransactionId xid)
{
FullTransactionId result;
- result.value = ((uint64) epoch) << 32 | xid;
+ result.value = xid;
return result;
}
@@ -91,8 +128,7 @@ FullTransactionIdFromU64(uint64 value)
#define TransactionIdAdvance(dest) \
do { \
(dest)++; \
- if ((dest) < FirstNormalTransactionId) \
- (dest) = FirstNormalTransactionId; \
+ Assert((dest) > FirstNormalTransactionId); \
} while(0)
/*
@@ -140,18 +176,19 @@ FullTransactionIdAdvance(FullTransactionId *dest)
/* back up a transaction ID variable, handling wraparound correctly */
#define TransactionIdRetreat(dest) \
do { \
+ Assert((dest) > FirstNormalTransactionId); \
(dest)--; \
- } while ((dest) < FirstNormalTransactionId)
+ } while(0)
/* compare two XIDs already known to be normal; this is a macro for speed */
#define NormalTransactionIdPrecedes(id1, id2) \
(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
- (int32) ((id1) - (id2)) < 0)
+ (int64) ((id1) - (id2)) < 0)
/* compare two XIDs already known to be normal; this is a macro for speed */
#define NormalTransactionIdFollows(id1, id2) \
(AssertMacro(TransactionIdIsNormal(id1) && TransactionIdIsNormal(id2)), \
- (int32) ((id1) - (id2)) > 0)
+ (int64) ((id1) - (id2)) > 0)
/* ----------
* Object ID (OID) zero is InvalidOid.
@@ -221,9 +258,6 @@ typedef struct VariableCacheData
TransactionId oldestXid; /* cluster-wide minimum datfrozenxid */
TransactionId xidVacLimit; /* start forcing autovacuums here */
- TransactionId xidWarnLimit; /* start complaining here */
- TransactionId xidStopLimit; /* refuse to advance nextXid beyond here */
- TransactionId xidWrapLimit; /* where the world ends */
Oid oldestXidDB; /* database with minimum datfrozenxid */
/*
@@ -277,10 +311,6 @@ extern bool TransactionIdIsKnownCompleted(TransactionId transactionId);
extern void TransactionIdCommitTree(TransactionId xid, int nxids, TransactionId *xids);
extern void TransactionIdAsyncCommitTree(TransactionId xid, int nxids, TransactionId *xids, XLogRecPtr lsn);
extern void TransactionIdAbortTree(TransactionId xid, int nxids, TransactionId *xids);
-extern bool TransactionIdPrecedes(TransactionId id1, TransactionId id2);
-extern bool TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2);
-extern bool TransactionIdFollows(TransactionId id1, TransactionId id2);
-extern bool TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2);
extern TransactionId TransactionIdLatest(TransactionId mainxid,
int nxids, const TransactionId *xids);
extern XLogRecPtr TransactionIdGetCommitLSN(TransactionId xid);
diff --git a/src/include/access/xact.h b/src/include/access/xact.h
index 17a6fa4abdb..ff605c5f0ae 100644
--- a/src/include/access/xact.h
+++ b/src/include/access/xact.h
@@ -226,7 +226,7 @@ typedef struct xl_xact_xinfo
* four so following records don't have to care about alignment. Commit
* records can be large, so copying large portions isn't attractive.
*/
- uint32 xinfo;
+ uint64 xinfo;
} xl_xact_xinfo;
typedef struct xl_xact_dbinfo
@@ -258,7 +258,12 @@ typedef struct xl_xact_invals
typedef struct xl_xact_twophase
{
- TransactionId xid;
+ /*
+ * TransactionId is split into 32-bit parts because
+ * xl_xact_twophase is only int-aligned.
+ */
+ uint32 xid_lo;
+ uint32 xid_hi;
} xl_xact_twophase;
typedef struct xl_xact_origin
@@ -276,7 +281,7 @@ typedef struct xl_xact_commit
/* xl_xact_subxacts follows if XINFO_HAS_SUBXACT */
/* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */
/* xl_xact_invals follows if XINFO_HAS_INVALS */
- /* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
+ /* xl_xact_twophase follows if XINFO_HAS_TWOPHASE (xid is int-aligned!) */
/* twophase_gid follows if XINFO_HAS_GID. As a null-terminated string. */
/* xl_xact_origin follows if XINFO_HAS_ORIGIN, stored unaligned! */
} xl_xact_commit;
@@ -291,7 +296,7 @@ typedef struct xl_xact_abort
/* xl_xact_subxacts follows if XINFO_HAS_SUBXACT */
/* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */
/* No invalidation messages needed. */
- /* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
+ /* xl_xact_twophase follows if XINFO_HAS_TWOPHASE (xid is int-aligned!) */
/* twophase_gid follows if XINFO_HAS_GID. As a null-terminated string. */
/* xl_xact_origin follows if XINFO_HAS_ORIGIN, stored unaligned! */
} xl_xact_abort;
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index bb0c52686a5..ab7eeddcb88 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -149,6 +149,14 @@ typedef enum RecoveryPauseState
extern PGDLLIMPORT int wal_level;
+/*
+ * these parameters specifies starting xid, multixact id and multixact offset
+ * for testing 64 bit xids
+ */
+extern TransactionId start_xid;
+extern MultiXactId start_mx_id;
+extern MultiXactOffset start_mx_offset;
+
/* Is WAL archiving enabled (always or only while server is running normally)? */
#define XLogArchivingActive() \
(AssertMacro(XLogArchiveMode == ARCHIVE_MODE_OFF || wal_level >= WAL_LEVEL_REPLICA), XLogArchiveMode > ARCHIVE_MODE_OFF)
diff --git a/src/include/access/xlogreader.h b/src/include/access/xlogreader.h
index 477f0efe26a..945e9eb66e8 100644
--- a/src/include/access/xlogreader.h
+++ b/src/include/access/xlogreader.h
@@ -327,10 +327,6 @@ extern bool DecodeXLogRecord(XLogReaderState *state, XLogRecord *record,
#define XLogRecBlockImageApply(decoder, block_id) \
((decoder)->blocks[block_id].apply_image)
-#ifndef FRONTEND
-extern FullTransactionId XLogRecGetFullXid(XLogReaderState *record);
-#endif
-
extern bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page);
extern char *XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len);
extern bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id,
diff --git a/src/include/c.h b/src/include/c.h
index 4f16e589b3e..cb3c4cdd84d 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -75,6 +75,10 @@
#include <libintl.h>
#endif
+#if HAVE_INTTYPES_H
+#include "inttypes.h"
+#endif
+
/* ----------------------------------------------------------------
* Section 1: compiler characteristics
@@ -483,6 +487,16 @@ typedef unsigned long long int uint64;
#define INT64_FORMAT "%" INT64_MODIFIER "d"
#define UINT64_FORMAT "%" INT64_MODIFIER "u"
+/*
+ * Used to make translatable strings.
+ * Also on macOS externally defined PRIu64 doesn't match postgres int64
+ * definition causing strom of printf warnings. So redefine it anyway. -- sk.
+ */
+#ifdef PRIu64
+#undef PRIu64
+#endif
+#define PRIu64 INT64_MODIFIER "u"
+
/*
* 128-bit signed and unsigned integers
* There currently is only limited support for such types.
@@ -584,19 +598,41 @@ typedef double float8;
typedef Oid regproc;
typedef regproc RegProcedure;
-typedef uint32 TransactionId;
+#define MAX_START_XID UINT64CONST(0x3fffffffffffffff)
+
+typedef uint64 TransactionId;
-typedef uint32 LocalTransactionId;
+#define TransactionIdPrecedes(id1, id2) ((id1) < (id2))
+#define TransactionIdPrecedesOrEquals(id1, id2) ((id1) <= (id2))
+#define TransactionIdFollows(id1, id2) ((id1) > (id2))
+#define TransactionIdFollowsOrEquals(id1, id2) ((id1) >= (id2))
-typedef uint32 SubTransactionId;
+#define StartTransactionIdIsValid(start_xid) ((start_xid) <= MAX_START_XID)
+
+typedef uint32 ShortTransactionId;
+
+typedef uint64 LocalTransactionId;
+
+typedef uint64 SubTransactionId;
#define InvalidSubTransactionId ((SubTransactionId) 0)
#define TopSubTransactionId ((SubTransactionId) 1)
+#define XID_FMT UINT64_FORMAT
+
/* MultiXactId must be equivalent to TransactionId, to fit in t_xmax */
typedef TransactionId MultiXactId;
-typedef uint32 MultiXactOffset;
+#define MultiXactIdPrecedes(id1, id2) ((id1) < (id2))
+#define MultiXactIdPrecedesOrEquals(id1, id2) ((id1) <= (id2))
+#define MultiXactIdFollows(id1, id2) ((id1) > (id2))
+#define MultiXactIdFollowsOrEquals(id1, id2) ((id1) >= (id2))
+
+#define StartMultiXactIdIsValid(start_mx_id) ((start_mx_id) <= MAX_START_XID)
+
+typedef uint64 MultiXactOffset;
+
+#define StartMultiXactOffsetIsValid(start_mx_offset) ((start_mx_offset) <= MAX_START_XID)
typedef uint32 CommandId;
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 4cc129bebd8..4f209776350 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -403,9 +403,9 @@
amprocrighttype => 'bytea', amprocnum => '2',
amproc => 'hashvarlenaextended' },
{ amprocfamily => 'hash/xid_ops', amproclefttype => 'xid',
- amprocrighttype => 'xid', amprocnum => '1', amproc => 'hashint4' },
+ amprocrighttype => 'xid', amprocnum => '1', amproc => 'hashint8' },
{ amprocfamily => 'hash/xid_ops', amproclefttype => 'xid',
- amprocrighttype => 'xid', amprocnum => '2', amproc => 'hashint4extended' },
+ amprocrighttype => 'xid', amprocnum => '2', amproc => 'hashint8extended' },
{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
amprocrighttype => 'xid8', amprocnum => '1', amproc => 'hashint8' },
{ amprocfamily => 'hash/xid8_ops', amproclefttype => 'xid8',
diff --git a/src/include/catalog/pg_control.h b/src/include/catalog/pg_control.h
index 1f3dc24ac11..8ca122777dd 100644
--- a/src/include/catalog/pg_control.h
+++ b/src/include/catalog/pg_control.h
@@ -249,4 +249,10 @@ typedef struct ControlFileData
*/
#define PG_CONTROL_FILE_SIZE 8192
+#define CONTROLFILE_GET_OLDEDITION(control) \
+ ((control)->pg_old_version >> 16)
+
+#define CONTROLFILE_SET_OLDEDITION(control, v) \
+ (control)->pg_old_version = ((v) << 16)
+
#endif /* PG_CONTROL_H */
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index bc5f8213f3a..912ab48ecf4 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -183,16 +183,16 @@
oprresult => 'bool', oprcom => '=(xid,xid)', oprnegate => '<>(xid,xid)',
oprcode => 'xideq', oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
{ oid => '353', descr => 'equal',
- oprname => '=', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
- oprnegate => '<>(xid,int4)', oprcode => 'xideqint4', oprrest => 'eqsel',
+ oprname => '=', oprleft => 'xid', oprright => 'int8', oprresult => 'bool',
+ oprnegate => '<>(xid,int8)', oprcode => 'xideqint8', oprrest => 'eqsel',
oprjoin => 'eqjoinsel' },
{ oid => '3315', descr => 'not equal',
oprname => '<>', oprleft => 'xid', oprright => 'xid', oprresult => 'bool',
oprcom => '<>(xid,xid)', oprnegate => '=(xid,xid)', oprcode => 'xidneq',
oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
{ oid => '3316', descr => 'not equal',
- oprname => '<>', oprleft => 'xid', oprright => 'int4', oprresult => 'bool',
- oprnegate => '=(xid,int4)', oprcode => 'xidneqint4', oprrest => 'neqsel',
+ oprname => '<>', oprleft => 'xid', oprright => 'int8', oprresult => 'bool',
+ oprnegate => '=(xid,int8)', oprcode => 'xidneqint8', oprrest => 'neqsel',
oprjoin => 'neqjoinsel' },
{ oid => '5068', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'xid8',
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 0859dc81cac..99e6fb4f752 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -2360,10 +2360,10 @@
{ oid => '1181',
descr => 'age of a transaction ID, in transactions before current transaction',
proname => 'age', provolatile => 's', proparallel => 'r',
- prorettype => 'int4', proargtypes => 'xid', prosrc => 'xid_age' },
+ prorettype => 'int8', proargtypes => 'xid', prosrc => 'xid_age' },
{ oid => '3939',
descr => 'age of a multi-transaction ID, in multi-transactions before current multi-transaction',
- proname => 'mxid_age', provolatile => 's', prorettype => 'int4',
+ proname => 'mxid_age', provolatile => 's', prorettype => 'int8',
proargtypes => 'xid', prosrc => 'mxid_age' },
{ oid => '1188',
@@ -2698,11 +2698,11 @@
prosrc => 'bpcharlen' },
{ oid => '1319',
- proname => 'xideqint4', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid int4', prosrc => 'xideq' },
+ proname => 'xideqint8', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid int8', prosrc => 'xideq' },
{ oid => '3309',
- proname => 'xidneqint4', proleakproof => 't', prorettype => 'bool',
- proargtypes => 'xid int4', prosrc => 'xidneq' },
+ proname => 'xidneqint8', proleakproof => 't', prorettype => 'bool',
+ proargtypes => 'xid int8', prosrc => 'xidneq' },
{ oid => '1326',
proname => 'interval_div', prorettype => 'interval',
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index df458794635..48e864eeb90 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -95,9 +95,9 @@
typinput => 'tidin', typoutput => 'tidout', typreceive => 'tidrecv',
typsend => 'tidsend', typalign => 's' },
{ oid => '28', array_type_oid => '1011', descr => 'transaction id',
- typname => 'xid', typlen => '4', typbyval => 't', typcategory => 'U',
+ typname => 'xid', typlen => '8', typbyval => 'FLOAT8PASSBYVAL', typcategory => 'U',
typinput => 'xidin', typoutput => 'xidout', typreceive => 'xidrecv',
- typsend => 'xidsend', typalign => 'i' },
+ typsend => 'xidsend', typalign => 'd' },
{ oid => '29', array_type_oid => '1012',
descr => 'command identifier type, sequence in transaction id',
typname => 'cid', typlen => '4', typbyval => 't', typcategory => 'U',
diff --git a/src/include/commands/vacuum.h b/src/include/commands/vacuum.h
index 5d0bdfa4279..98288a48996 100644
--- a/src/include/commands/vacuum.h
+++ b/src/include/commands/vacuum.h
@@ -213,12 +213,12 @@ typedef enum VacOptValue
*/
typedef struct VacuumParams
{
- bits32 options; /* bitmask of VACOPT_* */
- int freeze_min_age; /* min freeze age, -1 to use default */
- int freeze_table_age; /* age at which to scan whole table */
- int multixact_freeze_min_age; /* min multixact freeze age, -1 to
+ bits32 options; /* bitmask of VacuumOption */
+ int64 freeze_min_age; /* min freeze age, -1 to use default */
+ int64 freeze_table_age; /* age at which to scan whole table */
+ int64 multixact_freeze_min_age; /* min multixact freeze age, -1 to
* use default */
- int multixact_freeze_table_age; /* multixact age at which to scan
+ int64 multixact_freeze_table_age; /* multixact age at which to scan
* whole table */
bool is_wraparound; /* force a for-wraparound vacuum */
int log_min_duration; /* minimum execution threshold in ms at
@@ -252,12 +252,12 @@ typedef struct VacDeadItems
/* GUC parameters */
extern PGDLLIMPORT int default_statistics_target; /* PGDLLIMPORT for PostGIS */
-extern int vacuum_freeze_min_age;
-extern int vacuum_freeze_table_age;
-extern int vacuum_multixact_freeze_min_age;
-extern int vacuum_multixact_freeze_table_age;
-extern int vacuum_failsafe_age;
-extern int vacuum_multixact_failsafe_age;
+extern int64 vacuum_freeze_min_age;
+extern int64 vacuum_freeze_table_age;
+extern int64 vacuum_multixact_freeze_min_age;
+extern int64 vacuum_multixact_freeze_table_age;
+extern int64 vacuum_failsafe_age;
+extern int64 vacuum_multixact_failsafe_age;
/* Variables for cost-based parallel vacuum */
extern pg_atomic_uint32 *VacuumSharedCostBalance;
@@ -285,9 +285,9 @@ extern void vac_update_relstats(Relation relation,
MultiXactId minmulti,
bool in_outer_xact);
extern void vacuum_set_xid_limits(Relation rel,
- int freeze_min_age, int freeze_table_age,
- int multixact_freeze_min_age,
- int multixact_freeze_table_age,
+ int64 freeze_min_age, int64 freeze_table_age,
+ int64 multixact_freeze_min_age,
+ int64 multixact_freeze_table_age,
TransactionId *oldestXmin,
TransactionId *freezeLimit,
TransactionId *xidFullScanLimit,
diff --git a/src/include/fmgr.h b/src/include/fmgr.h
index 6560e462d66..0eee14afd70 100644
--- a/src/include/fmgr.h
+++ b/src/include/fmgr.h
@@ -281,6 +281,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
#define PG_GETARG_FLOAT4(n) DatumGetFloat4(PG_GETARG_DATUM(n))
#define PG_GETARG_FLOAT8(n) DatumGetFloat8(PG_GETARG_DATUM(n))
#define PG_GETARG_INT64(n) DatumGetInt64(PG_GETARG_DATUM(n))
+#define PG_GETARG_TRANSACTIONID(n) DatumGetTransactionId(PG_GETARG_DATUM(n))
/* use this if you want the raw, possibly-toasted input datum: */
#define PG_GETARG_RAW_VARLENA_P(n) ((struct varlena *) PG_GETARG_POINTER(n))
/* use this if you want the input datum de-toasted: */
@@ -367,6 +368,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum);
#define PG_RETURN_FLOAT8(x) return Float8GetDatum(x)
#define PG_RETURN_INT64(x) return Int64GetDatum(x)
#define PG_RETURN_UINT64(x) return UInt64GetDatum(x)
+#define PG_RETURN_TRANSACTIONID(x) return TransactionIdGetDatum(x)
/* RETURN macros for other pass-by-ref types will typically look like this: */
#define PG_RETURN_BYTEA_P(x) PG_RETURN_POINTER(x)
#define PG_RETURN_TEXT_P(x) PG_RETURN_POINTER(x)
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 9d9bd6b9efc..50804edbcc5 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -965,6 +965,9 @@
# endif
#endif
+/* Postgres Pro use 64bit xids */
+#undef XID_IS_64BIT
+
/* Size of a WAL file block. This need have no particular relation to BLCKSZ.
XLOG_BLCKSZ must be a power of 2, and if your system supports O_DIRECT I/O,
XLOG_BLCKSZ must be a multiple of the alignment requirement for direct-I/O
diff --git a/src/include/postgres.h b/src/include/postgres.h
index 31358110dca..e8b4b1f9dfb 100644
--- a/src/include/postgres.h
+++ b/src/include/postgres.h
@@ -555,21 +555,21 @@ typedef struct NullableDatum
* Returns transaction identifier value of a datum.
*/
-#define DatumGetTransactionId(X) ((TransactionId) (X))
+#define DatumGetTransactionId(X) (DatumGetUInt64(X))
/*
* TransactionIdGetDatum
* Returns datum representation for a transaction identifier.
*/
-#define TransactionIdGetDatum(X) ((Datum) (X))
+#define TransactionIdGetDatum(X) (UInt64GetDatum(X))
/*
* MultiXactIdGetDatum
* Returns datum representation for a multixact identifier.
*/
-#define MultiXactIdGetDatum(X) ((Datum) (X))
+#define MultiXactIdGetDatum(X) (UInt64GetDatum(X))
/*
* DatumGetCommandId
diff --git a/src/include/postmaster/autovacuum.h b/src/include/postmaster/autovacuum.h
index 30a2b3274f8..099bd827baa 100644
--- a/src/include/postmaster/autovacuum.h
+++ b/src/include/postmaster/autovacuum.h
@@ -37,8 +37,8 @@ extern int autovacuum_vac_ins_thresh;
extern double autovacuum_vac_ins_scale;
extern int autovacuum_anl_thresh;
extern double autovacuum_anl_scale;
-extern int autovacuum_freeze_max_age;
-extern int autovacuum_multixact_freeze_max_age;
+extern int64 autovacuum_freeze_max_age;
+extern int64 autovacuum_multixact_freeze_max_age;
extern double autovacuum_vac_cost_delay;
extern int autovacuum_vac_cost_limit;
diff --git a/src/include/storage/bufmgr.h b/src/include/storage/bufmgr.h
index dd01841c300..d649574e3b1 100644
--- a/src/include/storage/bufmgr.h
+++ b/src/include/storage/bufmgr.h
@@ -245,6 +245,8 @@ extern void TestForOldSnapshot_impl(Snapshot snapshot, Relation relation);
extern BufferAccessStrategy GetAccessStrategy(BufferAccessStrategyType btype);
extern void FreeAccessStrategy(BufferAccessStrategy strategy);
+/* old tuple format support */
+extern void convert_page(Relation rel, Page orig_page, Buffer buf, BlockNumber blkno);
/* inline functions */
diff --git a/src/include/storage/bufpage.h b/src/include/storage/bufpage.h
index e9f253f2c8a..63837e86411 100644
--- a/src/include/storage/bufpage.h
+++ b/src/include/storage/bufpage.h
@@ -159,12 +159,84 @@ typedef struct PageHeaderData
LocationIndex pd_upper; /* offset to end of free space */
LocationIndex pd_special; /* offset to start of special space */
uint16 pd_pagesize_version;
- TransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
+ ShortTransactionId pd_prune_xid; /* oldest prunable XID, or zero if none */
ItemIdData pd_linp[FLEXIBLE_ARRAY_MEMBER]; /* line pointer array */
} PageHeaderData;
typedef PageHeaderData *PageHeader;
+
+/*
+ * HeapPageSpecialData -- data that stored at the end of each heap page.
+ *
+ * pd_xid_base - base value for transaction IDs on page
+ * pd_multi_base - base value for multixact IDs on page
+ *
+ * pd_xid_base and pd_multi_base are base values for calculation of transaction
+ * identifiers from t_xmin and t_xmax in each heap tuple header on the page.
+ */
+typedef struct HeapPageSpecialData
+{
+ TransactionId pd_xid_base; /* base value for transaction IDs on page */
+ TransactionId pd_multi_base; /* base value for multixact IDs on page */
+} HeapPageSpecialData;
+
+#define SizeOfPageSpecial MAXALIGN(sizeof(HeapPageSpecialData))
+
+typedef HeapPageSpecialData *HeapPageSpecial;
+
+extern PGDLLIMPORT HeapPageSpecial doubleXmaxSpecial;
+
+/*
+ * Get pointer to HeapPageSpecialData without using pd_special of the page
+ * (for the sake of speed) assuming all heap pages have same size of special
+ * data.
+ *
+ * Return doubleXmaxSpecial when pd_special == BLCKSZ. See comment in bufpage.c
+ * for details.
+ */
+#define HeapPageGetSpecial(page) ( \
+ (((PageHeader) (page))->pd_special == BLCKSZ) ? \
+ ((HeapPageSpecial) doubleXmaxSpecial) : \
+ (AssertMacro(((PageHeader) (page))->pd_special == BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData))), \
+ (HeapPageSpecial) ((Pointer) (page) + BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData)))) \
+)
+
+/*
+ * Version of HeapPageGetSpecial() without assertions about pd_special. Used
+ * for non-consistent reads from non-locked pages.
+ */
+#define HeapPageGetSpecialNoAssert(page) ( \
+ (((PageHeader) (page))->pd_special == BLCKSZ) ? \
+ ((HeapPageSpecial) doubleXmaxSpecial) : \
+ (HeapPageSpecial) ((Pointer) (page) + BLCKSZ - MAXALIGN(sizeof(HeapPageSpecialData))) \
+)
+
+ShortTransactionId HeapPageSetPruneXidInternal(Page page, TransactionId xid);
+
+#define HeapPageSetPruneXid(page, xid) \
+ HeapPageSetPruneXidInternal((Page)(page), xid)
+
+#define HeapPageGetPruneXid(page) \
+( \
+ ShortTransactionIdToNormal(HeapPageGetSpecial(page)->pd_xid_base, ((PageHeader) (page))->pd_prune_xid) \
+)
+
+/*
+ * Read pd_prune_xid from non-locked page. May return invalid value, but doen't
+ * causes assert failures.
+ */
+#define HeapPageGetPruneXidNoAssert(page) \
+( \
+ ShortTransactionIdToNormal(HeapPageGetSpecialNoAssert(page)->pd_xid_base, ((PageHeader) (page))->pd_prune_xid) \
+)
+
+#define XidFitsPage(page, xid) \
+( \
+ (xid) >= HeapPageGetSpecial(page)->pd_xid_base + FirstNormalTransactionId && \
+ (xid) <= HeapPageGetSpecial(page)->pd_xid_base + MaxShortTransactionId \
+)
+
/*
* pd_flags contains the following flag bits. Undefined bits are initialized
* to zero and may be used in the future.
@@ -192,11 +264,13 @@ typedef PageHeaderData *PageHeader;
* Release 8.3 uses 4; it changed the HeapTupleHeader layout again, and
* added the pd_flags field (by stealing some bits from pd_tli),
* as well as adding the pd_prune_xid field (which enlarges the header).
+ * PgPro Enterprise 10 uses version number (0x00FF - 1), and should not
+ * collide with vanilla versions due to page conversion after pg_upgrade.
*
* As of Release 9.3, the checksum version must also be considered when
* handling pages.
*/
-#define PG_PAGE_LAYOUT_VERSION 4
+#define PG_PAGE_LAYOUT_VERSION 5
#define PG_DATA_CHECKSUM_VERSION 1
/* ----------------------------------------------------------------
@@ -389,16 +463,18 @@ PageValidateSpecialPointer(Page page)
#define PageClearAllVisible(page) \
(((PageHeader) (page))->pd_flags &= ~PD_ALL_VISIBLE)
+/* Check if page is in "double xmax" format */
+#define HeapPageIsDoubleXmax(page) \
+ (((PageHeader) (page))->pd_special == BLCKSZ)
+
#define PageSetPrunable(page, xid) \
do { \
Assert(TransactionIdIsNormal(xid)); \
- if (!TransactionIdIsValid(((PageHeader) (page))->pd_prune_xid) || \
- TransactionIdPrecedes(xid, ((PageHeader) (page))->pd_prune_xid)) \
- ((PageHeader) (page))->pd_prune_xid = (xid); \
+ if (!HeapPageIsDoubleXmax(page) && \
+ (!TransactionIdIsValid(HeapPageGetPruneXid(page)) || \
+ TransactionIdPrecedes(xid, HeapPageGetPruneXid(page)))) \
+ HeapPageSetPruneXid(page, xid); \
} while (0)
-#define PageClearPrunable(page) \
- (((PageHeader) (page))->pd_prune_xid = InvalidTransactionId)
-
/* ----------------------------------------------------------------
* extern declarations
@@ -432,6 +508,19 @@ do { \
StaticAssertDecl(BLCKSZ == ((BLCKSZ / sizeof(size_t)) * sizeof(size_t)),
"BLCKSZ has to be a multiple of sizeof(size_t)");
+/*
+ * Tuple defrag support for PageRepairFragmentation and PageIndexMultiDelete
+ */
+typedef struct itemIdCompactData
+{
+ uint16 offsetindex; /* linp array index */
+ int16 itemoff; /* page offset of item data */
+ uint16 alignedlen; /* MAXALIGN(item data len) */
+} itemIdCompactData;
+typedef itemIdCompactData *itemIdCompact;
+
+extern int itemoffcompare(const void *item1, const void *item2);
+
extern void PageInit(Page page, Size pageSize, Size specialSize);
extern bool PageIsVerifiedExtended(Page page, BlockNumber blkno, int flags);
extern OffsetNumber PageAddItemExtended(Page page, Item item, Size size,
diff --git a/src/include/storage/itemid.h b/src/include/storage/itemid.h
index e33637ff21e..442a72d658d 100644
--- a/src/include/storage/itemid.h
+++ b/src/include/storage/itemid.h
@@ -78,6 +78,8 @@ typedef uint16 ItemLength;
#define ItemIdGetRedirect(itemId) \
((itemId)->lp_off)
+#define ItemIdGetTupleEnd(itemId) \
+ (MAXALIGN(ItemIdGetLength((itemId))) + ItemIdGetOffset((itemId)))
/*
* ItemIdIsValid
* True iff item identifier is valid.
diff --git a/src/include/storage/lock.h b/src/include/storage/lock.h
index dc537e20f2c..c4d2d8a1222 100644
--- a/src/include/storage/lock.h
+++ b/src/include/storage/lock.h
@@ -227,8 +227,8 @@ typedef struct LOCKTAG
/* ID info for a transaction is its TransactionId */
#define SET_LOCKTAG_TRANSACTION(locktag,xid) \
- ((locktag).locktag_field1 = (xid), \
- (locktag).locktag_field2 = 0, \
+ ((locktag).locktag_field1 = (uint32)((xid) & 0xFFFFFFFF), \
+ (locktag).locktag_field2 = (uint32)((xid) >> 32), \
(locktag).locktag_field3 = 0, \
(locktag).locktag_field4 = 0, \
(locktag).locktag_type = LOCKTAG_TRANSACTION, \
@@ -237,8 +237,8 @@ typedef struct LOCKTAG
/* ID info for a virtual transaction is its VirtualTransactionId */
#define SET_LOCKTAG_VIRTUALTRANSACTION(locktag,vxid) \
((locktag).locktag_field1 = (vxid).backendId, \
- (locktag).locktag_field2 = (vxid).localTransactionId, \
- (locktag).locktag_field3 = 0, \
+ (locktag).locktag_field2 = (uint32)((vxid).localTransactionId & 0xFFFFFFFF), \
+ (locktag).locktag_field3 = (uint32)((vxid).localTransactionId >> 32), \
(locktag).locktag_field4 = 0, \
(locktag).locktag_type = LOCKTAG_VIRTUALTRANSACTION, \
(locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
@@ -248,9 +248,9 @@ typedef struct LOCKTAG
* its speculative insert counter.
*/
#define SET_LOCKTAG_SPECULATIVE_INSERTION(locktag,xid,token) \
- ((locktag).locktag_field1 = (xid), \
- (locktag).locktag_field2 = (token), \
- (locktag).locktag_field3 = 0, \
+ ((locktag).locktag_field1 = (uint32)((xid) & 0xFFFFFFFF), \
+ (locktag).locktag_field2 = (uint32)((xid) >> 32), \
+ (locktag).locktag_field3 = (token), \
(locktag).locktag_field4 = 0, \
(locktag).locktag_type = LOCKTAG_SPECULATIVE_TOKEN, \
(locktag).locktag_lockmethodid = DEFAULT_LOCKMETHOD)
diff --git a/src/include/storage/proc.h b/src/include/storage/proc.h
index a58888f9e90..e86edd66a63 100644
--- a/src/include/storage/proc.h
+++ b/src/include/storage/proc.h
@@ -238,7 +238,7 @@ struct PGPROC
TransactionId clogGroupMemberXid; /* transaction id of clog group member */
XidStatus clogGroupMemberXidStatus; /* transaction status of clog
* group member */
- int clogGroupMemberPage; /* clog page corresponding to
+ int64 clogGroupMemberPage; /* clog page corresponding to
* transaction id of clog group member */
XLogRecPtr clogGroupMemberLsn; /* WAL location of commit record for clog
* group member */
diff --git a/src/include/storage/standby.h b/src/include/storage/standby.h
index 58ea21aa132..5094c50a53f 100644
--- a/src/include/storage/standby.h
+++ b/src/include/storage/standby.h
@@ -21,7 +21,7 @@
#include "storage/standbydefs.h"
/* User-settable GUC parameters */
-extern int vacuum_defer_cleanup_age;
+extern int64 vacuum_defer_cleanup_age;
extern int max_standby_archive_delay;
extern int max_standby_streaming_delay;
extern bool log_recovery_conflict_waits;
diff --git a/src/include/storage/sync.h b/src/include/storage/sync.h
index 9737e1eb67c..35fc1dff1ab 100644
--- a/src/include/storage/sync.h
+++ b/src/include/storage/sync.h
@@ -52,7 +52,7 @@ typedef struct FileTag
int16 handler; /* SyncRequestHandler value, saving space */
int16 forknum; /* ForkNumber, saving space */
RelFileNode rnode;
- uint32 segno;
+ uint64 segno;
} FileTag;
extern void InitSync(void);
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 7ac4780e3fc..597c7f31c4b 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -53,6 +53,7 @@ extern int pg_ltoa(int32 l, char *a);
extern int pg_lltoa(int64 ll, char *a);
extern char *pg_ultostr_zeropad(char *str, uint32 value, int32 minwidth);
extern char *pg_ultostr(char *str, uint32 value);
+extern uint64 pg_strtouint64(const char *s, char **endptr, int base);
/* oid.c */
extern oidvector *buildoidvector(const Oid *oids, int n);
diff --git a/src/include/utils/combocid.h b/src/include/utils/combocid.h
index 80fe6d2ceac..8465768b6f1 100644
--- a/src/include/utils/combocid.h
+++ b/src/include/utils/combocid.h
@@ -15,7 +15,7 @@
#define COMBOCID_H
/*
- * HeapTupleHeaderGetCmin and HeapTupleHeaderGetCmax function prototypes
+ * HeapTupleGetCmin and HeapTupleGetCmax function prototypes
* are in access/htup.h, because that's where the macro definitions that
* those functions replaced used to be.
*/
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 6da1b220cdc..e77600a0f26 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -295,12 +295,12 @@ typedef struct AutoVacOpts
int vacuum_ins_threshold;
int analyze_threshold;
int vacuum_cost_limit;
- int freeze_min_age;
- int freeze_max_age;
- int freeze_table_age;
- int multixact_freeze_min_age;
- int multixact_freeze_max_age;
- int multixact_freeze_table_age;
+ int64 freeze_min_age;
+ int64 freeze_max_age;
+ int64 freeze_table_age;
+ int64 multixact_freeze_min_age;
+ int64 multixact_freeze_max_age;
+ int64 multixact_freeze_table_age;
int log_min_duration;
float8 vacuum_cost_delay;
float8 vacuum_scale_factor;
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 1c77211ac45..d0113fb8972 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -2659,7 +2659,7 @@ validate_plperl_function(plperl_proc_ptr *proc_ptr, HeapTuple procTup)
* This is needed because CREATE OR REPLACE FUNCTION can modify the
* function's pg_proc entry without changing its OID.
************************************************************/
- uptodate = (prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
+ uptodate = (prodesc->fn_xmin == HeapTupleGetRawXmin(procTup) &&
ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self));
if (uptodate)
@@ -2783,7 +2783,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger, bool is_event_trigger)
MemoryContextSetIdentifier(proc_cxt, prodesc->proname);
prodesc->fn_cxt = proc_cxt;
prodesc->fn_refcount = 0;
- prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
+ prodesc->fn_xmin = HeapTupleGetRawXmin(procTup);
prodesc->fn_tid = procTup->t_self;
prodesc->nargs = procStruct->pronargs;
prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index b791c23f066..820db4c0e47 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -171,7 +171,7 @@ recheck:
if (function)
{
/* We have a compiled function, but is it still valid? */
- if (function->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
+ if (function->fn_xmin == HeapTupleGetRawXmin(procTup) &&
ItemPointerEquals(&function->fn_tid, &procTup->t_self))
function_valid = true;
else
@@ -348,7 +348,7 @@ do_compile(FunctionCallInfo fcinfo,
function->fn_signature = format_procedure(fcinfo->flinfo->fn_oid);
MemoryContextSetIdentifier(func_cxt, function->fn_signature);
function->fn_oid = fcinfo->flinfo->fn_oid;
- function->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
+ function->fn_xmin = HeapTupleGetRawXmin(procTup);
function->fn_tid = procTup->t_self;
function->fn_input_collation = fcinfo->fncollation;
function->fn_cxt = func_cxt;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 70c4a752955..1bf48085084 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -7349,6 +7349,7 @@ deconstruct_composite_datum(Datum value, HeapTupleData *tmptup)
tmptup->t_len = HeapTupleHeaderGetDatumLength(td);
ItemPointerSetInvalid(&(tmptup->t_self));
tmptup->t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(tmptup);
tmptup->t_data = td;
/* Extract rowtype info and find a tupdesc */
@@ -7523,6 +7524,7 @@ exec_move_row_from_datum(PLpgSQL_execstate *estate,
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tmptup);
tmptup.t_data = td;
/* Extract rowtype info */
diff --git a/src/pl/plpython/plpy_procedure.c b/src/pl/plpython/plpy_procedure.c
index 494f109b323..9884f74fa78 100644
--- a/src/pl/plpython/plpy_procedure.c
+++ b/src/pl/plpython/plpy_procedure.c
@@ -178,7 +178,7 @@ PLy_procedure_create(HeapTuple procTup, Oid fn_oid, bool is_trigger)
proc->proname = pstrdup(NameStr(procStruct->proname));
MemoryContextSetIdentifier(cxt, proc->proname);
proc->pyname = pstrdup(procName);
- proc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
+ proc->fn_xmin = HeapTupleGetRawXmin(procTup);
proc->fn_tid = procTup->t_self;
proc->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE);
proc->is_setof = procStruct->proretset;
@@ -419,7 +419,7 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
return false;
/* If the pg_proc tuple has changed, it's not valid */
- if (!(proc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
+ if (!(proc->fn_xmin == HeapTupleGetRawXmin(procTup) &&
ItemPointerEquals(&proc->fn_tid, &procTup->t_self)))
return false;
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index 7c045f45607..52361811dcd 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -1426,7 +1426,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
* function's pg_proc entry without changing its OID.
************************************************************/
if (prodesc != NULL &&
- prodesc->fn_xmin == HeapTupleHeaderGetRawXmin(procTup->t_data) &&
+ prodesc->fn_xmin == HeapTupleGetRawXmin(procTup) &&
ItemPointerEquals(&prodesc->fn_tid, &procTup->t_self))
{
/* It's still up-to-date, so we can use it */
@@ -1493,7 +1493,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid,
prodesc->internal_proname = pstrdup(internal_proname);
prodesc->fn_cxt = proc_cxt;
prodesc->fn_refcount = 0;
- prodesc->fn_xmin = HeapTupleHeaderGetRawXmin(procTup->t_data);
+ prodesc->fn_xmin = HeapTupleGetRawXmin(procTup);
prodesc->fn_tid = procTup->t_self;
prodesc->nargs = procStruct->pronargs;
prodesc->arg_out_func = (FmgrInfo *) palloc0(prodesc->nargs * sizeof(FmgrInfo));
diff --git a/src/test/Makefile b/src/test/Makefile
index 46275915ff3..ec6558e33ba 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -12,7 +12,8 @@ subdir = src/test
top_builddir = ../..
include $(top_builddir)/src/Makefile.global
-SUBDIRS = perl regress isolation modules authentication recovery subscription
+SUBDIRS = perl regress isolation modules authentication recovery subscription \
+ xid-64
# Test suites that are not safe by default but can be run if selected
# by the user via the whitespace-separated list in variable
diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 265f3ae6574..1e9e4eb905c 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -431,6 +431,7 @@ sub init
mkdir $self->archive_dir;
PostgreSQL::Test::Utils::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N',
+ '-x', '1249835483136', '-m', '2422361554944', '-o', '3594887626752',
@{ $params{extra} });
PostgreSQL::Test::Utils::system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata,
@{ $params{auth_extra} });
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index 24da78c0bcd..62ccac68ef4 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -57,7 +57,7 @@ $node_primary->init(has_archiving => 1, allows_streaming => 1);
# Bump the transaction ID epoch. This is useful to stress the portability
# of recovery_target_xid parsing.
-system_or_bail('pg_resetwal', '--epoch', '1', $node_primary->data_dir);
+system_or_bail('pg_resetwal', $node_primary->data_dir);
# Start it
$node_primary->start;
diff --git a/src/test/regress/expected/indirect_toast.out b/src/test/regress/expected/indirect_toast.out
index ab1fa5e707c..df2c354364a 100644
--- a/src/test/regress/expected/indirect_toast.out
+++ b/src/test/regress/expected/indirect_toast.out
@@ -153,6 +153,14 @@ SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest;
("one-toasted,one-null, via indirect",0,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
(5 rows)
+create or replace function random_string(len integer) returns text as $$
+select substr((select string_agg(r,'') from (select random()::text as r from generate_series(1,(len+15)/16)) s1), 1, len);
+$$ language sql;
+create table toasttest_main(t text);
+alter table toasttest_main alter column t set storage main;
+insert into toasttest_main (select random_string(len) from generate_series(8000,9000) len);
DROP TABLE indtoasttest;
+DROP TABLE toasttest_main;
DROP FUNCTION update_using_indirect();
+DROP FUNCTION random_string(integer);
RESET default_toast_compression;
diff --git a/src/test/regress/expected/insert.out b/src/test/regress/expected/insert.out
index 5063a3dc221..fb7a1628332 100644
--- a/src/test/regress/expected/insert.out
+++ b/src/test/regress/expected/insert.out
@@ -100,7 +100,7 @@ SELECT pg_size_pretty(pg_relation_size('large_tuple_test'::regclass, 'main'));
INSERT INTO large_tuple_test (select 3, NULL);
-- now this tuple won't fit on the second page, but the insert should
-- still succeed by extending the relation
-INSERT INTO large_tuple_test (select 4, repeat('a', 8126));
+INSERT INTO large_tuple_test (select 4, repeat('a', 8112));
DROP TABLE large_tuple_test;
--
-- check indirection (field/array assignment), cf bug #14265
@@ -1002,3 +1002,17 @@ insert into returningwrtest values (2, 'foo') returning returningwrtest;
(1 row)
drop table returningwrtest;
+-- Check for MaxHeapTupleSize
+create table maxheaptuplesize_test(value text);
+alter table maxheaptuplesize_test alter column value set storage external;
+insert into maxheaptuplesize_test values (repeat('x', 8104));
+insert into maxheaptuplesize_test values (repeat('x', 8112));
+insert into maxheaptuplesize_test values (repeat('x', 8120));
+insert into maxheaptuplesize_test values (repeat('x', 8128));
+insert into maxheaptuplesize_test values (repeat('x', 8136));
+insert into maxheaptuplesize_test values (repeat('x', 8144));
+insert into maxheaptuplesize_test values (repeat('x', 8152));
+insert into maxheaptuplesize_test values (repeat('x', 8160));
+insert into maxheaptuplesize_test values (repeat('x', 8168));
+insert into maxheaptuplesize_test values (repeat('x', 8176));
+drop table maxheaptuplesize_test;
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 562b586d8e0..0c47f94ee0b 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -197,7 +197,7 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-----------------------------+--------------------------
- integer | xid
+ bigint | xid
timestamp without time zone | timestamp with time zone
bit | bit varying
txid_snapshot | pg_snapshot
@@ -704,7 +704,7 @@ int8(oid)
tideq(tid,tid)
timestamptz_cmp(timestamp with time zone,timestamp with time zone)
interval_cmp(interval,interval)
-xideqint4(xid,integer)
+xideqint8(xid,bigint)
timetz_eq(time with time zone,time with time zone)
timetz_ne(time with time zone,time with time zone)
timetz_lt(time with time zone,time with time zone)
@@ -818,7 +818,7 @@ pg_lsn_gt(pg_lsn,pg_lsn)
pg_lsn_ne(pg_lsn,pg_lsn)
pg_lsn_cmp(pg_lsn,pg_lsn)
xidneq(xid,xid)
-xidneqint4(xid,integer)
+xidneqint8(xid,bigint)
sha224(bytea)
sha256(bytea)
sha384(bytea)
diff --git a/src/test/regress/expected/select_views.out b/src/test/regress/expected/select_views.out
index 1aeed8452bd..d3be84754c1 100644
--- a/src/test/regress/expected/select_views.out
+++ b/src/test/regress/expected/select_views.out
@@ -2,9 +2,22 @@
-- SELECT_VIEWS
-- test the views defined in CREATE_VIEWS
--
-SELECT * FROM street;
+SELECT * FROM street ORDER BY name COLLATE "C", thepath::text COLLATE "C";
name | thepath | cname
------------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+-----------
+ 100th Ave | [(-122.1657,37.429),(-122.1647,37.432)] | Oakland
+ 107th Ave | [(-122.1555,37.403),(-122.1531,37.41)] | Oakland
+ 14th St | [(-122.299,37.147),(-122.3,37.148)] | Lafayette
+ 19th Ave | [(-122.2366,37.897),(-122.2359,37.905)] | Berkeley
+ 1st St | [(-121.75508,37.89294),(-121.753581,37.90031)] | Oakland
+ 5th St | [(-122.278,37),(-122.2792,37.005),(-122.2803,37.009)] | Lafayette
+ 5th St | [(-122.296,37.615),(-122.2953,37.598)] | Berkeley
+ 82nd Ave | [(-122.1695,37.596),(-122.1681,37.603)] | Berkeley
+ 85th Ave | [(-122.1877,37.466),(-122.186,37.476)] | Oakland
+ 89th Ave | [(-122.1822,37.459),(-122.1803,37.471)] | Oakland
+ 98th Ave | [(-122.1568,37.498),(-122.1558,37.502)] | Oakland
+ 98th Ave | [(-122.1693,37.438),(-122.1682,37.444)] | Oakland
+ 98th Ave | [(-122.2001,37.258),(-122.1974,37.27)] | Lafayette
Access Rd 25 | [(-121.9283,37.894),(-121.9283,37.9)] | Oakland
Ada St | [(-122.2487,37.398),(-122.2496,37.401)] | Lafayette
Agua Fria Creek | [(-121.9254,37.922),(-121.9281,37.889)] | Oakland
@@ -22,8 +35,8 @@ SELECT * FROM street;
Arroyo Las Positas | [(-121.7973,37.997),(-121.7957,37.005)] | Oakland
Arroyo Seco | [(-121.7073,37.766),(-121.6997,37.729)] | Oakland
Ash St | [(-122.0408,37.31),(-122.04,37.292)] | Oakland
- Avenue 134th | [(-122.1823,37.002),(-122.1851,37.992)] | Oakland
Avenue 134th | [(-122.1823,37.002),(-122.1851,37.992)] | Berkeley
+ Avenue 134th | [(-122.1823,37.002),(-122.1851,37.992)] | Oakland
Avenue 140th | [(-122.1656,37.003),(-122.1691,37.988)] | Oakland
Avenue 140th | [(-122.1656,37.003),(-122.1691,37.988)] | Berkeley
Avenue D | [(-122.298,37.848),(-122.3024,37.849)] | Berkeley
@@ -37,14 +50,14 @@ SELECT * FROM street;
Broadmore Ave | [(-122.095,37.522),(-122.0936,37.497)] | Oakland
Broadway | [(-122.2409,37.586),(-122.2395,37.601)] | Berkeley
Buckingham Blvd | [(-122.2231,37.59),(-122.2214,37.606)] | Berkeley
+ Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Berkeley
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Oakland
Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Oakland
- Butterfield Dr | [(-122.0838,37.002),(-122.0834,37.987)] | Berkeley
C St | [(-122.1768,37.46),(-122.1749,37.435)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
Calaveras Creek | [(-121.8203,37.035),(-121.8207,37.931)] | Oakland
- California St | [(-122.2032,37.005),(-122.2016,37.996)] | Berkeley
California St | [(-122.2032,37.005),(-122.2016,37.996)] | Lafayette
+ California St | [(-122.2032,37.005),(-122.2016,37.996)] | Berkeley
Cameron Ave | [(-122.1316,37.502),(-122.1327,37.481)] | Oakland
Campus Dr | [(-122.1704,37.905),(-122.1678,37.868),(-122.1671,37.865)] | Berkeley
Capricorn Ave | [(-122.2176,37.404),(-122.2164,37.384)] | Lafayette
@@ -55,8 +68,8 @@ SELECT * FROM street;
Central Ave | [(-122.2343,37.602),(-122.2331,37.595)] | Berkeley
Chambers Dr | [(-122.2004,37.352),(-122.1972,37.368)] | Lafayette
Chambers Lane | [(-122.2001,37.359),(-122.1975,37.371)] | Lafayette
- Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Berkeley
Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Lafayette
+ Champion St | [(-122.214,37.991),(-122.2147,37.002)] | Berkeley
Chapman Dr | [(-122.0421,37.504),(-122.0414,37.498)] | Oakland
Charles St | [(-122.0255,37.505),(-122.0252,37.499)] | Oakland
Cherry St | [(-122.0437,37.42),(-122.0434,37.413)] | Oakland
@@ -77,9 +90,9 @@ SELECT * FROM street;
Cull Canyon Road | [(-122.0536,37.435),(-122.0499,37.315)] | Oakland
Cull Creek | [(-122.0624,37.875),(-122.0582,37.527)] | Berkeley
D St | [(-122.1811,37.505),(-122.1805,37.497)] | Oakland
+ Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Berkeley
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Oakland
- Decoto Road | [(-122.0159,37.006),(-122.016,37.002),(-122.0164,37.993)] | Berkeley
Deering St | [(-122.2146,37.904),(-122.2126,37.897)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Berkeley
Dimond Ave | [(-122.2167,37.994),(-122.2162,37.006)] | Lafayette
@@ -117,12 +130,12 @@ SELECT * FROM street;
I- 580 | [(-121.9322,37.989),(-121.9243,37.006),(-121.9217,37.014)] | Oakland
I- 580 | [(-122.018,37.019),(-122.0009,37.032),(-121.9787,37.983),(-121.958,37.984),(-121.9571,37.986)] | Oakland
I- 580 | [(-122.018,37.019),(-122.0009,37.032),(-121.9787,37.983),(-121.958,37.984),(-121.9571,37.986)] | Oakland
- I- 580 | [(-122.1108,37.023),(-122.1101,37.02),(-122.108103,37.00764),(-122.108,37.007),(-122.1069,37.998),(-122.1064,37.994),(-122.1053,37.982),(-122.1048,37.977),(-122.1032,37.958),(-122.1026,37.953),(-122.1013,37.938),(-122.0989,37.911),(-122.0984,37.91),(-122.098,37.908)] | Oakland
I- 580 | [(-122.1108,37.023),(-122.1101,37.02),(-122.108103,37.00764),(-122.108,37.007),(-122.1069,37.998),(-122.1064,37.994),(-122.1053,37.982),(-122.1048,37.977),(-122.1032,37.958),(-122.1026,37.953),(-122.1013,37.938),(-122.0989,37.911),(-122.0984,37.91),(-122.098,37.908)] | Berkeley
+ I- 580 | [(-122.1108,37.023),(-122.1101,37.02),(-122.108103,37.00764),(-122.108,37.007),(-122.1069,37.998),(-122.1064,37.994),(-122.1053,37.982),(-122.1048,37.977),(-122.1032,37.958),(-122.1026,37.953),(-122.1013,37.938),(-122.0989,37.911),(-122.0984,37.91),(-122.098,37.908)] | Oakland
I- 580 | [(-122.1543,37.703),(-122.1535,37.694),(-122.1512,37.655),(-122.1475,37.603),(-122.1468,37.583),(-122.1472,37.569),(-122.149044,37.54874),(-122.1493,37.546),(-122.1501,37.532),(-122.1506,37.509),(-122.1495,37.482),(-122.1487,37.467),(-122.1477,37.447),(-122.1414,37.383),(-122.1404,37.376),(-122.1398,37.372),(-122.139,37.356),(-122.1388,37.353),(-122.1385,37.34),(-122.1382,37.33),(-122.1378,37.316)] | Oakland
I- 580 | [(-122.1543,37.703),(-122.1535,37.694),(-122.1512,37.655),(-122.1475,37.603),(-122.1468,37.583),(-122.1472,37.569),(-122.149044,37.54874),(-122.1493,37.546),(-122.1501,37.532),(-122.1506,37.509),(-122.1495,37.482),(-122.1487,37.467),(-122.1477,37.447),(-122.1414,37.383),(-122.1404,37.376),(-122.1398,37.372),(-122.139,37.356),(-122.1388,37.353),(-122.1385,37.34),(-122.1382,37.33),(-122.1378,37.316)] | Berkeley
- I- 580 | [(-122.2197,37.99),(-122.22,37.99),(-122.222092,37.99523),(-122.2232,37.998),(-122.224146,37.99963),(-122.2261,37.003),(-122.2278,37.007),(-122.2302,37.026),(-122.2323,37.043),(-122.2344,37.059),(-122.235405,37.06427),(-122.2365,37.07)] | Berkeley
I- 580 | [(-122.2197,37.99),(-122.22,37.99),(-122.222092,37.99523),(-122.2232,37.998),(-122.224146,37.99963),(-122.2261,37.003),(-122.2278,37.007),(-122.2302,37.026),(-122.2323,37.043),(-122.2344,37.059),(-122.235405,37.06427),(-122.2365,37.07)] | Lafayette
+ I- 580 | [(-122.2197,37.99),(-122.22,37.99),(-122.222092,37.99523),(-122.2232,37.998),(-122.224146,37.99963),(-122.2261,37.003),(-122.2278,37.007),(-122.2302,37.026),(-122.2323,37.043),(-122.2344,37.059),(-122.235405,37.06427),(-122.2365,37.07)] | Berkeley
I- 580 Ramp | [(-121.8521,37.011),(-121.8479,37.999),(-121.8476,37.999),(-121.8456,37.01),(-121.8455,37.011)] | Oakland
I- 580 Ramp | [(-121.8521,37.011),(-121.8479,37.999),(-121.8476,37.999),(-121.8456,37.01),(-121.8455,37.011)] | Oakland
I- 580 Ramp | [(-121.8743,37.014),(-121.8722,37.999),(-121.8714,37.999)] | Oakland
@@ -136,8 +149,8 @@ SELECT * FROM street;
I- 580 Ramp | [(-122.0941,37.897),(-122.0943,37.902)] | Berkeley
I- 580 Ramp | [(-122.096,37.888),(-122.0962,37.891),(-122.0964,37.9)] | Berkeley
I- 580 Ramp | [(-122.101,37.898),(-122.1005,37.902),(-122.0989,37.911)] | Berkeley
- I- 580 Ramp | [(-122.1086,37.003),(-122.1068,37.993),(-122.1066,37.992),(-122.1053,37.982)] | Oakland
I- 580 Ramp | [(-122.1086,37.003),(-122.1068,37.993),(-122.1066,37.992),(-122.1053,37.982)] | Berkeley
+ I- 580 Ramp | [(-122.1086,37.003),(-122.1068,37.993),(-122.1066,37.992),(-122.1053,37.982)] | Oakland
I- 580 Ramp | [(-122.1414,37.383),(-122.1407,37.376),(-122.1403,37.372),(-122.139,37.356)] | Oakland
I- 580/I-680 Ramp | ((-121.9207,37.988),(-121.9192,37.016)) | Oakland
I- 580/I-680 Ramp | ((-121.9207,37.988),(-121.9192,37.016)) | Oakland
@@ -158,16 +171,16 @@ SELECT * FROM street;
I- 880 | ((-121.9669,37.075),(-121.9663,37.071),(-121.9656,37.065),(-121.9618,37.037),(-121.95689,37),(-121.948,37.933)) | Oakland
I- 880 | [(-121.948,37.933),(-121.9471,37.925),(-121.9467,37.923),(-121.946,37.918),(-121.9452,37.912),(-121.937,37.852)] | Oakland
I- 880 | [(-122.0219,37.466),(-122.0205,37.447),(-122.020331,37.44447),(-122.020008,37.43962),(-122.0195,37.432),(-122.0193,37.429),(-122.0164,37.393),(-122.010219,37.34771),(-122.0041,37.313)] | Oakland
- I- 880 | [(-122.0375,37.632),(-122.0359,37.619),(-122.0358,37.616),(-122.034514,37.60409),(-122.031876,37.57965),(-122.031193,37.57332),(-122.03016,37.56375),(-122.02943,37.55698),(-122.028689,37.54929),(-122.027833,37.53908),(-122.025979,37.51698),(-122.0238,37.491)] | Oakland
I- 880 | [(-122.0375,37.632),(-122.0359,37.619),(-122.0358,37.616),(-122.034514,37.60409),(-122.031876,37.57965),(-122.031193,37.57332),(-122.03016,37.56375),(-122.02943,37.55698),(-122.028689,37.54929),(-122.027833,37.53908),(-122.025979,37.51698),(-122.0238,37.491)] | Berkeley
- I- 880 | [(-122.0612,37.003),(-122.0604,37.991),(-122.0596,37.982),(-122.0585,37.967),(-122.0583,37.961),(-122.0553,37.918),(-122.053635,37.89475),(-122.050759,37.8546),(-122.05,37.844),(-122.0485,37.817),(-122.0483,37.813),(-122.0482,37.811)] | Oakland
+ I- 880 | [(-122.0375,37.632),(-122.0359,37.619),(-122.0358,37.616),(-122.034514,37.60409),(-122.031876,37.57965),(-122.031193,37.57332),(-122.03016,37.56375),(-122.02943,37.55698),(-122.028689,37.54929),(-122.027833,37.53908),(-122.025979,37.51698),(-122.0238,37.491)] | Oakland
I- 880 | [(-122.0612,37.003),(-122.0604,37.991),(-122.0596,37.982),(-122.0585,37.967),(-122.0583,37.961),(-122.0553,37.918),(-122.053635,37.89475),(-122.050759,37.8546),(-122.05,37.844),(-122.0485,37.817),(-122.0483,37.813),(-122.0482,37.811)] | Oakland
I- 880 | [(-122.0612,37.003),(-122.0604,37.991),(-122.0596,37.982),(-122.0585,37.967),(-122.0583,37.961),(-122.0553,37.918),(-122.053635,37.89475),(-122.050759,37.8546),(-122.05,37.844),(-122.0485,37.817),(-122.0483,37.813),(-122.0482,37.811)] | Berkeley
+ I- 880 | [(-122.0612,37.003),(-122.0604,37.991),(-122.0596,37.982),(-122.0585,37.967),(-122.0583,37.961),(-122.0553,37.918),(-122.053635,37.89475),(-122.050759,37.8546),(-122.05,37.844),(-122.0485,37.817),(-122.0483,37.813),(-122.0482,37.811)] | Oakland
I- 880 | [(-122.0831,37.312),(-122.0819,37.296),(-122.081,37.285),(-122.0786,37.248),(-122.078,37.24),(-122.077642,37.23496),(-122.076983,37.22567),(-122.076599,37.22026),(-122.076229,37.21505),(-122.0758,37.209)] | Oakland
I- 880 | [(-122.0978,37.528),(-122.096,37.496),(-122.0931,37.453),(-122.09277,37.4496),(-122.090189,37.41442),(-122.0896,37.405),(-122.085,37.34)] | Oakland
I- 880 | [(-122.1365,37.902),(-122.1358,37.898),(-122.1333,37.881),(-122.1323,37.874),(-122.1311,37.866),(-122.1308,37.865),(-122.1307,37.864),(-122.1289,37.851),(-122.1277,37.843),(-122.1264,37.834),(-122.1231,37.812),(-122.1165,37.766),(-122.1104,37.72),(-122.109695,37.71094),(-122.109,37.702),(-122.108312,37.69168),(-122.1076,37.681)] | Berkeley
- I- 880 | [(-122.1755,37.185),(-122.1747,37.178),(-122.1742,37.173),(-122.1692,37.126),(-122.167792,37.11594),(-122.16757,37.11435),(-122.1671,37.111),(-122.1655,37.1),(-122.165169,37.09811),(-122.1641,37.092),(-122.1596,37.061),(-122.158381,37.05275),(-122.155991,37.03657),(-122.1531,37.017),(-122.1478,37.98),(-122.1407,37.932),(-122.1394,37.924),(-122.1389,37.92),(-122.1376,37.91)] | Oakland
I- 880 | [(-122.1755,37.185),(-122.1747,37.178),(-122.1742,37.173),(-122.1692,37.126),(-122.167792,37.11594),(-122.16757,37.11435),(-122.1671,37.111),(-122.1655,37.1),(-122.165169,37.09811),(-122.1641,37.092),(-122.1596,37.061),(-122.158381,37.05275),(-122.155991,37.03657),(-122.1531,37.017),(-122.1478,37.98),(-122.1407,37.932),(-122.1394,37.924),(-122.1389,37.92),(-122.1376,37.91)] | Berkeley
+ I- 880 | [(-122.1755,37.185),(-122.1747,37.178),(-122.1742,37.173),(-122.1692,37.126),(-122.167792,37.11594),(-122.16757,37.11435),(-122.1671,37.111),(-122.1655,37.1),(-122.165169,37.09811),(-122.1641,37.092),(-122.1596,37.061),(-122.158381,37.05275),(-122.155991,37.03657),(-122.1531,37.017),(-122.1478,37.98),(-122.1407,37.932),(-122.1394,37.924),(-122.1389,37.92),(-122.1376,37.91)] | Oakland
I- 880 | [(-122.2214,37.711),(-122.2202,37.699),(-122.2199,37.695),(-122.219,37.682),(-122.2184,37.672),(-122.2173,37.652),(-122.2159,37.638),(-122.2144,37.616),(-122.2138,37.612),(-122.2135,37.609),(-122.212,37.592),(-122.2116,37.586),(-122.2111,37.581)] | Berkeley
I- 880 | [(-122.2707,37.975),(-122.2693,37.972),(-122.2681,37.966),(-122.267,37.962),(-122.2659,37.957),(-122.2648,37.952),(-122.2636,37.946),(-122.2625,37.935),(-122.2617,37.927),(-122.2607,37.921),(-122.2593,37.916),(-122.258,37.911),(-122.2536,37.898),(-122.2432,37.858),(-122.2408,37.845),(-122.2386,37.827),(-122.2374,37.811)] | Berkeley
I- 880 Ramp | [(-122.0019,37.301),(-122.002,37.293)] | Oakland
@@ -202,28 +215,28 @@ SELECT * FROM street;
Laguna Ave | [(-122.2099,37.989),(-122.2089,37)] | Berkeley
Laguna Ave | [(-122.2099,37.989),(-122.2089,37)] | Lafayette
Lakehurst Cir | [(-122.284729,37.89025),(-122.286096,37.90364)] | Berkeley
- Lakeshore Ave | [(-122.2586,37.99),(-122.2556,37.006)] | Berkeley
Lakeshore Ave | [(-122.2586,37.99),(-122.2556,37.006)] | Lafayette
+ Lakeshore Ave | [(-122.2586,37.99),(-122.2556,37.006)] | Berkeley
Las Positas Road | [(-121.764488,37.99199),(-121.75569,37.02022)] | Oakland
Las Positas Road | [(-121.764488,37.99199),(-121.75569,37.02022)] | Oakland
- Linden St | [(-122.2867,37.998),(-122.2864,37.008)] | Berkeley
Linden St | [(-122.2867,37.998),(-122.2864,37.008)] | Lafayette
+ Linden St | [(-122.2867,37.998),(-122.2864,37.008)] | Berkeley
Livermore Ave | [(-121.7687,37.448),(-121.769,37.375)] | Oakland
Livermore Ave | [(-121.7687,37.448),(-121.769,37.375)] | Oakland
Livermore Ave | [(-121.772719,37.99085),(-121.7728,37.001)] | Oakland
Livermore Ave | [(-121.772719,37.99085),(-121.7728,37.001)] | Oakland
- Locust St | [(-122.1606,37.007),(-122.1593,37.987)] | Oakland
Locust St | [(-122.1606,37.007),(-122.1593,37.987)] | Berkeley
+ Locust St | [(-122.1606,37.007),(-122.1593,37.987)] | Oakland
Logan Ct | [(-122.0053,37.492),(-122.0061,37.484)] | Oakland
Magnolia St | [(-122.0971,37.5),(-122.0962,37.484)] | Oakland
Mandalay Road | [(-122.2322,37.397),(-122.2321,37.403)] | Lafayette
Marin Ave | [(-122.2741,37.894),(-122.272,37.901)] | Berkeley
Martin Luther King Jr Way | [(-122.2712,37.608),(-122.2711,37.599)] | Berkeley
Mattos Dr | [(-122.0005,37.502),(-122.000898,37.49683)] | Oakland
- Maubert Ave | [(-122.1114,37.009),(-122.1096,37.995)] | Oakland
Maubert Ave | [(-122.1114,37.009),(-122.1096,37.995)] | Berkeley
- McClure Ave | [(-122.1431,37.001),(-122.1436,37.998)] | Oakland
+ Maubert Ave | [(-122.1114,37.009),(-122.1096,37.995)] | Oakland
McClure Ave | [(-122.1431,37.001),(-122.1436,37.998)] | Berkeley
+ McClure Ave | [(-122.1431,37.001),(-122.1436,37.998)] | Oakland
Medlar Dr | [(-122.0627,37.378),(-122.0625,37.375)] | Oakland
Mildred Ct | [(-122.0002,37.388),(-121.9998,37.386)] | Oakland
Miller Road | [(-122.0902,37.645),(-122.0865,37.545)] | Berkeley
@@ -242,8 +255,8 @@ SELECT * FROM street;
Parkridge Dr | [(-122.1438,37.884),(-122.1428,37.9)] | Berkeley
Parkside Dr | [(-122.0475,37.603),(-122.0443,37.596)] | Berkeley
Paseo Padre Pkwy | [(-121.9143,37.005),(-121.913522,37)] | Oakland
- Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Oakland
Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Berkeley
+ Paseo Padre Pkwy | [(-122.0021,37.639),(-121.996,37.628)] | Oakland
Pearl St | [(-122.2383,37.594),(-122.2366,37.615)] | Berkeley
Periwinkle Road | [(-122.0451,37.301),(-122.044758,37.29844)] | Oakland
Pimlico Dr | [(-121.8616,37.998),(-121.8618,37.008)] | Oakland
@@ -254,11 +267,11 @@ SELECT * FROM street;
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Oakland
Railroad Ave | [(-122.0245,37.013),(-122.0234,37.003),(-122.0223,37.993)] | Berkeley
Ranspot Dr | [(-122.0972,37.999),(-122.0959,37)] | Oakland
- Ranspot Dr | [(-122.0972,37.999),(-122.0959,37)] | Oakland
Ranspot Dr | [(-122.0972,37.999),(-122.0959,37)] | Berkeley
+ Ranspot Dr | [(-122.0972,37.999),(-122.0959,37)] | Oakland
Redding St | [(-122.1978,37.901),(-122.1975,37.895)] | Berkeley
- Redwood Road | [(-122.1493,37.98),(-122.1437,37.001)] | Oakland
Redwood Road | [(-122.1493,37.98),(-122.1437,37.001)] | Berkeley
+ Redwood Road | [(-122.1493,37.98),(-122.1437,37.001)] | Oakland
Roca Dr | [(-122.0335,37.609),(-122.0314,37.599)] | Berkeley
Rosedale Ct | [(-121.9232,37.9),(-121.924,37.897)] | Oakland
Sacramento St | [(-122.2799,37.606),(-122.2797,37.597)] | Berkeley
@@ -266,8 +279,8 @@ SELECT * FROM street;
Saginaw Ct | [(-121.8803,37.898),(-121.8806,37.901)] | Oakland
San Andreas Dr | [(-122.0609,37.9),(-122.0614,37.895)] | Berkeley
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
- Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Berkeley
+ Santa Maria Ave | [(-122.0773,37),(-122.0773,37.98)] | Oakland
Shattuck Ave | [(-122.2686,37.904),(-122.2686,37.897)] | Berkeley
Sheridan Road | [(-122.2279,37.425),(-122.2253,37.411),(-122.2223,37.377)] | Lafayette
Shoreline Dr | [(-122.2657,37.603),(-122.2648,37.6)] | Berkeley
@@ -317,27 +330,14 @@ SELECT * FROM street;
Welch Creek Road | [(-121.7695,37.386),(-121.7737,37.413)] | Oakland
West Loop Road | [(-122.0576,37.604),(-122.0602,37.586)] | Berkeley
Western Pacific Railroad Spur | [(-122.0394,37.018),(-122.0394,37.961)] | Oakland
- Western Pacific Railroad Spur | [(-122.0394,37.018),(-122.0394,37.961)] | Oakland
Western Pacific Railroad Spur | [(-122.0394,37.018),(-122.0394,37.961)] | Berkeley
+ Western Pacific Railroad Spur | [(-122.0394,37.018),(-122.0394,37.961)] | Oakland
Whitlock Creek | [(-121.74683,37.91276),(-121.733107,37)] | Oakland
Whitlock Creek | [(-121.74683,37.91276),(-121.733107,37)] | Oakland
Willimet Way | [(-122.0964,37.517),(-122.0949,37.493)] | Oakland
Wisconsin St | [(-122.1994,37.017),(-122.1975,37.998),(-122.1971,37.994)] | Oakland
Wisconsin St | [(-122.1994,37.017),(-122.1975,37.998),(-122.1971,37.994)] | Berkeley
Wp Railroad | [(-122.254,37.902),(-122.2506,37.891)] | Berkeley
- 100th Ave | [(-122.1657,37.429),(-122.1647,37.432)] | Oakland
- 107th Ave | [(-122.1555,37.403),(-122.1531,37.41)] | Oakland
- 14th St | [(-122.299,37.147),(-122.3,37.148)] | Lafayette
- 19th Ave | [(-122.2366,37.897),(-122.2359,37.905)] | Berkeley
- 1st St | [(-121.75508,37.89294),(-121.753581,37.90031)] | Oakland
- 5th St | [(-122.278,37),(-122.2792,37.005),(-122.2803,37.009)] | Lafayette
- 5th St | [(-122.296,37.615),(-122.2953,37.598)] | Berkeley
- 82nd Ave | [(-122.1695,37.596),(-122.1681,37.603)] | Berkeley
- 85th Ave | [(-122.1877,37.466),(-122.186,37.476)] | Oakland
- 89th Ave | [(-122.1822,37.459),(-122.1803,37.471)] | Oakland
- 98th Ave | [(-122.1568,37.498),(-122.1558,37.502)] | Oakland
- 98th Ave | [(-122.1693,37.438),(-122.1682,37.444)] | Oakland
- 98th Ave | [(-122.2001,37.258),(-122.1974,37.27)] | Lafayette
(333 rows)
SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out
index 95ba66e95ee..2ea4434f513 100644
--- a/src/test/regress/expected/txid.out
+++ b/src/test/regress/expected/txid.out
@@ -238,9 +238,11 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
-ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3"
-LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
- ^
+ txid_snapshot
+-------------------------
+ 1:9223372036854775808:3
+(1 row)
+
-- test txid_current_if_assigned
BEGIN;
SELECT txid_current_if_assigned() IS NULL;
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index b7a1ed0f9ee..a26aa0c47bc 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -8,9 +8,9 @@ select '010'::xid,
'42'::xid8,
'0xffffffffffffffff'::xid8,
'-1'::xid8;
- xid | xid | xid | xid | xid8 | xid8 | xid8 | xid8
------+-----+------------+------------+------+------+----------------------+----------------------
- 8 | 42 | 4294967295 | 4294967295 | 8 | 42 | 18446744073709551615 | 18446744073709551615
+ xid | xid | xid | xid | xid8 | xid8 | xid8 | xid8
+-----+-----+------------+----------------------+------+------+----------------------+----------------------
+ 8 | 42 | 4294967295 | 18446744073709551615 | 8 | 42 | 18446744073709551615 | 18446744073709551615
(1 row)
-- garbage values are not yet rejected (perhaps they should be)
@@ -373,9 +373,11 @@ SELECT 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';
- ^
+ pg_snapshot
+-------------------------
+ 1:9223372036854775808:3
+(1 row)
+
-- test pg_current_xact_id_if_assigned
BEGIN;
SELECT pg_current_xact_id_if_assigned() IS NULL;
diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c
index e6f71c7582e..fad1ae80827 100644
--- a/src/test/regress/pg_regress.c
+++ b/src/test/regress/pg_regress.c
@@ -2261,7 +2261,7 @@ regression_main(int argc, char *argv[],
/* initdb */
header(_("initializing database system"));
snprintf(buf, sizeof(buf),
- "\"%s%sinitdb\" -D \"%s/data\" --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1",
+ "\"%s%sinitdb\" -D \"%s/data\" -x 1249835483136 -m 2422361554944 -o 3594887626752 --no-clean --no-sync%s%s > \"%s/log/initdb.log\" 2>&1",
bindir ? bindir : "",
bindir ? "/" : "",
temp_instance,
diff --git a/src/test/regress/regress.c b/src/test/regress/regress.c
index eefbd0f0dfd..b27fea8d23d 100644
--- a/src/test/regress/regress.c
+++ b/src/test/regress/regress.c
@@ -561,6 +561,7 @@ make_tuple_indirect(PG_FUNCTION_ARGS)
tuple.t_len = HeapTupleHeaderGetDatumLength(rec);
ItemPointerSetInvalid(&(tuple.t_self));
tuple.t_tableOid = InvalidOid;
+ HeapTupleSetZeroBase(&tuple);
tuple.t_data = rec;
values = (Datum *) palloc(ncolumns * sizeof(Datum));
diff --git a/src/test/regress/sql/indirect_toast.sql b/src/test/regress/sql/indirect_toast.sql
index 9156a44b7d9..c99c944cadb 100644
--- a/src/test/regress/sql/indirect_toast.sql
+++ b/src/test/regress/sql/indirect_toast.sql
@@ -65,7 +65,18 @@ SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest;
VACUUM FREEZE indtoasttest;
SELECT substring(indtoasttest::text, 1, 200) FROM indtoasttest;
+create or replace function random_string(len integer) returns text as $$
+select substr((select string_agg(r,'') from (select random()::text as r from generate_series(1,(len+15)/16)) s1), 1, len);
+$$ language sql;
+
+create table toasttest_main(t text);
+alter table toasttest_main alter column t set storage main;
+
+insert into toasttest_main (select random_string(len) from generate_series(8000,9000) len);
+
DROP TABLE indtoasttest;
+DROP TABLE toasttest_main;
DROP FUNCTION update_using_indirect();
+DROP FUNCTION random_string(integer);
RESET default_toast_compression;
diff --git a/src/test/regress/sql/insert.sql b/src/test/regress/sql/insert.sql
index bfaa8a3b277..589a5dcdc67 100644
--- a/src/test/regress/sql/insert.sql
+++ b/src/test/regress/sql/insert.sql
@@ -55,7 +55,7 @@ INSERT INTO large_tuple_test (select 3, NULL);
-- now this tuple won't fit on the second page, but the insert should
-- still succeed by extending the relation
-INSERT INTO large_tuple_test (select 4, repeat('a', 8126));
+INSERT INTO large_tuple_test (select 4, repeat('a', 8112));
DROP TABLE large_tuple_test;
@@ -624,3 +624,18 @@ alter table returningwrtest2 drop c;
alter table returningwrtest attach partition returningwrtest2 for values in (2);
insert into returningwrtest values (2, 'foo') returning returningwrtest;
drop table returningwrtest;
+
+-- Check for MaxHeapTupleSize
+create table maxheaptuplesize_test(value text);
+alter table maxheaptuplesize_test alter column value set storage external;
+insert into maxheaptuplesize_test values (repeat('x', 8104));
+insert into maxheaptuplesize_test values (repeat('x', 8112));
+insert into maxheaptuplesize_test values (repeat('x', 8120));
+insert into maxheaptuplesize_test values (repeat('x', 8128));
+insert into maxheaptuplesize_test values (repeat('x', 8136));
+insert into maxheaptuplesize_test values (repeat('x', 8144));
+insert into maxheaptuplesize_test values (repeat('x', 8152));
+insert into maxheaptuplesize_test values (repeat('x', 8160));
+insert into maxheaptuplesize_test values (repeat('x', 8168));
+insert into maxheaptuplesize_test values (repeat('x', 8176));
+drop table maxheaptuplesize_test;
diff --git a/src/test/regress/sql/select_views.sql b/src/test/regress/sql/select_views.sql
index e742f136990..70e663e350c 100644
--- a/src/test/regress/sql/select_views.sql
+++ b/src/test/regress/sql/select_views.sql
@@ -3,7 +3,7 @@
-- test the views defined in CREATE_VIEWS
--
-SELECT * FROM street;
+SELECT * FROM street ORDER BY name COLLATE "C", thepath::text COLLATE "C";
SELECT name, #thepath FROM iexit ORDER BY name COLLATE "C", 2;
diff --git a/src/test/xid-64/Makefile b/src/test/xid-64/Makefile
new file mode 100644
index 00000000000..3b1e50dfc0d
--- /dev/null
+++ b/src/test/xid-64/Makefile
@@ -0,0 +1,22 @@
+#-------------------------------------------------------------------------
+#
+# Makefile for src/test/xid-64
+#
+# Copyright (c) 2018, Postgres Professional
+#
+# src/test/xid-64/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/test/xid-64
+top_builddir = ../../..
+include $(top_builddir)/src/Makefile.global
+
+check:
+ $(prove_check)
+
+installcheck:
+ $(prove_installcheck)
+
+clean distclean maintainer-clean:
+ rm -rf tmp_check
diff --git a/src/test/xid-64/README b/src/test/xid-64/README
new file mode 100644
index 00000000000..01c0a1a1f74
--- /dev/null
+++ b/src/test/xid-64/README
@@ -0,0 +1,16 @@
+src/test/xid-64/README
+
+Regression tests for 64-bit XIDs
+=============================================
+
+This directory contains a test suite for 64-bit xids.
+
+Running the tests
+=================
+
+ make check
+
+NOTE: This creates a temporary installation, and some tests may
+create one or multiple nodes.
+
+NOTE: This requires the --enable-tap-tests argument to configure.
diff --git a/src/test/xid-64/t/001_test_large_xids.pl b/src/test/xid-64/t/001_test_large_xids.pl
new file mode 100644
index 00000000000..6a039d60d08
--- /dev/null
+++ b/src/test/xid-64/t/001_test_large_xids.pl
@@ -0,0 +1,53 @@
+# Tests for large xid values
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 9;
+
+use bigint;
+
+sub command_output
+{
+ my ($cmd) = @_;
+ my ($stdout, $stderr);
+ print("# Running: " . join(" ", @{$cmd}) . "\n");
+ my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+ ok($result, "@$cmd exit code 0");
+ is($stderr, '', "@$cmd no stderr");
+ return $stdout;
+}
+
+my $START_VAL = 2**32;
+my $MAX_VAL = 2**62;
+
+my $ixid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imxid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imoff = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+
+# Initialize master node with the random xid-related parameters
+my $node = PostgreSQL::Test::Cluster->new('master');
+$node->init(extra => [ "--xid=$ixid", "--multixact-id=$imxid", "--multixact-offset=$imoff" ]);
+$node->start;
+
+# Initialize master node and check the xid-related parameters
+my $pgcd_output = command_output(
+ [ 'pg_controldata', '-D', $node->data_dir ] );
+print($pgcd_output); print('\n');
+ok($pgcd_output =~ qr/Latest checkpoint's NextXID:\s*(\d+)/, "XID found");
+my ($nextxid) = ($1);
+ok($nextxid >= $ixid && $nextxid < $ixid + 1000,
+ "Latest checkpoint's NextXID ($nextxid) is close to the initial xid ($ixid).");
+ok($pgcd_output =~ qr/Latest checkpoint's NextMultiXactId:\s*(\d+)/, "MultiXactId found");
+my ($nextmxid) = ($1);
+ok($nextmxid >= $imxid && $nextmxid < $imxid + 1000,
+ "Latest checkpoint's NextMultiXactId ($nextmxid) is close to the initial multiXactId ($imxid).");
+ok($pgcd_output =~ qr/Latest checkpoint's NextMultiOffset:\s*(\d+)/, "MultiOffset found");
+my ($nextmoff) = ($1);
+ok($nextmoff >= $imoff && $nextmoff < $imoff + 1000,
+ "Latest checkpoint's NextMultiOffset ($nextmoff) is close to the initial multiOffset ($imoff).");
+
+# Run pgbench to check whether the database is working properly
+$node->command_ok(
+ [ qw(pgbench --initialize --no-vacuum --scale=10) ],
+ 'pgbench finished without errors');
diff --git a/src/test/xid-64/t/002_test_gucs.pl b/src/test/xid-64/t/002_test_gucs.pl
new file mode 100644
index 00000000000..2b76374f012
--- /dev/null
+++ b/src/test/xid-64/t/002_test_gucs.pl
@@ -0,0 +1,78 @@
+# Tests for guc boundary values
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 19;
+
+use bigint;
+
+sub command_output
+{
+ my ($cmd) = @_;
+ my ($stdout, $stderr);
+ print("# Running: " . join(" ", @{$cmd}) . "\n");
+ my $result = IPC::Run::run $cmd, '>', \$stdout, '2>', \$stderr;
+ ok($result, "@$cmd exit code 0");
+ is($stderr, '', "@$cmd no stderr");
+ return $stdout;
+}
+
+sub set_guc
+{
+ my ($node, $guc, $val) = @_;
+ print("SET $guc = $val\n");
+ $node->safe_psql('postgres', "ALTER SYSTEM SET $guc = $val");
+ $node->restart();
+}
+
+sub test_pgbench
+{
+ my ($node) = @_;
+ $node->command_ok(
+ [ qw(pgbench --progress=5 --transactions=1000 --jobs=5 --client=5) ],
+ 'pgbench finished without errors');
+}
+
+my @guc_vals = (
+ [ "autovacuum_freeze_max_age", 100000, 2**63 - 1 ],
+ [ "autovacuum_multixact_freeze_max_age", 10000, 2**63 - 1 ],
+ [ "vacuum_freeze_min_age", 0, 2**63 - 1 ],
+ [ "vacuum_freeze_table_age", 0, 2**63 - 1 ],
+ [ "vacuum_multixact_freeze_min_age", 0, 2**63 - 1 ],
+ [ "vacuum_multixact_freeze_table_age", 0, 2**63 -1 ]
+);
+
+my $START_VAL = 2**32;
+my $MAX_VAL = 2**62;
+
+my $ixid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imxid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imoff = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+
+# Initialize master node
+my $node = PostgreSQL::Test::Cluster->new('master');
+$node->init(extra => [ "--xid=$ixid", "--multixact-id=$imxid", "--multixact-offset=$imoff" ]);
+# Disable logging of all statements to avoid log bloat during pgbench
+$node->append_conf('postgresql.conf', "log_statement = none");
+$node->start;
+
+# Fill the test database with the pgbench data
+$node->command_ok(
+ [ qw(pgbench --initialize --scale=10) ],
+ 'pgbench finished without errors');
+
+# Test all GUCs with minimum, maximum and random value inbetween
+# (run pgbench for every configuration setting)
+foreach my $gi (0 .. $#guc_vals) {
+ print($guc_vals[$gi][0]); print("\n");
+ my $guc = $guc_vals[$gi][0];
+ my $minval = $guc_vals[$gi][1];
+ my $maxval = $guc_vals[$gi][2];
+ set_guc($node, $guc, $minval);
+ test_pgbench($node);
+ set_guc($node, $guc, $maxval);
+ test_pgbench($node);
+ set_guc($node, $guc, $minval + int(rand($maxval - $minval)));
+ test_pgbench($node);
+}
diff --git a/src/test/xid-64/t/003_test_integrity.pl b/src/test/xid-64/t/003_test_integrity.pl
new file mode 100644
index 00000000000..d984907aee8
--- /dev/null
+++ b/src/test/xid-64/t/003_test_integrity.pl
@@ -0,0 +1,56 @@
+# Check integrity after dump/restore with different xids
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More tests => 5;
+use File::Compare;
+
+my $tempdir = PostgreSQL::Test::Utils::tempdir;
+use bigint;
+
+my $START_VAL = 2**32;
+my $MAX_VAL = 2**62;
+
+my $ixid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imxid = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+my $imoff = $START_VAL + int(rand($MAX_VAL - $START_VAL));
+
+# Initialize master node
+my $node = PostgreSQL::Test::Cluster->new('master');
+$node->init();
+$node->start;
+
+# Create a database and fill it with the pgbench data
+$node->safe_psql('postgres', "CREATE DATABASE pgbench_db");
+$node->command_ok(
+ [ qw(pgbench --initialize --scale=2 pgbench_db) ],
+ 'pgbench finished without errors');
+# Dump the database (cluster the main table to put data in a determined order)
+$node->safe_psql('pgbench_db', qq(
+ CREATE INDEX pa_aid_idx ON pgbench_accounts (aid);
+ CLUSTER pgbench_accounts USING pa_aid_idx));
+$node->command_ok(
+ [ "pg_dump", "-w", "--inserts", "--file=$tempdir/pgbench.sql", "pgbench_db" ],
+ 'pgdump finished without errors');
+$node->stop('fast');
+
+# Initialize second node
+my $node2 = PostgreSQL::Test::Cluster->new('master2');
+$node2->init(extra => [ "--xid=$ixid", "--multixact-id=$imxid", "--multixact-offset=$imoff" ]);
+# Disable logging of all statements to avoid log bloat during restore
+$node2->append_conf('postgresql.conf', "log_statement = none");
+$node2->start;
+
+# Create a database and restore the previous dump
+$node2->safe_psql('postgres', "CREATE DATABASE pgbench_db");
+my $txid0 = $node2->safe_psql('pgbench_db', 'SELECT txid_current()');
+print("# Initial txid_current: $txid0\n");
+$node2->command_ok(["psql", "-q", "-f", "$tempdir/pgbench.sql", "pgbench_db"]);
+
+# Dump the database and compare the dumped content with the previous one
+$node2->safe_psql('pgbench_db', 'CLUSTER pgbench_accounts');
+$node2->command_ok(
+ [ "pg_dump", "-w", "--inserts", "--file=$tempdir/pgbench2.sql", "pgbench_db" ],
+ 'pgdump finished without errors');
+ok(File::Compare::compare_text("$tempdir/pgbench.sql", "$tempdir/pgbench2.sql") == 0, "no differences detected");
diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm
index e47c2d648cb..c74e1b9f3f7 100644
--- a/src/tools/msvc/Solution.pm
+++ b/src/tools/msvc/Solution.pm
@@ -460,6 +460,7 @@ sub GenerateFiles
PACKAGE_TARNAME => lc qq{"$package_name"},
PACKAGE_URL => qq{"$package_url"},
PACKAGE_VERSION => qq{"$package_version"},
+ XID_IS_64BIT => 1,
PG_INT128_TYPE => undef,
PG_INT64_TYPE => 'long long int',
PG_KRB_SRVNAM => qq{"postgres"},
--
2.24.3 (Apple Git-128)