diff --git a/doc/src/sgml/ref/initdb.sgml b/doc/src/sgml/ref/initdb.sgml new file mode 100644 index 6efb2e4..e4b1a04 *** a/doc/src/sgml/ref/initdb.sgml --- b/doc/src/sgml/ref/initdb.sgml *************** PostgreSQL documentation *** 376,381 **** --- 376,420 ---- + Other options for testing 64-bit XIDs: + + + + + + + + Specifies a start multixact id value in the decimal format for new db instance to test 64-bit xids, + default value is 0. + + + + + + + + + + Specifies a start multixact offset value in the decimal format for new db instance to test 64-bit xids, + default value is 0. + + + + + + + + + + Specifies a start xid value in the decimal for new db instance to test 64-bit xids, + default value is 0. + + + + + + + Other options: diff --git a/src/backend/access/transam/clog.c b/src/backend/access/transam/clog.c new file mode 100644 index 6136ffc..281f237 *** a/src/backend/access/transam/clog.c --- b/src/backend/access/transam/clog.c *************** void *** 462,467 **** --- 462,470 ---- BootStrapCLOG(void) { int slotno; + int64 pageno; + + pageno = TransactionIdToPage(ShmemVariableCache->nextXid); LWLockAcquire(CLogControlLock, LW_EXCLUSIVE); *************** BootStrapCLOG(void) *** 472,477 **** --- 475,490 ---- SimpleLruWritePage(ClogCtl, slotno); Assert(!ClogCtl->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(ClogCtl, slotno); + Assert(!ClogCtl->shared->page_dirty[slotno]); + } + LWLockRelease(CLogControlLock); } diff --git a/src/backend/access/transam/multixact.c b/src/backend/access/transam/multixact.c new file mode 100644 index a4b2c97..8e11a49 *** a/src/backend/access/transam/multixact.c --- b/src/backend/access/transam/multixact.c *************** void *** 1666,1671 **** --- 1666,1673 ---- BootStrapMultiXact(void) { int slotno; + int64 multiOffsetPageno; + int64 multiMemberPageno; LWLockAcquire(MultiXactOffsetControlLock, LW_EXCLUSIVE); *************** BootStrapMultiXact(void) *** 1676,1681 **** --- 1678,1694 ---- 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(MultiXactOffsetControlLock); LWLockAcquire(MultiXactMemberControlLock, LW_EXCLUSIVE); *************** BootStrapMultiXact(void) *** 1687,1693 **** --- 1700,1731 ---- 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(MultiXactMemberControlLock); + + /* + * 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); + } } /* diff --git a/src/backend/access/transam/subtrans.c b/src/backend/access/transam/subtrans.c new file mode 100644 index cc9eee6..8797a52 *** a/src/backend/access/transam/subtrans.c --- b/src/backend/access/transam/subtrans.c *************** void *** 210,220 **** BootStrapSUBTRANS(void) { int slotno; LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); /* Create and zero the first page of the subtrans log */ ! slotno = ZeroSUBTRANSPage(0); /* Make sure it's written out */ SimpleLruWritePage(SubTransCtl, slotno); --- 210,223 ---- BootStrapSUBTRANS(void) { int slotno; + int64 pageno; + + pageno = TransactionIdToPage(ShmemVariableCache->nextXid); LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE); /* Create and zero the first page of the subtrans log */ ! 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 new file mode 100644 index 6c6e88e..b36f751 *** a/src/backend/access/transam/xlog.c --- b/src/backend/access/transam/xlog.c *************** int CommitDelay = 0; /* precommit dela *** 106,111 **** --- 106,115 ---- int CommitSiblings = 5; /* # concurrent xacts needed to sleep */ int wal_retrieve_retry_interval = 5000; + TransactionId start_xid = 0; + MultiXactId start_mx_id = 0; + MultiXactOffset start_mx_offset = 0; + #ifdef WAL_DEBUG bool XLOG_DEBUG = false; #endif *************** BootStrapXLOG(void) *** 5003,5012 **** checkPoint.ThisTimeLineID = ThisTimeLineID; checkPoint.PrevTimeLineID = ThisTimeLineID; checkPoint.fullPageWrites = fullPageWrites; ! checkPoint.nextXid = FirstNormalTransactionId + 1; checkPoint.nextOid = FirstBootstrapObjectId; ! checkPoint.nextMulti = FirstMultiXactId + 1; ! checkPoint.nextMultiOffset = 0; checkPoint.oldestXid = checkPoint.nextXid - 1; checkPoint.oldestXidDB = TemplateDbOid; checkPoint.oldestMulti = checkPoint.nextMulti - 1; --- 5007,5016 ---- checkPoint.ThisTimeLineID = ThisTimeLineID; checkPoint.PrevTimeLineID = ThisTimeLineID; checkPoint.fullPageWrites = fullPageWrites; ! checkPoint.nextXid = Max(FirstNormalTransactionId + 1, start_xid); checkPoint.nextOid = FirstBootstrapObjectId; ! checkPoint.nextMulti = Max(FirstMultiXactId + 1, start_mx_id); ! checkPoint.nextMultiOffset = start_mx_offset; checkPoint.oldestXid = checkPoint.nextXid - 1; checkPoint.oldestXidDB = TemplateDbOid; checkPoint.oldestMulti = checkPoint.nextMulti - 1; diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c new file mode 100644 index 9fd692b..394e3a3 *** a/src/backend/bootstrap/bootstrap.c --- b/src/backend/bootstrap/bootstrap.c *************** AuxiliaryProcessMain(int argc, char *arg *** 222,228 **** /* If no -x argument, we are a CheckerProcess */ MyAuxProcType = CheckerProcess; ! while ((flag = getopt(argc, argv, "B:c:d:D:Fkr:x:-:")) != -1) { switch (flag) { --- 222,231 ---- /* If no -x argument, we are a CheckerProcess */ MyAuxProcType = CheckerProcess; ! start_xid = 0; ! start_mx_id = 0; ! start_mx_offset = 0; ! while ((flag = getopt(argc, argv, "B:c:d:D:Fkm:o:r:X:x:-:")) != -1) { switch (flag) { *************** AuxiliaryProcessMain(int argc, char *arg *** 251,259 **** --- 254,283 ---- 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 'X': + 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': MyAuxProcType = atoi(optarg); break; diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c new file mode 100644 index cd2f4b6..5851ddf *** a/src/bin/initdb/initdb.c --- b/src/bin/initdb/initdb.c *************** static bool sync_only = false; *** 141,146 **** --- 141,149 ---- static bool show_setting = false; static bool data_checksums = false; static char *xlog_dir = ""; + static TransactionId start_xid = 0; + static MultiXactId start_mx_id = 0; + static MultiXactOffset start_mx_offset = 0; /* internal vars */ *************** bootstrap_template1(void) *** 1356,1364 **** unsetenv("PGCLIENTENCODING"); snprintf(cmd, sizeof(cmd), ! "\"%s\" --boot -x1 %s %s %s", backend_exec, data_checksums ? "-k" : "", boot_options, talkargs); PG_CMD_OPEN; --- 1359,1370 ---- unsetenv("PGCLIENTENCODING"); snprintf(cmd, sizeof(cmd), ! "\"%s\" --boot -x1 %s %s " XID_FMT " %s " XID_FMT " %s " XID_FMT " %s %s", backend_exec, data_checksums ? "-k" : "", + "-X", start_xid, + "-m", start_mx_id, + "-o", start_mx_offset, boot_options, talkargs); PG_CMD_OPEN; *************** usage(const char *progname) *** 2272,2287 **** printf(_(" --no-locale equivalent to --locale=C\n")); printf(_(" --pwfile=FILE read password for the new superuser from file\n")); printf(_(" -T, --text-search-config=CFG\n" ! " default text search configuration\n")); printf(_(" -U, --username=NAME database superuser name\n")); 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(_("\nLess commonly used options:\n")); printf(_(" -d, --debug generate lots of debugging output\n")); printf(_(" -k, --data-checksums use data page checksums\n")); printf(_(" -L DIRECTORY where to find the input files\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(_(" -s, --show show internal settings\n")); printf(_(" -S, --sync-only only sync data directory\n")); printf(_("\nOther options:\n")); --- 2278,2301 ---- printf(_(" --no-locale equivalent to --locale=C\n")); printf(_(" --pwfile=FILE read password for the new superuser from file\n")); printf(_(" -T, --text-search-config=CFG\n" ! " default text search configuration\n")); printf(_(" -U, --username=NAME database superuser name\n")); 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(_(" -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(_(" -k, --data-checksums use data page checksums\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(_(" -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 data directory\n")); printf(_("\nOther options:\n")); *************** main(int argc, char *argv[]) *** 2965,2970 **** --- 2979,2987 ---- {"no-sync", no_argument, NULL, 'N'}, {"sync-only", no_argument, NULL, 'S'}, {"waldir", required_argument, NULL, 'X'}, + {"xid", required_argument, NULL, 'x'}, + {"multixact-id", required_argument, NULL, 'm'}, + {"multixact-offset", required_argument, NULL, 'o'}, {"data-checksums", no_argument, NULL, 'k'}, {NULL, 0, NULL, 0} }; *************** main(int argc, char *argv[]) *** 3007,3013 **** /* process command-line options */ ! while ((c = getopt_long(argc, argv, "dD:E:kL:nNU:WA:sST:X:", long_options, &option_index)) != -1) { switch (c) { --- 3024,3030 ---- /* process command-line options */ ! while ((c = getopt_long(argc, argv, "dD:E:kL:m:nNU:WA:o:sST:X:x:", long_options, &option_index)) != -1) { switch (c) { *************** main(int argc, char *argv[]) *** 3046,3051 **** --- 3063,3082 ---- 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")); *************** main(int argc, char *argv[]) *** 3053,3058 **** --- 3084,3103 ---- 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; *************** main(int argc, char *argv[]) *** 3098,3103 **** --- 3143,3162 ---- case 'X': xlog_dir = pg_strdup(optarg); 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/include/access/xlog.h b/src/include/access/xlog.h new file mode 100644 index 41c2fde..981f898 *** a/src/include/access/xlog.h --- b/src/include/access/xlog.h *************** typedef enum WalLevel *** 130,135 **** --- 130,143 ---- 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/test/perl/PostgresNode.pm b/src/test/perl/PostgresNode.pm new file mode 100644 index 42e66ed..b5be7fd *** a/src/test/perl/PostgresNode.pm --- b/src/test/perl/PostgresNode.pm *************** sub init *** 405,410 **** --- 405,411 ---- mkdir $self->archive_dir; TestLib::system_or_bail('initdb', '-D', $pgdata, '-A', 'trust', '-N', + '-x', '1249835483136', '-m', '2422361554944', '-o', '3594887626752', @{ $params{extra} }); TestLib::system_or_bail($ENV{PG_REGRESS}, '--config-auth', $pgdata); diff --git a/src/test/regress/pg_regress.c b/src/test/regress/pg_regress.c new file mode 100644 index b685aeb..71a2058 *** a/src/test/regress/pg_regress.c --- b/src/test/regress/pg_regress.c *************** regression_main(int argc, char *argv[], *** 2246,2252 **** /* 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", bindir ? bindir : "", bindir ? "/" : "", temp_instance, --- 2246,2252 ---- /* initdb */ header(_("initializing database system")); snprintf(buf, sizeof(buf), ! "\"%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,