Here is a rebase, v14.

-- 
Best regards,
Maxim Orlov.
From ee4b3b3c3ad3293460eb1f0418d87a065b9a589b Mon Sep 17 00:00:00 2001
From: Maxim Orlov <m.or...@postgrespro.ru>
Date: Wed, 4 May 2022 15:53:36 +0300
Subject: [PATCH v14 5/7] TEST: initdb option to initialize cluster with
 non-standard xid/mxid/mxoff

To date testing database cluster wraparund was not easy as initdb has always
inited it with default xid/mxid/mxoff. The option to specify any valid
xid/mxid/mxoff at cluster startup will make these things easier.

Author: Maxim Orlov <orlo...@gmail.com>
Author: Pavel Borisov <pashkin.e...@gmail.com>
Author: Svetlana Derevyanko <s.derevya...@postgrespro.ru>
Discussion: 
https://www.postgresql.org/message-id/flat/CACG%3Dezaa4vqYjJ16yoxgrpa-%3DgXnf0Vv3Ey9bjGrRRFN2YyWFQ%40mail.gmail.com
---
 src/backend/access/transam/clog.c      |  21 +++++
 src/backend/access/transam/multixact.c |  53 ++++++++++++
 src/backend/access/transam/subtrans.c  |   8 +-
 src/backend/access/transam/xlog.c      |  15 ++--
 src/backend/bootstrap/bootstrap.c      |  50 +++++++++++-
 src/backend/main/main.c                |   6 ++
 src/backend/postmaster/postmaster.c    |  14 +++-
 src/backend/tcop/postgres.c            |  53 +++++++++++-
 src/bin/initdb/initdb.c                | 107 ++++++++++++++++++++++++-
 src/bin/initdb/t/001_initdb.pl         |  60 ++++++++++++++
 src/include/access/xlog.h              |   3 +
 src/include/c.h                        |   4 +
 src/include/catalog/pg_class.h         |   2 +-
 13 files changed, 382 insertions(+), 14 deletions(-)

