/* * test_server_conn() * * tests whether postmaster is running or not by trying to connect * to it. If connection is unsuccessfull we do a sleep of 1 sec and then * try the connection again. This process continues "timeout" times. * * Returns true if the connection attempt was successfull, false otherwise. */ static bool test_server_conn(ClusterInfo *cluster, int timeout) { unsigned short port = cluster->port; PGconn *conn = NULL; char con_opts[MAX_STRING]; int tries; bool ret = false; snprintf(con_opts, sizeof(con_opts), "dbname = 'template1' user = '******' port = %d ", os_info.user, port); for (tries = 0; tries < timeout; tries++) { sleep(1); if ((conn = PQconnectdb(con_opts)) != NULL && PQstatus(conn) == CONNECTION_OK) { PQfinish(conn); ret = true; break; } if (tries == STARTUP_WARNING_TRIES) prep_status("Trying to start %s server ", CLUSTER_NAME(cluster)); else if (tries > STARTUP_WARNING_TRIES) pg_log(PG_REPORT, "."); } if (tries > STARTUP_WARNING_TRIES) check_ok(); return ret; }
/* * Test pg_upgrade_support.so is in the proper place. We cannot copy it * ourselves because install directories are typically root-owned. */ static void check_for_support_lib(ClusterInfo *cluster) { char cmd[MAX_PG_PATH]; char libdir[MAX_STRING]; char libfile[MAX_PG_PATH]; FILE *lib_test; FILE *output; snprintf(cmd, sizeof(cmd), "\"%s/pg_config\" --pkglibdir", cluster->bindir); if ((output = popen(cmd, "r")) == NULL) pg_log(PG_FATAL, "Could not get pkglibdir data: %s\n", getErrorText(errno)); fgets(libdir, sizeof(libdir), output); pclose(output); /* Remove trailing newline */ if (strchr(libdir, '\n') != NULL) *strchr(libdir, '\n') = '\0'; snprintf(libfile, sizeof(libfile), "%s/pg_upgrade_support%s", libdir, DLSUFFIX); if ((lib_test = fopen(libfile, "r")) == NULL) pg_log(PG_FATAL, "The pg_upgrade_support module must be created and installed in the %s cluster.\n", CLUSTER_NAME(cluster)); fclose(lib_test); }
void start_postmaster(ClusterInfo *cluster, bool quiet) { char cmd[MAXPGPATH]; const char *bindir; const char *datadir; unsigned short port; bool exit_hook_registered = false; bindir = cluster->bindir; datadir = cluster->pgdata; port = cluster->port; if (!exit_hook_registered) { #ifdef HAVE_ATEXIT atexit(stop_postmaster_atexit); #else on_exit(stop_postmaster_on_exit); #endif exit_hook_registered = true; } /* * On Win32, we can't send both pg_upgrade output and pg_ctl output to the * same file because we get the error: "The process cannot access the file * because it is being used by another process." so we have to send all * other output to 'nul'. * * Using autovacuum=off disables cleanup vacuum and analyze, but freeze * vacuums can still happen, so we set autovacuum_freeze_max_age to its * maximum. We assume all datfrozenxid and relfrozen values are less than * a gap of 2000000000 from the current xid counter, so autovacuum will * not touch them. */ snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" " "-o \"-p %d -c autovacuum=off " "-c autovacuum_freeze_max_age=2000000000\" " "start >> \"%s\" 2>&1" SYSTEMQUOTE, bindir, #ifndef WIN32 log_opts.filename, datadir, port, log_opts.filename); #else DEVNULL, datadir, port, DEVNULL); #endif exec_prog(true, "%s", cmd); /* wait for the server to start properly */ if (test_server_conn(cluster, POSTMASTER_UPTIME) == false) pg_log(PG_FATAL, " Unable to start %s postmaster with the command: %s\nPerhaps pg_hba.conf was not set to \"trust\".", CLUSTER_NAME(cluster), cmd); if ((os_info.postmasterPID = get_postmaster_pid(datadir)) == 0) pg_log(PG_FATAL, " Unable to get postmaster pid\n"); os_info.running_cluster = cluster; }
/* * adjust_data_dir * * If a configuration-only directory was specified, find the real data dir * by quering the running server. This has limited checking because we * can't check for a running server because we can't find postmaster.pid. */ void adjust_data_dir(ClusterInfo *cluster) { char filename[MAXPGPATH]; char cmd[MAXPGPATH], cmd_output[MAX_STRING]; FILE *fp, *output; /* If there is no postgresql.conf, it can't be a config-only dir */ snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig); if ((fp = fopen(filename, "r")) == NULL) return; fclose(fp); /* If PG_VERSION exists, it can't be a config-only dir */ snprintf(filename, sizeof(filename), "%s/PG_VERSION", cluster->pgconfig); if ((fp = fopen(filename, "r")) != NULL) { fclose(fp); return; } /* Must be a configuration directory, so find the real data directory. */ prep_status("Finding the real data directory for the %s cluster", CLUSTER_NAME(cluster)); /* * We don't have a data directory yet, so we can't check the PG version, * so this might fail --- only works for PG 9.2+. If this fails, * pg_upgrade will fail anyway because the data files will not be found. */ snprintf(cmd, sizeof(cmd), "\"%s/postmaster\" -D \"%s\" -C data_directory", cluster->bindir, cluster->pgconfig); if ((output = popen(cmd, "r")) == NULL || fgets(cmd_output, sizeof(cmd_output), output) == NULL) pg_fatal("Could not get data directory using %s: %s\n", cmd, getErrorText(errno)); pclose(output); /* Remove trailing newline */ if (strchr(cmd_output, '\n') != NULL) *strchr(cmd_output, '\n') = '\0'; cluster->pgdata = pg_strdup(cmd_output); check_ok(); }
/* * get_db_and_rel_infos() * * higher level routine to generate dbinfos for the database running * on the given "port". Assumes that server is already running. */ void get_db_and_rel_infos(ClusterInfo *cluster) { int dbnum; if (cluster->dbarr.dbs != NULL) free_db_and_rel_infos(&cluster->dbarr); get_db_infos(cluster); for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++) get_rel_infos(cluster, &cluster->dbarr.dbs[dbnum]); pg_log(PG_VERBOSE, "\n%s databases:\n", CLUSTER_NAME(cluster)); if (log_opts.verbose) print_db_infos(&cluster->dbarr); }
void start_postmaster(ClusterInfo *cluster, bool quiet) { char cmd[MAXPGPATH]; const char *bindir; const char *datadir; unsigned short port; bindir = cluster->bindir; datadir = cluster->pgdata; port = cluster->port; /* * On Win32, we can't send both pg_upgrade output and pg_ctl output to the * same file because we get the error: "The process cannot access the file * because it is being used by another process." so we have to send all * other output to 'nul'. */ snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "\"%s/pg_ctl\" -l \"%s\" -D \"%s\" " "-o \"-p %d -c autovacuum=off " "-c autovacuum_freeze_max_age=2000000000\" " "start >> \"%s\" 2>&1" SYSTEMQUOTE, bindir, #ifndef WIN32 log_opts.filename, datadir, port, log_opts.filename); #else DEVNULL, datadir, port, DEVNULL); #endif exec_prog(true, "%s", cmd); /* wait for the server to start properly */ if (test_server_conn(cluster, POSTMASTER_UPTIME) == false) pg_log(PG_FATAL, " Unable to start %s postmaster with the command: %s\nPerhaps pg_hba.conf was not set to \"trust\".", CLUSTER_NAME(cluster), cmd); if ((os_info.postmasterPID = get_postmaster_pid(datadir)) == 0) pg_log(PG_FATAL, " Unable to get postmaster pid\n"); os_info.running_cluster = cluster; }
/* * check_for_prepared_transactions() * * Make sure there are no prepared transactions because the storage format * might have changed. */ static void check_for_prepared_transactions(ClusterInfo *cluster) { PGresult *res; PGconn *conn = connectToServer(cluster, "template1"); prep_status("Checking for prepared transactions"); res = executeQueryOrDie(conn, "SELECT * " "FROM pg_catalog.pg_prepared_xacts"); if (PQntuples(res) != 0) pg_fatal("The %s cluster contains prepared transactions\n", CLUSTER_NAME(cluster)); PQclear(res); PQfinish(conn); check_ok(); }
bool start_postmaster(ClusterInfo *cluster, bool throw_error) { char cmd[MAXPGPATH * 4 + 1000]; PGconn *conn; bool pg_ctl_return = false; char socket_string[MAXPGPATH + 200]; static bool exit_hook_registered = false; if (!exit_hook_registered) { atexit(stop_postmaster_atexit); exit_hook_registered = true; } socket_string[0] = '\0'; #ifdef HAVE_UNIX_SOCKETS /* prevent TCP/IP connections, restrict socket access */ strcat(socket_string, " -c listen_addresses='' -c unix_socket_permissions=0700"); /* Have a sockdir? Tell the postmaster. */ if (cluster->sockdir) snprintf(socket_string + strlen(socket_string), sizeof(socket_string) - strlen(socket_string), " -c %s='%s'", (GET_MAJOR_VERSION(cluster->major_version) < 903) ? "unix_socket_directory" : "unix_socket_directories", cluster->sockdir); #endif /* * Since PG 9.1, we have used -b to disable autovacuum. For earlier * releases, setting autovacuum=off disables cleanup vacuum and analyze, * but freeze vacuums can still happen, so we set * autovacuum_freeze_max_age to its maximum. * (autovacuum_multixact_freeze_max_age was introduced after 9.1, so there * is no need to set that.) We assume all datfrozenxid and relfrozenxid * values are less than a gap of 2000000000 from the current xid counter, * so autovacuum will not touch them. * * Turn off durability requirements to improve object creation speed, and * we only modify the new cluster, so only use it there. If there is a * crash, the new cluster has to be recreated anyway. fsync=off is a big * win on ext4. */ snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d%s%s %s%s\" start", cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, (cluster->controldata.cat_ver >= BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? " -b" : " -c autovacuum=off -c autovacuum_freeze_max_age=2000000000", (cluster == &new_cluster) ? " -c synchronous_commit=off -c fsync=off -c full_page_writes=off" : "", cluster->pgopts ? cluster->pgopts : "", socket_string); /* * Don't throw an error right away, let connecting throw the error because * it might supply a reason for the failure. */ pg_ctl_return = exec_prog(SERVER_START_LOG_FILE, /* pass both file names if they differ */ (strcmp(SERVER_LOG_FILE, SERVER_START_LOG_FILE) != 0) ? SERVER_LOG_FILE : NULL, false, "%s", cmd); /* Did it fail and we are just testing if the server could be started? */ if (!pg_ctl_return && !throw_error) return false; /* * We set this here to make sure atexit() shuts down the server, but only * if we started the server successfully. We do it before checking for * connectivity in case the server started but there is a connectivity * failure. If pg_ctl did not return success, we will exit below. * * Pre-9.1 servers do not have PQping(), so we could be leaving the server * running if authentication was misconfigured, so someday we might went * to be more aggressive about doing server shutdowns even if pg_ctl * fails, but now (2013-08-14) it seems prudent to be cautious. We don't * want to shutdown a server that might have been accidentally started * during the upgrade. */ if (pg_ctl_return) os_info.running_cluster = cluster; /* * pg_ctl -w might have failed because the server couldn't be started, or * there might have been a connection problem in _checking_ if the server * has started. Therefore, even if pg_ctl failed, we continue and test * for connectivity in case we get a connection reason for the failure. */ if ((conn = get_db_conn(cluster, "template1")) == NULL || PQstatus(conn) != CONNECTION_OK) { pg_log(PG_REPORT, "\nconnection to database failed: %s\n", PQerrorMessage(conn)); if (conn) PQfinish(conn); pg_fatal("could not connect to %s postmaster started with the command:\n" "%s\n", CLUSTER_NAME(cluster), cmd); } PQfinish(conn); /* * If pg_ctl failed, and the connection didn't fail, and throw_error is * enabled, fail now. This could happen if the server was already * running. */ if (!pg_ctl_return) pg_fatal("pg_ctl failed to start the %s server, or connection failed\n", CLUSTER_NAME(cluster)); return true; }
void start_postmaster(ClusterInfo *cluster) { char cmd[MAXPGPATH]; PGconn *conn; bool exit_hook_registered = false; int pg_ctl_return = 0; #ifndef WIN32 char *output_filename = log_opts.filename; #else /* * On Win32, we can't send both pg_upgrade output and pg_ctl output to the * same file because we get the error: "The process cannot access the file * because it is being used by another process." so we have to send all * other output to 'nul'. */ char *output_filename = DEVNULL; #endif if (!exit_hook_registered) { #ifdef HAVE_ATEXIT atexit(stop_postmaster_atexit); #else on_exit(stop_postmaster_on_exit); #endif exit_hook_registered = true; } /* * Using autovacuum=off disables cleanup vacuum and analyze, but freeze * vacuums can still happen, so we set autovacuum_freeze_max_age to its * maximum. We assume all datfrozenxid and relfrozen values are less than * a gap of 2000000000 from the current xid counter, so autovacuum will * not touch them. */ snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" " "-o \"-p %d %s\" start >> \"%s\" 2>&1" SYSTEMQUOTE, cluster->bindir, output_filename, cluster->pgdata, cluster->port, (cluster->controldata.cat_ver >= BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? "-b" : "-c autovacuum=off -c autovacuum_freeze_max_age=2000000000", output_filename); /* * Don't throw an error right away, let connecting throw the error because * it might supply a reason for the failure. */ pg_ctl_return = exec_prog(false, "%s", cmd); /* Check to see if we can connect to the server; if not, report it. */ if ((conn = get_db_conn(cluster, "template1")) == NULL || PQstatus(conn) != CONNECTION_OK) { pg_log(PG_REPORT, "\nconnection to database failed: %s\n", PQerrorMessage(conn)); if (conn) PQfinish(conn); pg_log(PG_FATAL, "unable to connect to %s postmaster started with the command: %s\n", CLUSTER_NAME(cluster), cmd); } PQfinish(conn); /* If the connection didn't fail, fail now */ if (pg_ctl_return != 0) pg_log(PG_FATAL, "pg_ctl failed to start the %s server\n", CLUSTER_NAME(cluster)); os_info.running_cluster = cluster; }
void start_postmaster(ClusterInfo *cluster) { char cmd[MAXPGPATH * 4 + 1000]; PGconn *conn; bool exit_hook_registered = false; bool pg_ctl_return = false; char socket_string[MAXPGPATH + 200]; if (!exit_hook_registered) { atexit(stop_postmaster_atexit); exit_hook_registered = true; } socket_string[0] = '\0'; #ifdef HAVE_UNIX_SOCKETS /* prevent TCP/IP connections, restrict socket access */ strcat(socket_string, " -c listen_addresses='' -c unix_socket_permissions=0700"); /* Have a sockdir? Tell the postmaster. */ if (cluster->sockdir) snprintf(socket_string + strlen(socket_string), sizeof(socket_string) - strlen(socket_string), " -c %s='%s'", (GET_MAJOR_VERSION(cluster->major_version) < 903) ? "unix_socket_directory" : "unix_socket_directories", cluster->sockdir); #endif /* * Using autovacuum=off disables cleanup vacuum and analyze, but freeze * vacuums can still happen, so we set autovacuum_freeze_max_age to its * maximum. We assume all datfrozenxid and relfrozen values are less than * a gap of 2000000000 from the current xid counter, so autovacuum will * not touch them. */ snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" -w -l \"%s\" -D \"%s\" -o \"-p %d %s %s%s\" start", cluster->bindir, SERVER_LOG_FILE, cluster->pgconfig, cluster->port, (cluster->controldata.cat_ver >= BINARY_UPGRADE_SERVER_FLAG_CAT_VER) ? "-b" : "-c autovacuum=off -c autovacuum_freeze_max_age=2000000000", cluster->pgopts ? cluster->pgopts : "", socket_string); /* * Don't throw an error right away, let connecting throw the error because * it might supply a reason for the failure. */ pg_ctl_return = exec_prog(SERVER_START_LOG_FILE, /* pass both file names if they differ */ (strcmp(SERVER_LOG_FILE, SERVER_START_LOG_FILE) != 0) ? SERVER_LOG_FILE : NULL, false, "%s", cmd); /* Check to see if we can connect to the server; if not, report it. */ if ((conn = get_db_conn(cluster, "template1")) == NULL || PQstatus(conn) != CONNECTION_OK) { pg_log(PG_REPORT, "\nconnection to database failed: %s\n", PQerrorMessage(conn)); if (conn) PQfinish(conn); pg_log(PG_FATAL, "could not connect to %s postmaster started with the command:\n" "%s\n", CLUSTER_NAME(cluster), cmd); } PQfinish(conn); /* If the connection didn't fail, fail now */ if (!pg_ctl_return) pg_log(PG_FATAL, "pg_ctl failed to start the %s server, or connection failed\n", CLUSTER_NAME(cluster)); os_info.running_cluster = cluster; }
/* * get_control_data() * * gets pg_control information in "ctrl". Assumes that bindir and * datadir are valid absolute paths to postgresql bin and pgdata * directories respectively *and* pg_resetxlog is version compatible * with datadir. The main purpose of this function is to get pg_control * data in a version independent manner. * * The approach taken here is to invoke pg_resetxlog with -n option * and then pipe its output. With little string parsing we get the * pg_control data. pg_resetxlog cannot be run while the server is running * so we use pg_controldata; pg_controldata doesn't provide all the fields * we need to actually perform the upgrade, but it provides enough for * check mode. We do not implement pg_resetxlog -n because it is hard to * return valid xid data for a running server. */ void get_control_data(ClusterInfo *cluster, bool live_check) { char cmd[MAXPGPATH]; char bufin[MAX_STRING]; FILE *output; char *p; bool got_xid = false; bool got_oid = false; bool got_nextxlogfile = false; bool got_multi = false; bool got_mxoff = false; bool got_oldestmulti = false; bool got_log_id = false; bool got_log_seg = false; bool got_tli = false; bool got_align = false; bool got_blocksz = false; bool got_largesz = false; bool got_walsz = false; bool got_walseg = false; bool got_ident = false; bool got_index = false; bool got_toast = false; bool got_large_object = false; bool got_date_is_int = false; bool got_float8_pass_by_value = false; bool got_data_checksum_version = false; char *lc_collate = NULL; char *lc_ctype = NULL; char *lc_monetary = NULL; char *lc_numeric = NULL; char *lc_time = NULL; char *lang = NULL; char *language = NULL; char *lc_all = NULL; char *lc_messages = NULL; uint32 logid = 0; uint32 segno = 0; uint32 tli = 0; /* * Because we test the pg_resetxlog output as strings, it has to be in * English. Copied from pg_regress.c. */ if (getenv("LC_COLLATE")) lc_collate = pg_strdup(getenv("LC_COLLATE")); if (getenv("LC_CTYPE")) lc_ctype = pg_strdup(getenv("LC_CTYPE")); if (getenv("LC_MONETARY")) lc_monetary = pg_strdup(getenv("LC_MONETARY")); if (getenv("LC_NUMERIC")) lc_numeric = pg_strdup(getenv("LC_NUMERIC")); if (getenv("LC_TIME")) lc_time = pg_strdup(getenv("LC_TIME")); if (getenv("LANG")) lang = pg_strdup(getenv("LANG")); if (getenv("LANGUAGE")) language = pg_strdup(getenv("LANGUAGE")); if (getenv("LC_ALL")) lc_all = pg_strdup(getenv("LC_ALL")); if (getenv("LC_MESSAGES")) lc_messages = pg_strdup(getenv("LC_MESSAGES")); pg_putenv("LC_COLLATE", NULL); pg_putenv("LC_CTYPE", NULL); pg_putenv("LC_MONETARY", NULL); pg_putenv("LC_NUMERIC", NULL); pg_putenv("LC_TIME", NULL); pg_putenv("LANG", #ifndef WIN32 NULL); #else /* On Windows the default locale cannot be English, so force it */ "en"); #endif pg_putenv("LANGUAGE", NULL); pg_putenv("LC_ALL", NULL); pg_putenv("LC_MESSAGES", "C"); snprintf(cmd, sizeof(cmd), "\"%s/%s \"%s\"", cluster->bindir, live_check ? "pg_controldata\"" : "pg_resetxlog\" -n", cluster->pgdata); fflush(stdout); fflush(stderr); if ((output = popen(cmd, "r")) == NULL) pg_fatal("Could not get control data using %s: %s\n", cmd, getErrorText(errno)); /* Only in <= 9.2 */ if (GET_MAJOR_VERSION(cluster->major_version) <= 902) { cluster->controldata.data_checksum_version = 0; got_data_checksum_version = true; } /* we have the result of cmd in "output". so parse it line by line now */ while (fgets(bufin, sizeof(bufin), output)) { pg_log(PG_VERBOSE, "%s", bufin); if ((p = strstr(bufin, "pg_control version number:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: pg_resetxlog problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.ctrl_ver = str2uint(p); } else if ((p = strstr(bufin, "Catalog version number:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.cat_ver = str2uint(p); } else if ((p = strstr(bufin, "First log segment after reset:")) != NULL) { /* Skip the colon and any whitespace after it */ p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p = strpbrk(p, "01234567890ABCDEF"); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); /* Make sure it looks like a valid WAL file name */ if (strspn(p, "0123456789ABCDEF") != 24) pg_fatal("%d: controldata retrieval problem\n", __LINE__); strlcpy(cluster->controldata.nextxlogfile, p, 25); got_nextxlogfile = true; } else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ logid = str2uint(p); got_log_id = true; } else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ segno = str2uint(p); got_log_seg = true; } else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_tli = str2uint(p); got_tli = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_nxtepoch = str2uint(p); p = strchr(p, '/'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove '/' char */ cluster->controldata.chkpnt_nxtxid = str2uint(p); got_xid = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextOID:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_nxtoid = str2uint(p); got_oid = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextMultiXactId:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_nxtmulti = str2uint(p); got_multi = true; } else if ((p = strstr(bufin, "Latest checkpoint's oldestMultiXid:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_oldstMulti = str2uint(p); got_oldestmulti = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextMultiOffset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.chkpnt_nxtmxoff = str2uint(p); got_mxoff = true; } else if ((p = strstr(bufin, "Maximum data alignment:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.align = str2uint(p); got_align = true; } else if ((p = strstr(bufin, "Database block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.blocksz = str2uint(p); got_blocksz = true; } else if ((p = strstr(bufin, "Blocks per segment of large relation:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.largesz = str2uint(p); got_largesz = true; } else if ((p = strstr(bufin, "WAL block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.walsz = str2uint(p); got_walsz = true; } else if ((p = strstr(bufin, "Bytes per WAL segment:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.walseg = str2uint(p); got_walseg = true; } else if ((p = strstr(bufin, "Maximum length of identifiers:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.ident = str2uint(p); got_ident = true; } else if ((p = strstr(bufin, "Maximum columns in an index:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.index = str2uint(p); got_index = true; } else if ((p = strstr(bufin, "Maximum size of a TOAST chunk:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.toast = str2uint(p); got_toast = true; } else if ((p = strstr(bufin, "Size of a large-object chunk:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.large_object = str2uint(p); got_large_object = true; } else if ((p = strstr(bufin, "Date/time type storage:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ cluster->controldata.date_is_int = strstr(p, "64-bit integers") != NULL; got_date_is_int = true; } else if ((p = strstr(bufin, "Float8 argument passing:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ /* used later for contrib check */ cluster->controldata.float8_pass_by_value = strstr(p, "by value") != NULL; got_float8_pass_by_value = true; } else if ((p = strstr(bufin, "checksum")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_fatal("%d: controldata retrieval problem\n", __LINE__); p++; /* remove ':' char */ /* used later for contrib check */ cluster->controldata.data_checksum_version = str2uint(p); got_data_checksum_version = true; } } if (output) pclose(output); /* * Restore environment variables */ pg_putenv("LC_COLLATE", lc_collate); pg_putenv("LC_CTYPE", lc_ctype); pg_putenv("LC_MONETARY", lc_monetary); pg_putenv("LC_NUMERIC", lc_numeric); pg_putenv("LC_TIME", lc_time); pg_putenv("LANG", lang); pg_putenv("LANGUAGE", language); pg_putenv("LC_ALL", lc_all); pg_putenv("LC_MESSAGES", lc_messages); pg_free(lc_collate); pg_free(lc_ctype); pg_free(lc_monetary); pg_free(lc_numeric); pg_free(lc_time); pg_free(lang); pg_free(language); pg_free(lc_all); pg_free(lc_messages); /* * Before 9.3, pg_resetxlog reported the xlogid and segno of the first log * file after reset as separate lines. Starting with 9.3, it reports the * WAL file name. If the old cluster is older than 9.3, we construct the * WAL file name from the xlogid and segno. */ if (GET_MAJOR_VERSION(cluster->major_version) <= 902) { if (got_log_id && got_log_seg) { snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X", tli, logid, segno); got_nextxlogfile = true; } } /* verify that we got all the mandatory pg_control data */ if (!got_xid || !got_oid || !got_multi || !got_mxoff || (!got_oldestmulti && cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) || (!live_check && !got_nextxlogfile) || !got_tli || !got_align || !got_blocksz || !got_largesz || !got_walsz || !got_walseg || !got_ident || !got_index || !got_toast || (!got_large_object && cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) || !got_date_is_int || !got_float8_pass_by_value || !got_data_checksum_version) { pg_log(PG_REPORT, "The %s cluster lacks some required control information:\n", CLUSTER_NAME(cluster)); if (!got_xid) pg_log(PG_REPORT, " checkpoint next XID\n"); if (!got_oid) pg_log(PG_REPORT, " latest checkpoint next OID\n"); if (!got_multi) pg_log(PG_REPORT, " latest checkpoint next MultiXactId\n"); if (!got_mxoff) pg_log(PG_REPORT, " latest checkpoint next MultiXactOffset\n"); if (!got_oldestmulti && cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) pg_log(PG_REPORT, " latest checkpoint oldest MultiXactId\n"); if (!live_check && !got_nextxlogfile) pg_log(PG_REPORT, " first WAL segment after reset\n"); if (!got_tli) pg_log(PG_REPORT, " latest checkpoint timeline ID\n"); if (!got_align) pg_log(PG_REPORT, " maximum alignment\n"); if (!got_blocksz) pg_log(PG_REPORT, " block size\n"); if (!got_largesz) pg_log(PG_REPORT, " large relation segment size\n"); if (!got_walsz) pg_log(PG_REPORT, " WAL block size\n"); if (!got_walseg) pg_log(PG_REPORT, " WAL segment size\n"); if (!got_ident) pg_log(PG_REPORT, " maximum identifier length\n"); if (!got_index) pg_log(PG_REPORT, " maximum number of indexed columns\n"); if (!got_toast) pg_log(PG_REPORT, " maximum TOAST chunk size\n"); if (!got_large_object && cluster->controldata.ctrl_ver >= LARGE_OBJECT_SIZE_PG_CONTROL_VER) pg_log(PG_REPORT, " large-object chunk size\n"); if (!got_date_is_int) pg_log(PG_REPORT, " dates/times are integers?\n"); if (!got_float8_pass_by_value) pg_log(PG_REPORT, " float8 argument passing method\n"); /* value added in Postgres 9.3 */ if (!got_data_checksum_version) pg_log(PG_REPORT, " data checksum version\n"); pg_fatal("Cannot continue without required control information, terminating\n"); } }
/* * get_control_data() * * gets pg_control information in "ctrl". Assumes that bindir and * datadir are valid absolute paths to postgresql bin and pgdata * directories respectively *and* pg_resetxlog is version compatible * with datadir. The main purpose of this function is to get pg_control * data in a version independent manner. * * The approach taken here is to invoke pg_resetxlog with -n option * and then pipe its output. With little string parsing we get the * pg_control data. pg_resetxlog cannot be run while the server is running * so we use pg_controldata; pg_controldata doesn't provide all the fields * we need to actually perform the upgrade, but it provides enough for * check mode. We do not implement pg_resetxlog -n because it is hard to * return valid xid data for a running server. */ void get_control_data(ClusterInfo *cluster, bool live_check) { char cmd[MAXPGPATH]; char bufin[MAX_STRING]; FILE *output; char *p; bool got_xid = false; bool got_oid = false; bool got_nextxlogfile = false; bool got_multi = false; bool got_mxoff = false; bool got_oldestmulti = false; bool got_log_id = false; bool got_log_seg = false; bool got_tli = false; bool got_align = false; bool got_blocksz = false; bool got_largesz = false; bool got_walsz = false; bool got_walseg = false; bool got_ident = false; bool got_index = false; bool got_toast = false; bool got_date_is_int = false; bool got_float8_pass_by_value = false; char *lc_collate = NULL; char *lc_ctype = NULL; char *lc_monetary = NULL; char *lc_numeric = NULL; char *lc_time = NULL; char *lang = NULL; char *language = NULL; char *lc_all = NULL; char *lc_messages = NULL; uint32 logid = 0; uint32 segno = 0; uint32 tli = 0; /* * Because we test the pg_resetxlog output as strings, it has to be in * English. Copied from pg_regress.c. */ if (getenv("LC_COLLATE")) lc_collate = pg_strdup(getenv("LC_COLLATE")); if (getenv("LC_CTYPE")) lc_ctype = pg_strdup(getenv("LC_CTYPE")); if (getenv("LC_MONETARY")) lc_monetary = pg_strdup(getenv("LC_MONETARY")); if (getenv("LC_NUMERIC")) lc_numeric = pg_strdup(getenv("LC_NUMERIC")); if (getenv("LC_TIME")) lc_time = pg_strdup(getenv("LC_TIME")); if (getenv("LANG")) lang = pg_strdup(getenv("LANG")); if (getenv("LANGUAGE")) language = pg_strdup(getenv("LANGUAGE")); if (getenv("LC_ALL")) lc_all = pg_strdup(getenv("LC_ALL")); if (getenv("LC_MESSAGES")) lc_messages = pg_strdup(getenv("LC_MESSAGES")); pg_putenv("LC_COLLATE", NULL); pg_putenv("LC_CTYPE", NULL); pg_putenv("LC_MONETARY", NULL); pg_putenv("LC_NUMERIC", NULL); pg_putenv("LC_TIME", NULL); pg_putenv("LANG", #ifndef WIN32 NULL); #else /* On Windows the default locale cannot be English, so force it */ "en"); #endif pg_putenv("LANGUAGE", NULL); pg_putenv("LC_ALL", NULL); pg_putenv("LC_MESSAGES", "C"); snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "\"%s/%s \"%s\"" SYSTEMQUOTE, cluster->bindir, live_check ? "pg_controldata\"" : "pg_resetxlog\" -n", cluster->pgdata); fflush(stdout); fflush(stderr); if ((output = popen(cmd, "r")) == NULL) pg_log(PG_FATAL, "Could not get control data using %s: %s\n", cmd, getErrorText(errno)); /* Only pre-8.4 has these so if they are not set below we will check later */ cluster->controldata.lc_collate = NULL; cluster->controldata.lc_ctype = NULL; /* Only in <= 8.3 */ if (GET_MAJOR_VERSION(cluster->major_version) <= 803) { cluster->controldata.float8_pass_by_value = false; got_float8_pass_by_value = true; } /* we have the result of cmd in "output". so parse it line by line now */ while (fgets(bufin, sizeof(bufin), output)) { pg_log(PG_VERBOSE, "%s", bufin); #ifdef WIN32 /* * Due to an installer bug, LANG=C doesn't work for PG 8.3.3, but does * work 8.2.6 and 8.3.7, so check for non-ASCII output and suggest a * minor upgrade. */ if (GET_MAJOR_VERSION(cluster->major_version) <= 803) { for (p = bufin; *p; p++) if (!isascii(*p)) pg_log(PG_FATAL, "The 8.3 cluster's pg_controldata is incapable of outputting ASCII, even\n" "with LANG=C. You must upgrade this cluster to a newer version of PostgreSQL\n" "8.3 to fix this bug. PostgreSQL 8.3.7 and later are known to work properly.\n"); } #endif if ((p = strstr(bufin, "pg_control version number:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: pg_resetxlog problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.ctrl_ver = str2uint(p); } else if ((p = strstr(bufin, "Catalog version number:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.cat_ver = str2uint(p); } else if ((p = strstr(bufin, "First log segment after reset:")) != NULL) { /* Skip the colon and any whitespace after it */ p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p = strpbrk(p, "01234567890ABCDEF"); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); /* Make sure it looks like a valid WAL file name */ if (strspn(p, "0123456789ABCDEF") != 24) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); strlcpy(cluster->controldata.nextxlogfile, p, 25); got_nextxlogfile = true; } else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ logid = str2uint(p); got_log_id = true; } else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ segno = str2uint(p); got_log_seg = true; } else if ((p = strstr(bufin, "Latest checkpoint's TimeLineID:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_tli = str2uint(p); got_tli = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextXID:")) != NULL) { char *op = strchr(p, '/'); if (op == NULL) op = strchr(p, ':'); if (op == NULL || strlen(op) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); op++; /* removing ':' char */ cluster->controldata.chkpnt_nxtxid = str2uint(op); got_xid = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextOID:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_nxtoid = str2uint(p); got_oid = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextMultiXactId:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_nxtmulti = str2uint(p); got_multi = true; } else if ((p = strstr(bufin, "Latest checkpoint's oldestMultiXid:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_oldstMulti = str2uint(p); got_oldestmulti = true; } else if ((p = strstr(bufin, "Latest checkpoint's NextMultiOffset:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_nxtmxoff = str2uint(p); got_mxoff = true; } else if ((p = strstr(bufin, "Maximum data alignment:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.align = str2uint(p); got_align = true; } else if ((p = strstr(bufin, "Database block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.blocksz = str2uint(p); got_blocksz = true; } else if ((p = strstr(bufin, "Blocks per segment of large relation:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.largesz = str2uint(p); got_largesz = true; } else if ((p = strstr(bufin, "WAL block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.walsz = str2uint(p); got_walsz = true; } else if ((p = strstr(bufin, "Bytes per WAL segment:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.walseg = str2uint(p); got_walseg = true; } else if ((p = strstr(bufin, "Maximum length of identifiers:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.ident = str2uint(p); got_ident = true; } else if ((p = strstr(bufin, "Maximum columns in an index:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.index = str2uint(p); got_index = true; } else if ((p = strstr(bufin, "Maximum size of a TOAST chunk:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.toast = str2uint(p); got_toast = true; } else if ((p = strstr(bufin, "Date/time type storage:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.date_is_int = strstr(p, "64-bit integers") != NULL; got_date_is_int = true; } else if ((p = strstr(bufin, "Float8 argument passing:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ /* used later for contrib check */ cluster->controldata.float8_pass_by_value = strstr(p, "by value") != NULL; got_float8_pass_by_value = true; } /* In pre-8.4 only */ else if ((p = strstr(bufin, "LC_COLLATE:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ /* skip leading spaces and remove trailing newline */ p += strspn(p, " "); if (strlen(p) > 0 && *(p + strlen(p) - 1) == '\n') *(p + strlen(p) - 1) = '\0'; cluster->controldata.lc_collate = pg_strdup(p); } /* In pre-8.4 only */ else if ((p = strstr(bufin, "LC_CTYPE:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ /* skip leading spaces and remove trailing newline */ p += strspn(p, " "); if (strlen(p) > 0 && *(p + strlen(p) - 1) == '\n') *(p + strlen(p) - 1) = '\0'; cluster->controldata.lc_ctype = pg_strdup(p); } } if (output) pclose(output); /* * Restore environment variables */ pg_putenv("LC_COLLATE", lc_collate); pg_putenv("LC_CTYPE", lc_ctype); pg_putenv("LC_MONETARY", lc_monetary); pg_putenv("LC_NUMERIC", lc_numeric); pg_putenv("LC_TIME", lc_time); pg_putenv("LANG", lang); pg_putenv("LANGUAGE", language); pg_putenv("LC_ALL", lc_all); pg_putenv("LC_MESSAGES", lc_messages); pg_free(lc_collate); pg_free(lc_ctype); pg_free(lc_monetary); pg_free(lc_numeric); pg_free(lc_time); pg_free(lang); pg_free(language); pg_free(lc_all); pg_free(lc_messages); /* * Before 9.3, pg_resetxlog reported the xlogid and segno of the first * log file after reset as separate lines. Starting with 9.3, it reports * the WAL file name. If the old cluster is older than 9.3, we construct * the WAL file name from the xlogid and segno. */ if (GET_MAJOR_VERSION(cluster->major_version) <= 902) { if (got_log_id && got_log_seg) { snprintf(cluster->controldata.nextxlogfile, 25, "%08X%08X%08X", tli, logid, segno); got_nextxlogfile = true; } } /* verify that we got all the mandatory pg_control data */ if (!got_xid || !got_oid || !got_multi || !got_mxoff || (!got_oldestmulti && cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) || (!live_check && !got_nextxlogfile) || !got_tli || !got_align || !got_blocksz || !got_largesz || !got_walsz || !got_walseg || !got_ident || !got_index || !got_toast || !got_date_is_int || !got_float8_pass_by_value) { pg_log(PG_REPORT, "The %s cluster lacks some required control information:\n", CLUSTER_NAME(cluster)); if (!got_xid) pg_log(PG_REPORT, " checkpoint next XID\n"); if (!got_oid) pg_log(PG_REPORT, " latest checkpoint next OID\n"); if (!got_multi) pg_log(PG_REPORT, " latest checkpoint next MultiXactId\n"); if (!got_mxoff) pg_log(PG_REPORT, " latest checkpoint next MultiXactOffset\n"); if (!got_oldestmulti && cluster->controldata.cat_ver >= MULTIXACT_FORMATCHANGE_CAT_VER) pg_log(PG_REPORT, " latest checkpoint oldest MultiXactId\n"); if (!live_check && !got_nextxlogfile) pg_log(PG_REPORT, " first WAL segment after reset\n"); if (!got_tli) pg_log(PG_REPORT, " latest checkpoint timeline ID\n"); if (!got_align) pg_log(PG_REPORT, " maximum alignment\n"); if (!got_blocksz) pg_log(PG_REPORT, " block size\n"); if (!got_largesz) pg_log(PG_REPORT, " large relation segment size\n"); if (!got_walsz) pg_log(PG_REPORT, " WAL block size\n"); if (!got_walseg) pg_log(PG_REPORT, " WAL segment size\n"); if (!got_ident) pg_log(PG_REPORT, " maximum identifier length\n"); if (!got_index) pg_log(PG_REPORT, " maximum number of indexed columns\n"); if (!got_toast) pg_log(PG_REPORT, " maximum TOAST chunk size\n"); if (!got_date_is_int) pg_log(PG_REPORT, " dates/times are integers?\n"); /* value added in Postgres 8.4 */ if (!got_float8_pass_by_value) pg_log(PG_REPORT, " float8 argument passing method\n"); pg_log(PG_FATAL, "Cannot continue without required control information, terminating\n"); } }