diff --git a/src/backend/access/transam/clog.c 
b/src/backend/access/transam/clog.c
index 48f10bec91..eb8a9791ab 100644
--- a/src/backend/access/transam/clog.c
+++ b/src/backend/access/transam/clog.c
@@ -834,6 +834,7 @@ BootStrapCLOG(void)
 {
        int                     slotno;
        LWLock     *lock = SimpleLruGetBankLock(XactCtl, 0);
+       int64           pageno;
 
        LWLockAcquire(lock, LW_EXCLUSIVE);
 
@@ -844,6 +845,26 @@ BootStrapCLOG(void)
        SimpleLruWritePage(XactCtl, slotno);
        Assert(!XactCtl->shared->page_dirty[slotno]);
 
+       pageno = 
TransactionIdToPage(XidFromFullTransactionId(TransamVariables->nextXid));
+       if (pageno != 0)
+       {
+               LWLock *nextlock = SimpleLruGetBankLock(XactCtl, pageno);
+
+               if (nextlock != lock)
+               {
+                       LWLockRelease(lock);
+                       LWLockAcquire(nextlock, LW_EXCLUSIVE);
+                       lock = nextlock;
+               }
+
+               /* 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(lock);
 }
 
diff --git a/src/backend/access/transam/multixact.c 
b/src/backend/access/transam/multixact.c
index 4dae8f4799..ac68becf8b 100644
--- a/src/backend/access/transam/multixact.c
+++ b/src/backend/access/transam/multixact.c
@@ -1955,6 +1955,7 @@ BootStrapMultiXact(void)
 {
        int                     slotno;
        LWLock     *lock;
+       int64           pageno;
 
        lock = SimpleLruGetBankLock(MultiXactOffsetCtl, 0);
        LWLockAcquire(lock, LW_EXCLUSIVE);
@@ -1966,6 +1967,26 @@ BootStrapMultiXact(void)
        SimpleLruWritePage(MultiXactOffsetCtl, slotno);
        Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
 
+       pageno = MultiXactIdToOffsetPage(MultiXactState->nextMXact);
+       if (pageno != 0)
+       {
+               LWLock *nextlock = SimpleLruGetBankLock(MultiXactOffsetCtl, 
pageno);
+
+               if (nextlock != lock)
+               {
+                       LWLockRelease(lock);
+                       LWLockAcquire(nextlock, LW_EXCLUSIVE);
+                       lock = nextlock;
+               }
+
+               /* Create and zero the first page of the offsets log */
+               slotno = ZeroMultiXactOffsetPage(pageno, false);
+
+               /* Make sure it's written out */
+               SimpleLruWritePage(MultiXactOffsetCtl, slotno);
+               Assert(!MultiXactOffsetCtl->shared->page_dirty[slotno]);
+       }
+
        LWLockRelease(lock);
 
        lock = SimpleLruGetBankLock(MultiXactMemberCtl, 0);
@@ -1978,7 +1999,39 @@ BootStrapMultiXact(void)
        SimpleLruWritePage(MultiXactMemberCtl, slotno);
        Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
 
+       pageno = MXOffsetToMemberPage(MultiXactState->nextOffset);
+       if (pageno != 0)
+       {
+               LWLock *nextlock = SimpleLruGetBankLock(MultiXactMemberCtl, 
pageno);
+
+               if (nextlock != lock)
+               {
+                       LWLockRelease(lock);
+                       LWLockAcquire(nextlock, LW_EXCLUSIVE);
+                       lock = nextlock;
+               }
+
+               /* Create and zero the first page of the members log */
+               slotno = ZeroMultiXactMemberPage(pageno, false);
+
+               /* Make sure it's written out */
+               SimpleLruWritePage(MultiXactMemberCtl, slotno);
+               Assert(!MultiXactMemberCtl->shared->page_dirty[slotno]);
+       }
+
        LWLockRelease(lock);
+
+       /*
+        * 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,
+                                                  MultiXactState->nextOffset, 
0, NULL);
+       }
 }
 
 /*
diff --git a/src/backend/access/transam/subtrans.c 
b/src/backend/access/transam/subtrans.c
index 15153618fa..218675fa60 100644
--- a/src/backend/access/transam/subtrans.c
+++ b/src/backend/access/transam/subtrans.c
@@ -270,12 +270,16 @@ void
 BootStrapSUBTRANS(void)
 {
        int                     slotno;
-       LWLock     *lock = SimpleLruGetBankLock(SubTransCtl, 0);
+       LWLock     *lock;
+       int64           pageno;
+
+       pageno = 
TransactionIdToPage(XidFromFullTransactionId(TransamVariables->nextXid));
+       lock = SimpleLruGetBankLock(SubTransCtl, pageno);
 
        LWLockAcquire(lock, 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);
diff --git a/src/backend/access/transam/xlog.c 
b/src/backend/access/transam/xlog.c
index 799fc739e1..ced9d5cd26 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -136,6 +136,10 @@ int                        max_slot_wal_keep_size_mb = -1;
 int                    wal_decode_buffer_size = 512 * 1024;
 bool           track_wal_io_timing = false;
 
+TransactionId          start_xid = FirstNormalTransactionId;
+MultiXactId                    start_mxid = FirstMultiXactId;
+MultiXactOffset                start_mxoff = 0;
+
 #ifdef WAL_DEBUG
 bool           XLOG_DEBUG = false;
 #endif
@@ -5126,13 +5130,14 @@ BootStrapXLOG(uint32 data_checksum_version)
        checkPoint.fullPageWrites = fullPageWrites;
        checkPoint.wal_level = wal_level;
        checkPoint.nextXid =
-               FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
+               FullTransactionIdFromEpochAndXid(0, 
Max(FirstNormalTransactionId,
+                                                                               
                start_xid));
        checkPoint.nextOid = FirstGenbkiObjectId;
-       checkPoint.nextMulti = FirstMultiXactId;
-       checkPoint.nextMultiOffset = 0;
-       checkPoint.oldestXid = FirstNormalTransactionId;
+       checkPoint.nextMulti = Max(FirstMultiXactId, start_mxid);
+       checkPoint.nextMultiOffset = start_mxoff;
+       checkPoint.oldestXid = XidFromFullTransactionId(checkPoint.nextXid);
        checkPoint.oldestXidDB = Template1DbOid;
-       checkPoint.oldestMulti = FirstMultiXactId;
+       checkPoint.oldestMulti = checkPoint.nextMulti;
        checkPoint.oldestMultiDB = Template1DbOid;
        checkPoint.oldestCommitTsXid = InvalidTransactionId;
        checkPoint.newestCommitTsXid = InvalidTransactionId;
diff --git a/src/backend/bootstrap/bootstrap.c 
b/src/backend/bootstrap/bootstrap.c
index 6db864892d..458dc3eb29 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -218,7 +218,7 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
        argv++;
        argc--;
 
-       while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:X:-:")) != -1)
+       while ((flag = getopt(argc, argv, "B:c:d:D:Fkm:o:r:X:x:-:")) != -1)
        {
                switch (flag)
                {
@@ -286,12 +286,60 @@ BootstrapModeMain(int argc, char *argv[], bool check_only)
                        case 'k':
                                bootstrap_data_checksum_version = 
PG_DATA_CHECKSUM_VERSION;
                                break;
+                       case 'm':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactIdIsValid(start_mxid))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster multixact id")));
+                                       }
+                               }
+                               break;
+                       case 'o':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxoff = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactOffsetIsValid(start_mxoff))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster multixact offset")));
+                                       }
+                               }
+                               break;
                        case 'r':
                                strlcpy(OutputFileName, optarg, MAXPGPATH);
                                break;
                        case 'X':
                                SetConfigOption("wal_segment_size", optarg, 
PGC_INTERNAL, PGC_S_DYNAMIC_DEFAULT);
                                break;
+                       case 'x':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_xid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartTransactionIdIsValid(start_xid))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster xid value")));
+                                       }
+                               }
+                               break;
                        default:
                                write_stderr("Try \"%s --help\" for more 
information.\n",
                                                         progname);
diff --git a/src/backend/main/main.c b/src/backend/main/main.c
index e8effe5024..ff252dffbd 100644
--- a/src/backend/main/main.c
+++ b/src/backend/main/main.c
@@ -426,12 +426,18 @@ help(const char *progname)
        printf(_("  -E                 echo statement before execution\n"));
        printf(_("  -j                 do not use newline as interactive query 
delimiter\n"));
        printf(_("  -r FILENAME        send stdout and stderr to given 
file\n"));
+       printf(_("  -m START_MXID      set initial database cluster multixact 
id\n"));
+       printf(_("  -o START_MXOFF     set initial database cluster multixact 
offset\n"));
+       printf(_("  -x START_XID       set initial database cluster xid\n"));
 
        printf(_("\nOptions for bootstrapping mode:\n"));
        printf(_("  --boot             selects bootstrapping mode (must be 
first argument)\n"));
        printf(_("  --check            selects check mode (must be first 
argument)\n"));
        printf(_("  DBNAME             database name (mandatory argument in 
bootstrapping mode)\n"));
        printf(_("  -r FILENAME        send stdout and stderr to given 
file\n"));
+       printf(_("  -m START_MXID      set initial database cluster multixact 
id\n"));
+       printf(_("  -o START_MXOFF     set initial database cluster multixact 
offset\n"));
+       printf(_("  -x START_XID       set initial database cluster xid\n"));
 
        printf(_("\nPlease read the documentation for the complete list of 
run-time\n"
                         "configuration settings and how to set them on the 
command line or in\n"
diff --git a/src/backend/postmaster/postmaster.c 
b/src/backend/postmaster/postmaster.c
index 5dd3b6a4fd..d166c26f4c 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -585,7 +585,7 @@ PostmasterMain(int argc, char *argv[])
         * tcop/postgres.c (the option sets should not conflict) and with the
         * common help() function in main/main.c.
         */
-       while ((opt = getopt(argc, argv, 
"B:bC:c:D:d:EeFf:h:ijk:lN:OPp:r:S:sTt:W:-:")) != -1)
+       while ((opt = getopt(argc, argv, 
"B:bC:c:D:d:EeFf:h:ijk:lm:N:Oo:Pp:r:S:sTt:W:x:-:")) != -1)
        {
                switch (opt)
                {
@@ -695,10 +695,18 @@ PostmasterMain(int argc, char *argv[])
                                SetConfigOption("max_connections", optarg, 
PGC_POSTMASTER, PGC_S_ARGV);
                                break;
 
+                       case 'm':
+                               /* only used by single-user backend */
+                               break;
+
                        case 'O':
                                SetConfigOption("allow_system_table_mods", 
"true", PGC_POSTMASTER, PGC_S_ARGV);
                                break;
 
+                       case 'o':
+                               /* only used by single-user backend */
+                               break;
+
                        case 'P':
                                SetConfigOption("ignore_system_indexes", 
"true", PGC_POSTMASTER, PGC_S_ARGV);
                                break;
@@ -749,6 +757,10 @@ PostmasterMain(int argc, char *argv[])
                                SetConfigOption("post_auth_delay", optarg, 
PGC_POSTMASTER, PGC_S_ARGV);
                                break;
 
+                       case 'x':
+                               /* only used by single-user backend */
+                               break;
+
                        default:
                                write_stderr("Try \"%s --help\" for more 
information.\n",
                                                         progname);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index f2f75aa0f8..9b8326ed6a 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3790,7 +3790,7 @@ process_postgres_switches(int argc, char *argv[], 
GucContext ctx,
         * postmaster/postmaster.c (the option sets should not conflict) and 
with
         * the common help() function in main/main.c.
         */
-       while ((flag = getopt(argc, argv, 
"B:bC:c:D:d:EeFf:h:ijk:lN:nOPp:r:S:sTt:v:W:-:")) != -1)
+       while ((flag = getopt(argc, argv, 
"B:bC:c:D:d:EeFf:h:ijk:lm:N:nOo:Pp:r:S:sTt:v:W:x:-:")) != -1)
        {
                switch (flag)
                {
@@ -3895,6 +3895,23 @@ process_postgres_switches(int argc, char *argv[], 
GucContext ctx,
                                SetConfigOption("ssl", "true", ctx, gucsource);
                                break;
 
+                       case 'm':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactIdIsValid(start_mxid))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster multixact id")));
+                                       }
+                               }
+                               break;
+
                        case 'N':
                                SetConfigOption("max_connections", optarg, ctx, 
gucsource);
                                break;
@@ -3907,6 +3924,23 @@ process_postgres_switches(int argc, char *argv[], 
GucContext ctx,
                                SetConfigOption("allow_system_table_mods", 
"true", ctx, gucsource);
                                break;
 
+                       case 'o':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxoff = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactOffsetIsValid(start_mxoff))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster multixact offset")));
+                                       }
+                               }
+                               break;
+
                        case 'P':
                                SetConfigOption("ignore_system_indexes", 
"true", ctx, gucsource);
                                break;
@@ -3961,6 +3995,23 @@ process_postgres_switches(int argc, char *argv[], 
GucContext ctx,
                                SetConfigOption("post_auth_delay", optarg, ctx, 
gucsource);
                                break;
 
+                       case 'x':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_xid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartTransactionIdIsValid(start_xid))
+                                       {
+                                               ereport(ERROR,
+                                                               
(errcode(ERRCODE_SYNTAX_ERROR),
+                                                                
errmsg("invalid initial database cluster xid")));
+                                       }
+                               }
+                               break;
+
                        default:
                                errs++;
                                break;
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 21a0fe3ecd..04d56cca4f 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -168,6 +168,9 @@ static bool data_checksums = true;
 static char *xlog_dir = NULL;
 static int     wal_segment_size_mb = (DEFAULT_XLOG_SEG_SIZE) / (1024 * 1024);
 static DataDirSyncMethod sync_method = DATA_DIR_SYNC_METHOD_FSYNC;
+static TransactionId start_xid = 0;
+static MultiXactId start_mxid = 0;
+static MultiXactOffset start_mxoff = 0;
 
 
 /* internal vars */
@@ -1598,6 +1601,11 @@ bootstrap_template1(void)
        bki_lines = replace_token(bki_lines, "POSTGRES",
                                                          
escape_quotes_bki(username));
 
+       /* relfrozenxid must not be less than FirstNormalTransactionId */
+       sprintf(buf, "%llu", (unsigned long long) Max(start_xid, 3));
+       bki_lines = replace_token(bki_lines, "RECENTXMIN",
+                                                         buf);
+
        bki_lines = replace_token(bki_lines, "ENCODING",
                                                          
encodingid_to_string(encodingid));
 
@@ -1623,6 +1631,9 @@ bootstrap_template1(void)
 
        printfPQExpBuffer(&cmd, "\"%s\" --boot %s %s", backend_exec, 
boot_options, extra_options);
        appendPQExpBuffer(&cmd, " -X %d", wal_segment_size_mb * (1024 * 1024));
+       appendPQExpBuffer(&cmd, " -m %llu", (unsigned long long) start_mxid);
+       appendPQExpBuffer(&cmd, " -o %llu", (unsigned long long) start_mxoff);
+       appendPQExpBuffer(&cmd, " -x %llu", (unsigned long long) start_xid);
        if (data_checksums)
                appendPQExpBuffer(&cmd, " -k");
        if (debug)
@@ -2564,12 +2575,20 @@ usage(const char *progname)
        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_MXID\n"
+                        "                            set initial database 
cluster multixact id\n"
+                        "                            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_MXOFF\n"
+                        "                            set initial database 
cluster multixact offset\n"
+                        "                            max value is 2^62-1\n"));
        printf(_("  -s, --show                show internal settings, then 
exit\n"));
        printf(_("      --sync-method=METHOD  set method for syncing files to 
disk\n"));
        printf(_("  -S, --sync-only           only sync database files to disk, 
then exit\n"));
+       printf(_("  -x, --xid=START_XID       set initial database cluster 
xid\n"
+                        "                            max value is 2^62-1\n"));
        printf(_("\nOther options:\n"));
        printf(_("  -V, --version             output version information, then 
exit\n"));
        printf(_("  -?, --help                show this help, then exit\n"));
@@ -3104,6 +3123,18 @@ initialize_data_directory(void)
        /* Now create all the text config files */
        setup_config();
 
+       if (start_mxid != 0)
+               printf(_("selecting initial multixact id ... %llu\n"),
+                                (unsigned long long) start_mxid);
+
+       if (start_mxoff != 0)
+               printf(_("selecting initial multixact offset ... %llu\n"),
+                                (unsigned long long) start_mxoff);
+
+       if (start_xid != 0)
+               printf(_("selecting initial xid ... %llu\n"),
+                                (unsigned long long) start_xid);
+
        /* Bootstrap template1 */
        bootstrap_template1();
 
@@ -3120,8 +3151,12 @@ initialize_data_directory(void)
        fflush(stdout);
 
        initPQExpBuffer(&cmd);
-       printfPQExpBuffer(&cmd, "\"%s\" %s %s template1 >%s",
-                                         backend_exec, backend_options, 
extra_options, DEVNULL);
+       printfPQExpBuffer(&cmd, "\"%s\" %s %s",
+                                         backend_exec, backend_options, 
extra_options);
+       appendPQExpBuffer(&cmd, " -m %llu", (unsigned long long) start_mxid);
+       appendPQExpBuffer(&cmd, " -o %llu", (unsigned long long) start_mxoff);
+       appendPQExpBuffer(&cmd, " -x %llu", (unsigned long long) start_xid);
+       appendPQExpBuffer(&cmd, " template1 >%s", DEVNULL);
 
        PG_CMD_OPEN(cmd.data);
 
@@ -3208,6 +3243,9 @@ main(int argc, char *argv[])
                {"icu-rules", required_argument, NULL, 18},
                {"sync-method", required_argument, NULL, 19},
                {"no-data-checksums", no_argument, NULL, 20},
+               {"xid", required_argument, NULL, 'x'},
+               {"multixact-id", required_argument, NULL, 'm'},
+               {"multixact-offset", required_argument, NULL, 'o'},
                {NULL, 0, NULL, 0}
        };
 
@@ -3249,7 +3287,7 @@ main(int argc, char *argv[])
 
        /* process command-line options */
 
-       while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:nNsST:U:WX:",
+       while ((c = getopt_long(argc, argv, "A:c:dD:E:gkL:m:nNo:sST:U:Wx:X:",
                                                        long_options, 
&option_index)) != -1)
        {
                switch (c)
@@ -3307,6 +3345,30 @@ main(int argc, char *argv[])
                                debug = true;
                                printf(_("Running in debug mode.\n"));
                                break;
+                       case 'm':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactIdIsValid(start_mxid))
+                                       {
+                                               pg_log_error("invalid initial 
database cluster multixact id");
+                                               exit(1);
+                                       }
+                                       else if (start_mxid < 1) /* 
FirstMultiXactId */
+                                       {
+                                               /*
+                                                * We avoid mxid to be silently 
set to
+                                                * FirstMultiXactId, though it 
does not harm.
+                                                */
+                                               pg_log_error("multixact id 
should be greater than 0");
+                                               exit(1);
+                                       }
+                               }
+                               break;
                        case 'n':
                                noclean = true;
                                printf(_("Running in no-clean mode.  Mistakes 
will not be cleaned up.\n"));
@@ -3314,6 +3376,21 @@ main(int argc, char *argv[])
                        case 'N':
                                do_sync = false;
                                break;
+                       case 'o':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_mxoff = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartMultiXactOffsetIsValid(start_mxoff))
+                                       {
+                                               pg_log_error("invalid initial 
database cluster multixact offset");
+                                               exit(1);
+                                       }
+                               }
+                               break;
                        case 'S':
                                sync_only = true;
                                break;
@@ -3402,6 +3479,30 @@ main(int argc, char *argv[])
                        case 20:
                                data_checksums = false;
                                break;
+                       case 'x':
+                               {
+                                       char       *endptr;
+
+                                       errno = 0;
+                                       start_xid = strtou64(optarg, &endptr, 
0);
+
+                                       if (endptr == optarg || *endptr != '\0' 
|| errno != 0 ||
+                                               
!StartTransactionIdIsValid(start_xid))
+                                       {
+                                               pg_log_error("invalid value for 
initial database cluster xid");
+                                               exit(1);
+                                       }
+                                       else if (start_xid < 3) /* 
FirstNormalTransactionId */
+                                       {
+                                               /*
+                                                * We avoid xid to be silently 
set to
+                                                * FirstNormalTransactionId, 
though it does not harm.
+                                                */
+                                               pg_log_error("xid should be 
greater than 2");
+                                               exit(1);
+                                       }
+                               }
+                               break;
                        default:
                                /* getopt_long already emitted a complaint */
                                pg_log_error_hint("Try \"%s --help\" for more 
information.", progname);
diff --git a/src/bin/initdb/t/001_initdb.pl b/src/bin/initdb/t/001_initdb.pl
index 01cc4a1602..8b017eb907 100644
--- a/src/bin/initdb/t/001_initdb.pl
+++ b/src/bin/initdb/t/001_initdb.pl
@@ -329,4 +329,64 @@ command_fails(
        [ 'pg_checksums', '--pgdata' => $datadir_nochecksums ],
        "pg_checksums fails with data checksum disabled");
 
+# Set non-standard initial mxid/mxoff/xid.
+command_fails_like(
+       [ 'initdb', '-m', 'seven', $datadir ],
+       qr/initdb: error: invalid initial database cluster multixact id/,
+       'fails for invalid initial database cluster multixact id');
+command_fails_like(
+       [ 'initdb', '-o', 'seven', $datadir ],
+       qr/initdb: error: invalid initial database cluster multixact offset/,
+       'fails for invalid initial database cluster multixact offset');
+command_fails_like(
+       [ 'initdb', '-x', 'seven', $datadir ],
+       qr/initdb: error: invalid value for initial database cluster xid/,
+       'fails for invalid initial database cluster xid');
+
+command_checks_all(
+       [ 'initdb', '-m', '65535', "$tempdir/data-m65535" ],
+       0,
+       [qr/selecting initial multixact id ... 65535/],
+       [],
+       'selecting initial multixact id');
+command_checks_all(
+       [ 'initdb', '-o', '65535', "$tempdir/data-o65535" ],
+       0,
+       [qr/selecting initial multixact offset ... 65535/],
+       [],
+       'selecting initial multixact offset');
+command_checks_all(
+       [ 'initdb', '-x', '65535', "$tempdir/data-x65535" ],
+       0,
+       [qr/selecting initial xid ... 65535/],
+       [],
+       'selecting initial xid');
+
+# Setup new cluster with given mxid/mxoff/xid.
+my $node;
+my $result;
+
+$node = PostgreSQL::Test::Cluster->new('test-mxid');
+$node->init(extra => ['-m', '16777215']); # 0xFFFFFF
+$node->start;
+$result = $node->safe_psql('postgres', "SELECT next_multixact_id FROM 
pg_control_checkpoint();");
+ok($result >= 16777215, 'setup cluster with given mxid');
+$node->stop;
+
+$node = PostgreSQL::Test::Cluster->new('test-mxoff');
+$node->init(extra => ['-o', '16777215']); # 0xFFFFFF
+$node->start;
+$result = $node->safe_psql('postgres', "SELECT next_multi_offset FROM 
pg_control_checkpoint();");
+ok($result >= 16777215, 'setup cluster with given mxoff');
+$node->stop;
+
+$node = PostgreSQL::Test::Cluster->new('test-xid');
+$node->init(extra => ['-x', '16777215']); # 0xFFFFFF
+$node->start;
+$result = $node->safe_psql('postgres', "SELECT txid_current();");
+ok($result >= 16777215, 'setup cluster with given xid - check 1');
+$result = $node->safe_psql('postgres', "SELECT oldest_xid FROM 
pg_control_checkpoint();");
+ok($result >= 16777215, 'setup cluster with given xid - check 2');
+$node->stop;
+
 done_testing();
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d313099c02..23b8dd0375 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -94,6 +94,9 @@ typedef enum RecoveryState
 } RecoveryState;
 
 extern PGDLLIMPORT int wal_level;
+extern PGDLLIMPORT TransactionId start_xid;
+extern PGDLLIMPORT MultiXactId start_mxid;
+extern PGDLLIMPORT MultiXactOffset start_mxoff;
 
 /* Is WAL archiving enabled (always or only while server is running normally)? 
*/
 #define XLogArchivingActive() \
diff --git a/src/include/c.h b/src/include/c.h
index 318194f78d..4f2b5432e5 100644
--- a/src/include/c.h
+++ b/src/include/c.h
@@ -622,6 +622,10 @@ typedef uint64 MultiXactOffset;
 
 typedef uint32 CommandId;
 
+#define StartTransactionIdIsValid(xid)                 ((xid) <= 0xFFFFFFFF)
+#define StartMultiXactIdIsValid(mxid)                  ((mxid) <= 0xFFFFFFFF)
+#define StartMultiXactOffsetIsValid(offset)            ((offset) <= 0xFFFFFFFF)
+
 #define FirstCommandId ((CommandId) 0)
 #define InvalidCommandId       (~(CommandId)0)
 
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index fa96ba07bf..44c2f9cdf9 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -126,7 +126,7 @@ CATALOG(pg_class,1259,RelationRelationId) BKI_BOOTSTRAP 
BKI_ROWTYPE_OID(83,Relat
        Oid                     relrewrite BKI_DEFAULT(0) 
BKI_LOOKUP_OPT(pg_class);
 
        /* all Xids < this are frozen in this rel */
-       TransactionId relfrozenxid BKI_DEFAULT(3);      /* 
FirstNormalTransactionId */
+       TransactionId relfrozenxid BKI_DEFAULT(RECENTXMIN);     /* 
FirstNormalTransactionId */
 
        /* all multixacts in this rel are >= this; it is really a MultiXactId */
        TransactionId relminmxid BKI_DEFAULT(1);        /* FirstMultiXactId */
-- 
2.43.0

Attachment: v14-0001-Use-64-bit-format-output-for-multixact-offsets.patch
Description: Binary data

Attachment: v14-0004-Get-rid-of-MultiXactMemberFreezeThreshold-call.patch
Description: Binary data

Attachment: v14-0002-Use-64-bit-multixact-offsets.patch
Description: Binary data

Attachment: v14-0003-Make-pg_upgrade-convert-multixact-offsets.patch
Description: Binary data

From 325612fc61a5f4728b62070169eb2dd013baa119 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <orlo...@gmail.com>
Date: Tue, 19 Nov 2024 17:08:10 +0300
Subject: [PATCH v14 6/7] TEST: add src/bin/pg_upgrade/t/006_offset.pl

---
 src/bin/pg_upgrade/t/006_offset.pl | 562 +++++++++++++++++++++++++++++
 1 file changed, 562 insertions(+)
 create mode 100644 src/bin/pg_upgrade/t/006_offset.pl

diff --git a/src/bin/pg_upgrade/t/006_offset.pl 
b/src/bin/pg_upgrade/t/006_offset.pl
new file mode 100644
index 0000000000..f5dc733a30
--- /dev/null
+++ b/src/bin/pg_upgrade/t/006_offset.pl
@@ -0,0 +1,562 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+use strict;
+use warnings FATAL => 'all';
+
+use File::Find qw(find);
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+# This pair of calls will create significantly more member segments than offset
+# segments.
+sub prep
+{
+       my $node = shift;
+       my $tbl = shift;
+
+       $node->safe_psql('postgres',
+               "CREATE TABLE ${tbl} (I INT PRIMARY KEY, N_UPDATED INT) " .
+               "       WITH (AUTOVACUUM_ENABLED=FALSE);" .
+               "INSERT INTO ${tbl} SELECT G, 0 FROM GENERATE_SERIES(1, 50) 
G;");
+}
+
+sub fill
+{
+       my $node = shift;
+       my $tbl = shift;
+
+       my $nclients = 50;
+       my $update_every = 90;
+       my @connections = ();
+
+       for (0..$nclients)
+       {
+               my $conn = $node->background_psql('postgres');
+               $conn->query_safe("BEGIN");
+
+               push(@connections, $conn);
+       }
+
+       for (my $i = 0; $i < 20000; $i++)
+       {
+               my $conn = $connections[$i % $nclients];
+
+               $conn->query_safe("COMMIT;");
+               $conn->query_safe("BEGIN");
+
+               if ($i % $update_every == 0)
+               {
+                       $conn->query_safe(
+                               "UPDATE ${tbl} SET " .
+                               "N_UPDATED = N_UPDATED + 1 " .
+                               "WHERE I = ${i} % 50");
+               }
+               else
+               {
+                       $conn->query_safe(
+                               "SELECT * FROM ${tbl} FOR KEY SHARE");
+               }
+       }
+
+       for my $conn (@connections)
+       {
+               $conn->quit();
+       }
+}
+
+# This pair of calls will create more or less the same amount of membsers and
+# offsets segments.
+sub prep2
+{
+       my $node = shift;
+       my $tbl = shift;
+
+       $node->safe_psql('postgres',
+               "CREATE TABLE ${tbl}(BAR INT PRIMARY KEY, BAZ INT); " .
+               "CREATE OR REPLACE PROCEDURE MXIDFILLER(N_STEPS INT DEFAULT 
1000) " .
+               "LANGUAGE PLPGSQL " .
+               "AS \$\$ " .
+               "BEGIN " .
+               "       FOR I IN 1..N_STEPS LOOP " .
+               "               UPDATE ${tbl} SET BAZ = RANDOM(1, 1000) " .
+               "               WHERE BAR IN (SELECT BAR FROM ${tbl} " .
+               "                                               TABLESAMPLE 
BERNOULLI(80)); " .
+               "               COMMIT; " .
+               "       END LOOP; " .
+               "END; \$\$; " .
+               "INSERT INTO ${tbl} (BAR, BAZ) " .
+               "SELECT ID, ID FROM GENERATE_SERIES(1, 1024) ID;");
+}
+
+sub fill2
+{
+       my $node = shift;
+       my $tbl = shift;
+       my $scale = shift // 1;
+
+       $node->safe_psql('postgres',
+               "BEGIN; " .
+               "SELECT * FROM ${tbl} FOR KEY SHARE; " .
+               "PREPARE TRANSACTION 'A'; " .
+               "CALL MXIDFILLER((365 * ${scale})::int); " .
+               "COMMIT PREPARED 'A';");
+}
+
+
+# generate around 2 offset segments and 55 member segments
+sub mxid_gen1
+{
+       my $node = shift;
+       my $tbl = shift;
+
+       prep($node, $tbl);
+       fill($node, $tbl);
+
+       $node->safe_psql('postgres', q(CHECKPOINT));
+}
+
+# generate around 10 offset segments and 12 member segments
+sub mxid_gen2
+{
+       my $node = shift;
+       my $tbl = shift;
+       my $scale = shift // 1;
+
+       prep2($node, $tbl);
+       fill2($node, $tbl, $scale);
+
+       $node->safe_psql('postgres', q(CHECKPOINT));
+}
+
+# Fetch latest multixact checkpoint values.
+sub multi_bounds
+{
+       my ($node) = @_;
+       my $path = $node->config_data('--bindir');
+       my ($stdout, $stderr) = run_command([
+                                                                       $path . 
'/pg_controldata',
+                                                                       
$node->data_dir
+                                                               ]);
+       my @control_data = split("\n", $stdout);
+       my $next = undef;
+       my $oldest = undef;
+       my $next_offset = undef;
+
+       foreach (@control_data)
+       {
+               if ($_ =~ /^Latest checkpoint's NextMultiXactId:\s*(.*)$/mg)
+               {
+                       $next = $1;
+                       print ">>> @ node ". $node->name . ", " . $_ . "\n";
+               }
+
+               if ($_ =~ /^Latest checkpoint's oldestMultiXid:\s*(.*)$/mg)
+               {
+                       $oldest = $1;
+                       print ">>> @ node ". $node->name . ", " . $_ . "\n";
+               }
+
+               if ($_ =~ /^Latest checkpoint's NextMultiOffset:\s*(.*)$/mg)
+               {
+                       $next_offset = $1;
+                       print ">>> @ node ". $node->name . ", " . $_ . "\n";
+               }
+
+               if (defined($oldest) && defined($next) && defined($next_offset))
+               {
+                       last;
+               }
+       }
+
+       die "Latest checkpoint's NextMultiXactId not found in control file!\n"
+       unless defined($next);
+
+       die "Latest checkpoint's oldestMultiXid not found in control file!\n"
+       unless defined($oldest);
+
+       die "Latest checkpoint's NextMultiOffset not found in control file!\n"
+       unless defined($next_offset);
+
+       return ($oldest, $next, $next_offset);
+}
+
+# Create node from existing bins.
+sub create_new_node
+{
+       my ($name, %params) = @_;
+
+       create_node(0, @_);
+}
+
+# Create node from ENV oldinstall
+sub create_old_node
+{
+       my ($name, %params) = @_;
+
+       if (!defined($ENV{oldinstall}))
+       {
+               die "oldinstall is not defined";
+       }
+
+       create_node(1, @_);
+}
+
+sub create_node
+{
+       my ($install_path_from_env, $name, %params) = @_;
+       my $scale = defined $params{scale} ? $params{scale} : 1;
+       my $multi = defined $params{multi} ? $params{multi} : undef;
+       my $offset = defined $params{offset} ? $params{offset} : undef;
+
+       my $node =
+               $install_path_from_env ?
+                       PostgreSQL::Test::Cluster->new($name,
+                                       install_path => $ENV{oldinstall}) :
+                       PostgreSQL::Test::Cluster->new($name);
+
+       $node->init(force_initdb => 1,
+               extra => [
+                       $multi ? ('-m', $multi) : (),
+                       $offset ? ('-o', $offset) : (),
+               ]);
+
+       # Fixup MOX patch quirk
+       if ($multi)
+       {
+               unlink $node->data_dir . '/pg_multixact/offsets/0000';
+       }
+       if ($offset)
+       {
+               unlink $node->data_dir . '/pg_multixact/members/0000';
+       }
+
+       $node->append_conf('fsync', 'off');
+       $node->append_conf('postgresql.conf', 'max_prepared_transactions = 2');
+
+       $node->start();
+       mxid_gen2($node, 'FOO', $scale);
+       mxid_gen1($node, 'BAR', $scale);
+       $node->restart();
+       $node->safe_psql('postgres', q(SELECT * FROM FOO));             # just 
in case...
+       $node->safe_psql('postgres', q(SELECT * FROM BAR));
+       $node->safe_psql('postgres', q(CHECKPOINT));
+       $node->stop();
+
+       return $node;
+}
+
+sub do_upgrade
+{
+       my ($oldnode, $newnode) = @_;
+
+       command_ok(
+               [
+                       'pg_upgrade', '--no-sync',
+                       '-d', $oldnode->data_dir,
+                       '-D', $newnode->data_dir,
+                       '-b', $oldnode->config_data('--bindir'),
+                       '-B', $newnode->config_data('--bindir'),
+                       '-s', $newnode->host,
+                       '-p', $oldnode->port,
+                       '-P', $newnode->port,
+                       '--check'
+               ],
+               'run of pg_upgrade');
+
+       command_ok(
+               [
+                       'pg_upgrade', '--no-sync',
+                       '-d', $oldnode->data_dir,
+                       '-D', $newnode->data_dir,
+                       '-b', $oldnode->config_data('--bindir'),
+                       '-B', $newnode->config_data('--bindir'),
+                       '-s', $newnode->host,
+                       '-p', $oldnode->port,
+                       '-P', $newnode->port,
+                       '--copy'
+               ],
+               'run of pg_upgrade');
+
+       $oldnode->start();
+       $newnode->start();
+
+       my $oldfoo = $oldnode->safe_psql('postgres', q(SELECT * FROM FOO));
+       my $newfoo = $newnode->safe_psql('postgres', q(SELECT * FROM FOO));
+       is($oldfoo, $newfoo, "select foo eq");
+
+       my $oldbar = $oldnode->safe_psql('postgres', q(SELECT * FROM BAR));
+       my $newbar = $newnode->safe_psql('postgres', q(SELECT * FROM BAR));
+       is($oldbar, $newbar, "select bar eq");
+
+       $oldnode->stop();
+       $newnode->stop();
+
+       multi_bounds($oldnode);
+       multi_bounds($newnode);
+}
+
+my @TESTS = (
+       # tests without ENV oldinstall
+       #0, 1, 2, 3, 4, 5, 6,
+       # tests with "real" pg_upgrade
+       #100, 101, 102, 103, 104, 105, 106,
+       # self upgrade
+       1000,
+);
+
+# =============================================================================
+# Basic sanity tests on a NEW bin
+# =============================================================================
+
+# starts from the zero
+SKIP:
+{
+       my $TEST_NO = 0;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_mo',
+                                               scale => 1);
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value
+SKIP:
+{
+       my $TEST_NO = 1;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_Mo',
+                                               scale => 1.15,
+                                               multi => '0x123400');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# offsets starts from the value
+SKIP:
+{
+       my $TEST_NO = 2;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_mO',
+                                               scale => 1.15,
+                                               offset => '0x432100');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi and offsets starts from the value
+SKIP:
+{
+       my $TEST_NO = 3;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_MO',
+                                               scale => 1.15,
+                                               multi => '0xDEAD00', offset => 
'0xBEEF00');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value, multi wrap
+SKIP:
+{
+       my $TEST_NO = 4;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_Mo_wrap',
+                                               scale => 1.15,
+                                               multi => '0xFFFF7000');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# offsets starts from the value, offsets wrap
+SKIP:
+{
+       my $TEST_NO = 5;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_mO_wrap',
+                                               scale => 1.15,
+                                               offset => '0xFFFFFC00');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value, offsets starts from the value,
+# multi wrap, offsets wrap
+SKIP:
+{
+       my $TEST_NO = 6;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $node = create_new_node('simple_MO_wrap',
+                                               scale => 1.15,
+                                               multi => '0xFFFF7000', offset 
=> '0xFFFFFC00');
+       multi_bounds($node);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# =============================================================================
+# pg_upgarde tests
+# =============================================================================
+
+# starts from the zero
+SKIP:
+{
+       my $TEST_NO = 100;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'mo';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1);
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value
+SKIP:
+{
+       my $TEST_NO = 101;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'Mo';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               multi => '0x123400');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# offsets starts from the value
+SKIP:
+{
+       my $TEST_NO = 102;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'mO';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               offset => '0x432100');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi and offsets starts from the value
+SKIP:
+{
+       my $TEST_NO = 103;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'MO';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               multi => '0xDEAD00', offset => 
'0xBEEF00');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value, multi wrap
+SKIP:
+{
+       my $TEST_NO = 104;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'Mo_wrap';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               multi => '0xFFFF7000');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# offsets starts from the value, offsets wrap
+SKIP:
+{
+       my $TEST_NO = 105;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'mO_wrap';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               offset => '0xFFFFFC00');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# multi starts from the value, offsets starts from the value,
+# multi wrap, offsets wrap
+SKIP:
+{
+       my $TEST_NO = 106;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'MO_wrap';
+       my $oldnode = create_old_node("old_$dbname",
+                                               scale => 1.2,
+                                               multi => '0xFFFF7000', offset 
=> '0xFFFFFC00');
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+# =============================================================================
+# Self upgrade
+# =============================================================================
+
+# starts from the zero
+SKIP:
+{
+       my $TEST_NO = 1000;
+       skip "do not test case $TEST_NO", 1
+               unless ( grep( /^$TEST_NO$/, @TESTS ) );
+
+       my $dbname = 'self_upgrade';
+       my $oldnode = create_new_node("old_$dbname",
+                                               scale => 1);
+       my $newnode = PostgreSQL::Test::Cluster->new("new_$dbname");
+       $newnode->init();
+
+       do_upgrade($oldnode, $newnode);
+       ok(1, "TEST $TEST_NO PASSED");
+}
+
+done_testing();
-- 
2.43.0

From b80320d07ade1fba3fce94326bd2e5e06a9e7f58 Mon Sep 17 00:00:00 2001
From: Maxim Orlov <orlo...@gmail.com>
Date: Wed, 13 Nov 2024 16:34:34 +0300
Subject: [PATCH v14 7/7] TEST: bump catver

---
 src/bin/pg_upgrade/pg_upgrade.h  | 2 +-
 src/include/catalog/catversion.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/bin/pg_upgrade/pg_upgrade.h b/src/bin/pg_upgrade/pg_upgrade.h
index 1adea73bd3..14d49be040 100644
--- a/src/bin/pg_upgrade/pg_upgrade.h
+++ b/src/bin/pg_upgrade/pg_upgrade.h
@@ -119,7 +119,7 @@ extern char *output_files[];
  *
  * XXX: should be changed to the actual CATALOG_VERSION_NO on commit.
  */
-#define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202409041
+#define MULTIXACTOFFSET_FORMATCHANGE_CAT_VER 202503032
 
 /*
  * large object chunk size added to pg_controldata,
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index f0962e17b3..c952e122f2 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -57,6 +57,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202503031
+#define CATALOG_VERSION_NO     202503032
 
 #endif
-- 
2.43.0

Reply via email to