bool start_postmaster(ClusterInfo *cluster, bool throw_error) { 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 /* * 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; }
/* * get_tablespace_paths() * * Scans pg_tablespace and returns a malloc'ed array of all tablespace * paths. Its the caller's responsibility to free the array. */ static void get_tablespace_paths(void) { PGconn *conn = connectToServer(&old_cluster, "template1"); PGresult *res; int tblnum; int i_spclocation; char query[QUERY_ALLOC]; snprintf(query, sizeof(query), "SELECT %s " "FROM pg_catalog.pg_tablespace " "WHERE spcname != 'pg_default' AND " " spcname != 'pg_global'", /* 9.2 removed the spclocation column */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 901) ? "spclocation" : "pg_catalog.pg_tablespace_location(oid) AS spclocation"); res = executeQueryOrDie(conn, "%s", query); if ((os_info.num_old_tablespaces = PQntuples(res)) != 0) os_info.old_tablespaces = (char **) pg_malloc( os_info.num_old_tablespaces * sizeof(char *)); else os_info.old_tablespaces = NULL; i_spclocation = PQfnumber(res, "spclocation"); for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) { struct stat statBuf; os_info.old_tablespaces[tblnum] = pg_strdup( PQgetvalue(res, tblnum, i_spclocation)); /* * Check that the tablespace path exists and is a directory. * Effectively, this is checking only for tables/indexes in * non-existent tablespace directories. Databases located in * non-existent tablespaces already throw a backend error. * Non-existent tablespace directories can occur when a data directory * that contains user tablespaces is moved as part of pg_upgrade * preparation and the symbolic links are not updated. */ if (stat(os_info.old_tablespaces[tblnum], &statBuf) != 0) { if (errno == ENOENT) report_status(PG_FATAL, "tablespace directory \"%s\" does not exist\n", os_info.old_tablespaces[tblnum]); else report_status(PG_FATAL, "could not stat tablespace directory \"%s\": %s\n", os_info.old_tablespaces[tblnum], strerror(errno)); } if (!S_ISDIR(statBuf.st_mode)) report_status(PG_FATAL, "tablespace path \"%s\" is not a directory\n", os_info.old_tablespaces[tblnum]); } PQclear(res); PQfinish(conn); return; }
/* * get_rel_infos() * * gets the relinfos for all the user tables of the database referred * by "db". * * NOTE: we assume that relations/entities with oids greater than * FirstNormalObjectId belongs to the user */ static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) { PGconn *conn = connectToServer(cluster, dbinfo->db_name); PGresult *res; RelInfo *relinfos; int ntups; int relnum; int num_rels = 0; char *nspname = NULL; char *relname = NULL; char *tablespace = NULL; int i_spclocation, i_nspname, i_relname, i_oid, i_relfilenode, i_reltablespace; char query[QUERY_ALLOC]; char *last_namespace = NULL, *last_tablespace = NULL; /* * pg_largeobject contains user data that does not appear in pg_dump * --schema-only output, so we have to copy that system table heap and * index. We could grab the pg_largeobject oids from template1, but it is * easy to treat it as a normal table. Order by oid so we can join old/new * structures efficiently. */ snprintf(query, sizeof(query), /* get regular heap */ "WITH regular_heap (reloid) AS ( " " SELECT c.oid " " FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " LEFT OUTER JOIN pg_catalog.pg_index i " " ON c.oid = i.indexrelid " " WHERE relkind IN ('r', 'm', 'i', 'S') AND " /* * pg_dump only dumps valid indexes; testing indisready is necessary in * 9.2, and harmless in earlier/later versions. */ " i.indisvalid IS DISTINCT FROM false AND " " i.indisready IS DISTINCT FROM false AND " /* exclude possible orphaned temp tables */ " ((n.nspname !~ '^pg_temp_' AND " " n.nspname !~ '^pg_toast_temp_' AND " /* skip pg_toast because toast index have relkind == 'i', not 't' */ " n.nspname NOT IN ('pg_catalog', 'information_schema', " " 'binary_upgrade', 'pg_toast') AND " " c.oid >= %u) OR " " (n.nspname = 'pg_catalog' AND " " relname IN ('pg_largeobject', 'pg_largeobject_loid_pn_index'%s) ))), " /* * We have to gather the TOAST tables in later steps because we * can't schema-qualify TOAST tables. */ /* get TOAST heap */ " toast_heap (reloid) AS ( " " SELECT reltoastrelid " " FROM regular_heap JOIN pg_catalog.pg_class c " " ON regular_heap.reloid = c.oid " " AND c.reltoastrelid != %u), " /* get indexes on regular and TOAST heap */ " all_index (reloid) AS ( " " SELECT indexrelid " " FROM pg_index " " WHERE indisvalid " " AND indrelid IN (SELECT reltoastrelid " " FROM (SELECT reloid FROM regular_heap " " UNION ALL " " SELECT reloid FROM toast_heap) all_heap " " JOIN pg_catalog.pg_class c " " ON all_heap.reloid = c.oid " " AND c.reltoastrelid != %u)) " /* get all rels */ "SELECT c.oid, n.nspname, c.relname, " " c.relfilenode, c.reltablespace, %s " "FROM (SELECT reloid FROM regular_heap " " UNION ALL " " SELECT reloid FROM toast_heap " " UNION ALL " " SELECT reloid FROM all_index) all_rels " " JOIN pg_catalog.pg_class c " " ON all_rels.reloid = c.oid " " JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " LEFT OUTER JOIN pg_catalog.pg_tablespace t " " ON c.reltablespace = t.oid " /* we preserve pg_class.oid so we sort by it to match old/new */ "ORDER BY 1;", FirstNormalObjectId, /* does pg_largeobject_metadata need to be migrated? */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) ? "" : ", 'pg_largeobject_metadata', 'pg_largeobject_metadata_oid_index'", InvalidOid, InvalidOid, /* 9.2 removed the spclocation column */ (GET_MAJOR_VERSION(cluster->major_version) <= 901) ? "t.spclocation" : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation"); res = executeQueryOrDie(conn, "%s", query); ntups = PQntuples(res); relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups); i_oid = PQfnumber(res, "oid"); i_nspname = PQfnumber(res, "nspname"); i_relname = PQfnumber(res, "relname"); i_relfilenode = PQfnumber(res, "relfilenode"); i_reltablespace = PQfnumber(res, "reltablespace"); i_spclocation = PQfnumber(res, "spclocation"); for (relnum = 0; relnum < ntups; relnum++) { RelInfo *curr = &relinfos[num_rels++]; curr->reloid = atooid(PQgetvalue(res, relnum, i_oid)); nspname = PQgetvalue(res, relnum, i_nspname); curr->nsp_alloc = false; /* * Many of the namespace and tablespace strings are identical, so we * try to reuse the allocated string pointers where possible to reduce * memory consumption. */ /* Can we reuse the previous string allocation? */ if (last_namespace && strcmp(nspname, last_namespace) == 0) curr->nspname = last_namespace; else { last_namespace = curr->nspname = pg_strdup(nspname); curr->nsp_alloc = true; } relname = PQgetvalue(res, relnum, i_relname); curr->relname = pg_strdup(relname); curr->relfilenode = atooid(PQgetvalue(res, relnum, i_relfilenode)); curr->tblsp_alloc = false; /* Is the tablespace oid non-zero? */ if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0) { /* * The tablespace location might be "", meaning the cluster * default location, i.e. pg_default or pg_global. */ tablespace = PQgetvalue(res, relnum, i_spclocation); /* Can we reuse the previous string allocation? */ if (last_tablespace && strcmp(tablespace, last_tablespace) == 0) curr->tablespace = last_tablespace; else { last_tablespace = curr->tablespace = pg_strdup(tablespace); curr->tblsp_alloc = true; } } else /* A zero reltablespace oid indicates the database tablespace. */ curr->tablespace = dbinfo->db_tablespace; } PQclear(res); PQfinish(conn); dbinfo->rel_arr.rels = relinfos; dbinfo->rel_arr.nrels = num_rels; }
/* * gen_db_file_maps() * * generates database mappings for "old_db" and "new_db". Returns a malloc'ed * array of mappings. nmaps is a return parameter which refers to the number * mappings. */ FileNameMap * gen_db_file_maps(DbInfo *old_db, DbInfo *new_db, int *nmaps, const char *old_pgdata, const char *new_pgdata) { FileNameMap *maps; int old_relnum, new_relnum; int num_maps = 0; maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) * old_db->rel_arr.nrels); /* * The old database shouldn't have more relations than the new one. * We force the new cluster to have a TOAST table if the old table * had one. */ if (old_db->rel_arr.nrels > new_db->rel_arr.nrels) pg_fatal("old and new databases \"%s\" have a mismatched number of relations\n", old_db->db_name); /* Drive the loop using new_relnum, which might be higher. */ for (old_relnum = new_relnum = 0; new_relnum < new_db->rel_arr.nrels; new_relnum++) { RelInfo *old_rel; RelInfo *new_rel = &new_db->rel_arr.rels[new_relnum]; /* * It is possible that the new cluster has a TOAST table for a table * that didn't need one in the old cluster, e.g. 9.0 to 9.1 changed the * NUMERIC length computation. Therefore, if we have a TOAST table * in the new cluster that doesn't match, skip over it and continue * processing. It is possible this TOAST table used an OID that was * reserved in the old cluster, but we have no way of testing that, * and we would have already gotten an error at the new cluster schema * creation stage. Fortunately, since we only restore the OID counter * after schema restore, and restore in OID order via pg_dump, a * conflict would only happen if the new TOAST table had a very low * OID. However, TOAST tables created long after initial table * creation can have any OID, particularly after OID wraparound. */ if (old_relnum == old_db->rel_arr.nrels) { if (strcmp(new_rel->nspname, "pg_toast") == 0) continue; else pg_fatal("Extra non-TOAST relation found in database \"%s\": new OID %d\n", old_db->db_name, new_rel->reloid); } old_rel = &old_db->rel_arr.rels[old_relnum]; if (old_rel->reloid != new_rel->reloid) { if (strcmp(new_rel->nspname, "pg_toast") == 0) continue; else pg_fatal("Mismatch of relation OID in database \"%s\": old OID %d, new OID %d\n", old_db->db_name, old_rel->reloid, new_rel->reloid); } /* * TOAST table names initially match the heap pg_class oid. In * pre-8.4, TOAST table names change during CLUSTER; in pre-9.0, TOAST * table names change during ALTER TABLE ALTER COLUMN SET TYPE. In >= * 9.0, TOAST relation names always use heap table oids, hence we * cannot check relation names when upgrading from pre-9.0. Clusters * upgraded to 9.0 will get matching TOAST names. If index names don't * match primary key constraint names, this will fail because pg_dump * dumps constraint names and pg_upgrade checks index names. */ if (strcmp(old_rel->nspname, new_rel->nspname) != 0 || ((GET_MAJOR_VERSION(old_cluster.major_version) >= 900 || strcmp(old_rel->nspname, "pg_toast") != 0) && strcmp(old_rel->relname, new_rel->relname) != 0)) pg_fatal("Mismatch of relation names in database \"%s\": " "old name \"%s.%s\", new name \"%s.%s\"\n", old_db->db_name, old_rel->nspname, old_rel->relname, new_rel->nspname, new_rel->relname); create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db, old_rel, new_rel, maps + num_maps); num_maps++; old_relnum++; } /* Did we fail to exhaust the old array? */ if (old_relnum != old_db->rel_arr.nrels) pg_fatal("old and new databases \"%s\" have a mismatched number of relations\n", old_db->db_name); *nmaps = num_maps; return maps; }
/* * get_sock_dir * * Identify the socket directory to use for this cluster. If we're doing * a live check (old cluster only), we need to find out where the postmaster * is listening. Otherwise, we're going to put the socket into the current * directory. */ void get_sock_dir(ClusterInfo *cluster, bool live_check) { #ifdef HAVE_UNIX_SOCKETS /* * sockdir and port were added to postmaster.pid in PG 9.1. Pre-9.1 cannot * process pg_ctl -w for sockets in non-default locations. */ if (GET_MAJOR_VERSION(cluster->major_version) >= 901) { if (!live_check) { /* Use the current directory for the socket */ cluster->sockdir = pg_malloc(MAXPGPATH); if (!getcwd(cluster->sockdir, MAXPGPATH)) pg_fatal("cannot find current directory\n"); } else { /* * If we are doing a live check, we will use the old cluster's * Unix domain socket directory so we can connect to the live * server. */ unsigned short orig_port = cluster->port; char filename[MAXPGPATH], line[MAXPGPATH]; FILE *fp; int lineno; snprintf(filename, sizeof(filename), "%s/postmaster.pid", cluster->pgdata); if ((fp = fopen(filename, "r")) == NULL) pg_fatal("Cannot open file %s: %m\n", filename); for (lineno = 1; lineno <= Max(LOCK_FILE_LINE_PORT, LOCK_FILE_LINE_SOCKET_DIR); lineno++) { if (fgets(line, sizeof(line), fp) == NULL) pg_fatal("Cannot read line %d from %s: %m\n", lineno, filename); /* potentially overwrite user-supplied value */ if (lineno == LOCK_FILE_LINE_PORT) sscanf(line, "%hu", &old_cluster.port); if (lineno == LOCK_FILE_LINE_SOCKET_DIR) { cluster->sockdir = pg_strdup(line); /* strip off newline */ if (strchr(cluster->sockdir, '\n') != NULL) *strchr(cluster->sockdir, '\n') = '\0'; } } fclose(fp); /* warn of port number correction */ if (orig_port != DEF_PGUPORT && old_cluster.port != orig_port) pg_log(PG_WARNING, "User-supplied old port number %hu corrected to %hu\n", orig_port, cluster->port); } } else /* * Can't get sockdir and pg_ctl -w can't use a non-default, use * default */ cluster->sockdir = NULL; #else /* !HAVE_UNIX_SOCKETS */ cluster->sockdir = NULL; #endif }
/* * get_loadable_libraries() * * Fetch the names of all old libraries containing C-language functions. * We will later check that they all exist in the new installation. */ void get_loadable_libraries(void) { PGresult **ress; int totaltups; int dbnum; bool found_public_plpython_handler = false; ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *)); totaltups = 0; /* Fetch all library names, removing duplicates within each DB */ for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { DbInfo *active_db = &old_cluster.dbarr.dbs[dbnum]; PGconn *conn = connectToServer(&old_cluster, active_db->db_name); /* * Fetch all libraries referenced in this DB. We can't exclude the * "pg_catalog" schema because, while such functions are not * explicitly dumped by pg_dump, they do reference implicit objects * that pg_dump does dump, e.g. CREATE LANGUAGE plperl. */ ress[dbnum] = executeQueryOrDie(conn, "SELECT DISTINCT probin " "FROM pg_catalog.pg_proc " "WHERE prolang = 13 /* C */ AND " "probin IS NOT NULL AND " "oid >= %u;", FirstNormalObjectId); totaltups += PQntuples(ress[dbnum]); /* * Systems that install plpython before 8.1 have * plpython_call_handler() defined in the "public" schema, causing * pg_dumpall to dump it. However that function still references * "plpython" (no "2"), so it throws an error on restore. This code * checks for the problem function, reports affected databases to the * user and explains how to remove them. 8.1 git commit: * e0dedd0559f005d60c69c9772163e69c204bac69 * http://archives.postgresql.org/pgsql-hackers/2012-03/msg01101.php * http://archives.postgresql.org/pgsql-bugs/2012-05/msg00206.php */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 901) { PGresult *res; res = executeQueryOrDie(conn, "SELECT 1 " "FROM pg_catalog.pg_proc JOIN pg_namespace " " ON pronamespace = pg_namespace.oid " "WHERE proname = 'plpython_call_handler' AND " "nspname = 'public' AND " "prolang = 13 /* C */ AND " "probin = '$libdir/plpython' AND " "pg_proc.oid >= %u;", FirstNormalObjectId); if (PQntuples(res) > 0) { if (!found_public_plpython_handler) { pg_log(PG_WARNING, "\nThe old cluster has a \"plpython_call_handler\" function defined\n" "in the \"public\" schema which is a duplicate of the one defined\n" "in the \"pg_catalog\" schema. You can confirm this by executing\n" "in psql:\n" "\n" " \\df *.plpython_call_handler\n" "\n" "The \"public\" schema version of this function was created by a\n" "pre-8.1 install of plpython, and must be removed for pg_upgrade\n" "to complete because it references a now-obsolete \"plpython\"\n" "shared object file. You can remove the \"public\" schema version\n" "of this function by running the following command:\n" "\n" " DROP FUNCTION public.plpython_call_handler()\n" "\n" "in each affected database:\n" "\n"); } pg_log(PG_WARNING, " %s\n", active_db->db_name); found_public_plpython_handler = true; } PQclear(res); } PQfinish(conn); } if (found_public_plpython_handler) pg_log(PG_FATAL, "Remove the problem functions from the old cluster to continue.\n"); totaltups++; /* reserve for pg_upgrade_support */ /* Allocate what's certainly enough space */ os_info.libraries = (char **) pg_malloc(totaltups * sizeof(char *)); /* * Now remove duplicates across DBs. This is pretty inefficient code, but * there probably aren't enough entries to matter. */ totaltups = 0; os_info.libraries[totaltups++] = pg_strdup(PG_UPGRADE_SUPPORT); for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { PGresult *res = ress[dbnum]; int ntups; int rowno; ntups = PQntuples(res); for (rowno = 0; rowno < ntups; rowno++) { char *lib = PQgetvalue(res, rowno, 0); bool dup = false; int n; for (n = 0; n < totaltups; n++) { if (strcmp(lib, os_info.libraries[n]) == 0) { dup = true; break; } } if (!dup) os_info.libraries[totaltups++] = pg_strdup(lib); } PQclear(res); } os_info.num_libraries = totaltups; pg_free(ress); }
static void create_new_objects(void) { int dbnum; prep_status("Adding support functions to new cluster"); /* * Technically, we only need to install these support functions in new * databases that also exist in the old cluster, but for completeness we * process all new databases. */ for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++) { DbInfo *new_db = &new_cluster.dbarr.dbs[dbnum]; /* skip db we already installed */ if (strcmp(new_db->db_name, "template1") != 0) install_support_functions_in_new_db(new_db->db_name); } check_ok(); prep_status("Restoring database schemas in the new cluster\n"); for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); /* * pg_dump only produces its output at the end, so there is little * parallelism if using the pipe. */ parallel_exec_prog(log_file_name, NULL, "\"%s/pg_restore\" %s --exit-on-error --verbose --dbname \"%s\" \"%s\"", new_cluster.bindir, cluster_conn_opts(&new_cluster), old_db->db_name, sql_file_name); } /* reap all children */ while (reap_child(true) == true) ; end_progress_output(); check_ok(); /* * We don't have minmxids for databases or relations in pre-9.3 * clusters, so set those after we have restores the schemas. */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 903) set_frozenxids(true); optionally_create_toast_tables(); /* regenerate now that we have objects in the databases */ get_db_and_rel_infos(&new_cluster); uninstall_support_functions_from_new_cluster(); }
/* * gen_db_file_maps() * * generates a database mapping from "old_db" to "new_db". * * Returns a malloc'ed array of mappings. The length of the array * is returned into *nmaps. */ FileNameMap * gen_db_file_maps(DbInfo *old_db, DbInfo *new_db, int *nmaps, const char *old_pgdata, const char *new_pgdata) { FileNameMap *maps; int old_relnum, new_relnum; int num_maps = 0; bool all_matched = true; /* There will certainly not be more mappings than there are old rels */ maps = (FileNameMap *) pg_malloc(sizeof(FileNameMap) * old_db->rel_arr.nrels); /* * Each of the RelInfo arrays should be sorted by OID. Scan through them * and match them up. If we fail to match everything, we'll abort, but * first print as much info as we can about mismatches. */ old_relnum = new_relnum = 0; while (old_relnum < old_db->rel_arr.nrels || new_relnum < new_db->rel_arr.nrels) { RelInfo *old_rel = (old_relnum < old_db->rel_arr.nrels) ? &old_db->rel_arr.rels[old_relnum] : NULL; RelInfo *new_rel = (new_relnum < new_db->rel_arr.nrels) ? &new_db->rel_arr.rels[new_relnum] : NULL; /* handle running off one array before the other */ if (!new_rel) { /* * old_rel is unmatched. This should never happen, because we * force new rels to have TOAST tables if the old one did. */ report_unmatched_relation(old_rel, old_db, false); all_matched = false; old_relnum++; continue; } if (!old_rel) { /* * new_rel is unmatched. This shouldn't really happen either, but * if it's a TOAST table, we can ignore it and continue * processing, assuming that the new server made a TOAST table * that wasn't needed. */ if (strcmp(new_rel->nspname, "pg_toast") != 0) { report_unmatched_relation(new_rel, new_db, true); all_matched = false; } new_relnum++; continue; } /* check for mismatched OID */ if (old_rel->reloid < new_rel->reloid) { /* old_rel is unmatched, see comment above */ report_unmatched_relation(old_rel, old_db, false); all_matched = false; old_relnum++; continue; } else if (old_rel->reloid > new_rel->reloid) { /* new_rel is unmatched, see comment above */ if (strcmp(new_rel->nspname, "pg_toast") != 0) { report_unmatched_relation(new_rel, new_db, true); all_matched = false; } new_relnum++; continue; } /* * Verify that rels of same OID have same name. The namespace name * should always match, but the relname might not match for TOAST * tables (and, therefore, their indexes). * * TOAST table names initially match the heap pg_class oid, but * pre-9.0 they can change during certain commands such as CLUSTER, so * don't insist on a match if old cluster is < 9.0. */ if (strcmp(old_rel->nspname, new_rel->nspname) != 0 || (strcmp(old_rel->relname, new_rel->relname) != 0 && (GET_MAJOR_VERSION(old_cluster.major_version) >= 900 || strcmp(old_rel->nspname, "pg_toast") != 0))) { pg_log(PG_WARNING, "Relation names for OID %u in database \"%s\" do not match: " "old name \"%s.%s\", new name \"%s.%s\"\n", old_rel->reloid, old_db->db_name, old_rel->nspname, old_rel->relname, new_rel->nspname, new_rel->relname); all_matched = false; old_relnum++; new_relnum++; continue; } /* OK, create a mapping entry */ create_rel_filename_map(old_pgdata, new_pgdata, old_db, new_db, old_rel, new_rel, maps + num_maps); num_maps++; old_relnum++; new_relnum++; } if (!all_matched) pg_fatal("Failed to match up old and new tables in database \"%s\"\n", old_db->db_name); *nmaps = num_maps; return maps; }
/* * transfer_single_new_db() * * create links for mappings stored in "maps" array. */ static void transfer_single_new_db(pageCnvCtx *pageConverter, FileNameMap *maps, int size) { char old_dir[MAXPGPATH]; struct dirent **namelist = NULL; int numFiles = 0; int mapnum; int fileno; bool vm_crashsafe_change = false; old_dir[0] = '\0'; /* Do not copy non-crashsafe vm files for binaries that assume crashsafety */ if (old_cluster.controldata.cat_ver < VISIBILITY_MAP_CRASHSAFE_CAT_VER && new_cluster.controldata.cat_ver >= VISIBILITY_MAP_CRASHSAFE_CAT_VER) vm_crashsafe_change = true; for (mapnum = 0; mapnum < size; mapnum++) { char old_file[MAXPGPATH]; char new_file[MAXPGPATH]; /* Changed tablespaces? Need a new directory scan? */ if (strcmp(maps[mapnum].old_dir, old_dir) != 0) { if (numFiles > 0) { for (fileno = 0; fileno < numFiles; fileno++) pg_free(namelist[fileno]); pg_free(namelist); } snprintf(old_dir, sizeof(old_dir), "%s", maps[mapnum].old_dir); numFiles = load_directory(old_dir, &namelist); } /* Copying files might take some time, so give feedback. */ snprintf(old_file, sizeof(old_file), "%s/%u", maps[mapnum].old_dir, maps[mapnum].old_relfilenode); snprintf(new_file, sizeof(new_file), "%s/%u", maps[mapnum].new_dir, maps[mapnum].new_relfilenode); pg_log(PG_REPORT, OVERWRITE_MESSAGE, old_file); /* * Copy/link the relation file to the new cluster */ unlink(new_file); transfer_relfile(pageConverter, old_file, new_file, maps[mapnum].nspname, maps[mapnum].relname); /* fsm/vm files added in PG 8.4 */ if (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) { /* * Copy/link any fsm and vm files, if they exist */ snprintf(scandir_file_pattern, sizeof(scandir_file_pattern), "%u_", maps[mapnum].old_relfilenode); for (fileno = 0; fileno < numFiles; fileno++) { char *vm_offset = strstr(namelist[fileno]->d_name, "_vm"); bool is_vm_file = false; /* Is a visibility map file? (name ends with _vm) */ if (vm_offset && strlen(vm_offset) == strlen("_vm")) is_vm_file = true; if (strncmp(namelist[fileno]->d_name, scandir_file_pattern, strlen(scandir_file_pattern)) == 0 && (!is_vm_file || !vm_crashsafe_change)) { snprintf(old_file, sizeof(old_file), "%s/%s", maps[mapnum].old_dir, namelist[fileno]->d_name); snprintf(new_file, sizeof(new_file), "%s/%u%s", maps[mapnum].new_dir, maps[mapnum].new_relfilenode, strchr(namelist[fileno]->d_name, '_')); unlink(new_file); transfer_relfile(pageConverter, old_file, new_file, maps[mapnum].nspname, maps[mapnum].relname); } } } /* * Now copy/link any related segments as well. Remember, PG breaks * large files into 1GB segments, the first segment has no extension, * subsequent segments are named relfilenode.1, relfilenode.2, * relfilenode.3, ... 'fsm' and 'vm' files use underscores so are not * copied. */ snprintf(scandir_file_pattern, sizeof(scandir_file_pattern), "%u.", maps[mapnum].old_relfilenode); for (fileno = 0; fileno < numFiles; fileno++) { if (strncmp(namelist[fileno]->d_name, scandir_file_pattern, strlen(scandir_file_pattern)) == 0) { snprintf(old_file, sizeof(old_file), "%s/%s", maps[mapnum].old_dir, namelist[fileno]->d_name); snprintf(new_file, sizeof(new_file), "%s/%u%s", maps[mapnum].new_dir, maps[mapnum].new_relfilenode, strchr(namelist[fileno]->d_name, '.')); unlink(new_file); transfer_relfile(pageConverter, old_file, new_file, maps[mapnum].nspname, maps[mapnum].relname); } } } if (numFiles > 0) { for (fileno = 0; fileno < numFiles; fileno++) pg_free(namelist[fileno]); pg_free(namelist); } }
/* * get_rel_infos() * * gets the relinfos for all the user tables of the database refered * by "db". * * NOTE: we assume that relations/entities with oids greater than * FirstNormalObjectId belongs to the user */ static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) { PGconn *conn = connectToServer(cluster, dbinfo->db_name); PGresult *res; RelInfo *relinfos; int ntups; int relnum; int num_rels = 0; char *nspname = NULL; char *relname = NULL; int i_spclocation, i_nspname, i_relname, i_oid, i_relfilenode, i_reltablespace; char query[QUERY_ALLOC]; /* * pg_largeobject contains user data that does not appear in pg_dumpall * --schema-only output, so we have to copy that system table heap and * index. We could grab the pg_largeobject oids from template1, but it is * easy to treat it as a normal table. Order by oid so we can join old/new * structures efficiently. */ snprintf(query, sizeof(query), "SELECT c.oid, n.nspname, c.relname, " " c.relfilenode, c.reltablespace, t.spclocation " "FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " LEFT OUTER JOIN pg_catalog.pg_tablespace t " " ON c.reltablespace = t.oid " "WHERE relkind IN ('r','t', 'i'%s) AND " /* exclude possible orphaned temp tables */ " ((n.nspname !~ '^pg_temp_' AND " " n.nspname !~ '^pg_toast_temp_' AND " " n.nspname NOT IN ('pg_catalog', 'information_schema', 'binary_upgrade') AND " " c.oid >= %u) " " OR (n.nspname = 'pg_catalog' AND " " relname IN ('pg_largeobject', 'pg_largeobject_loid_pn_index'%s) )) " /* we preserve pg_class.oid so we sort by it to match old/new */ "ORDER BY 1;", /* see the comment at the top of old_8_3_create_sequence_script() */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 803) ? "" : ", 'S'", /* this oid allows us to skip system toast tables */ FirstNormalObjectId, /* does pg_largeobject_metadata need to be migrated? */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) ? "" : ", 'pg_largeobject_metadata', 'pg_largeobject_metadata_oid_index'"); res = executeQueryOrDie(conn, query); ntups = PQntuples(res); relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups); i_oid = PQfnumber(res, "oid"); i_nspname = PQfnumber(res, "nspname"); i_relname = PQfnumber(res, "relname"); i_relfilenode = PQfnumber(res, "relfilenode"); i_reltablespace = PQfnumber(res, "reltablespace"); i_spclocation = PQfnumber(res, "spclocation"); for (relnum = 0; relnum < ntups; relnum++) { RelInfo *curr = &relinfos[num_rels++]; const char *tblspace; curr->reloid = atooid(PQgetvalue(res, relnum, i_oid)); nspname = PQgetvalue(res, relnum, i_nspname); strlcpy(curr->nspname, nspname, sizeof(curr->nspname)); relname = PQgetvalue(res, relnum, i_relname); strlcpy(curr->relname, relname, sizeof(curr->relname)); curr->relfilenode = atooid(PQgetvalue(res, relnum, i_relfilenode)); if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0) /* Might be "", meaning the cluster default location. */ tblspace = PQgetvalue(res, relnum, i_spclocation); else /* A zero reltablespace indicates the database tablespace. */ tblspace = dbinfo->db_tblspace; strlcpy(curr->tablespace, tblspace, sizeof(curr->tablespace)); } PQclear(res); PQfinish(conn); dbinfo->rel_arr.rels = relinfos; dbinfo->rel_arr.nrels = num_rels; }
/* * create_script_for_old_cluster_deletion() * * This is particularly useful for tablespace deletion. */ void create_script_for_old_cluster_deletion(char **deletion_script_file_name) { FILE *script = NULL; int tblnum; char old_cluster_pgdata[MAXPGPATH]; *deletion_script_file_name = psprintf("delete_old_cluster.%s", SCRIPT_EXT); /* * Some users (oddly) create tablespaces inside the cluster data * directory. We can't create a proper old cluster delete script in that * case. */ strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH); canonicalize_path(old_cluster_pgdata); for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) { char old_tablespace_dir[MAXPGPATH]; strlcpy(old_tablespace_dir, os_info.old_tablespaces[tblnum], MAXPGPATH); canonicalize_path(old_tablespace_dir); if (path_is_prefix_of_path(old_cluster_pgdata, old_tablespace_dir)) { /* Unlink file in case it is left over from a previous run. */ unlink(*deletion_script_file_name); pg_free(*deletion_script_file_name); *deletion_script_file_name = NULL; return; } } prep_status("Creating script to delete old cluster"); if ((script = fopen_priv(*deletion_script_file_name, "w")) == NULL) pg_fatal("Could not open file \"%s\": %s\n", *deletion_script_file_name, getErrorText(errno)); #ifndef WIN32 /* add shebang header */ fprintf(script, "#!/bin/sh\n\n"); #endif /* delete old cluster's default tablespace */ fprintf(script, RMDIR_CMD " %s\n", fix_path_separator(old_cluster.pgdata)); /* delete old cluster's alternate tablespaces */ for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) { /* * Do the old cluster's per-database directories share a directory * with a new version-specific tablespace? */ if (strlen(old_cluster.tablespace_suffix) == 0) { /* delete per-database directories */ int dbnum; fprintf(script, "\n"); /* remove PG_VERSION? */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) fprintf(script, RM_CMD " %s%cPG_VERSION\n", fix_path_separator(os_info.old_tablespaces[tblnum]), PATH_SEPARATOR); for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) fprintf(script, RMDIR_CMD " %s%c%d\n", fix_path_separator(os_info.old_tablespaces[tblnum]), PATH_SEPARATOR, old_cluster.dbarr.dbs[dbnum].db_oid); } else { char *suffix_path = pg_strdup(old_cluster.tablespace_suffix); /* * Simply delete the tablespace directory, which might be ".old" * or a version-specific subdirectory. */ fprintf(script, RMDIR_CMD " %s%s\n", fix_path_separator(os_info.old_tablespaces[tblnum]), fix_path_separator(suffix_path)); pfree(suffix_path); } } fclose(script); #ifndef WIN32 if (chmod(*deletion_script_file_name, S_IRWXU) != 0) pg_fatal("Could not add execute permission to file \"%s\": %s\n", *deletion_script_file_name, getErrorText(errno)); #endif check_ok(); }
/* * create_script_for_cluster_analyze() * * This incrementally generates better optimizer statistics */ void create_script_for_cluster_analyze(char **analyze_script_file_name) { FILE *script = NULL; char *user_specification = ""; prep_status("Creating script to analyze new cluster"); if (os_info.user_specified) user_specification = psprintf("-U \"%s\" ", os_info.user); *analyze_script_file_name = psprintf("analyze_new_cluster.%s", SCRIPT_EXT); if ((script = fopen_priv(*analyze_script_file_name, "w")) == NULL) pg_fatal("Could not open file \"%s\": %s\n", *analyze_script_file_name, getErrorText(errno)); #ifndef WIN32 /* add shebang header */ fprintf(script, "#!/bin/sh\n\n"); #else /* suppress command echoing */ fprintf(script, "@echo off\n"); #endif fprintf(script, "echo %sThis script will generate minimal optimizer statistics rapidly%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sso your system is usable, and then gather statistics twice more%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %swith increasing accuracy. When it is done, your system will%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %shave the default level of optimizer statistics.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sIf you have used ALTER TABLE to modify the statistics target for%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sany tables, you might want to remove them and restore them after%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %srunning this script because they will delay fast statistics generation.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sIf you would like default statistics as quickly as possible, cancel%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sthis script and run:%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s \"%s/vacuumdb\" %s--all %s%s\n", ECHO_QUOTE, new_cluster.bindir, user_specification, /* Did we copy the free space files? */ (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ? "--analyze-only" : "--analyze", ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "\"%s/vacuumdb\" %s--all --analyze-in-stages\n", new_cluster.bindir, user_specification); /* Did we copy the free space files? */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 804) fprintf(script, "\"%s/vacuumdb\" %s--all\n", new_cluster.bindir, user_specification); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sDone%s\n", ECHO_QUOTE, ECHO_QUOTE); fclose(script); #ifndef WIN32 if (chmod(*analyze_script_file_name, S_IRWXU) != 0) pg_fatal("Could not add execute permission to file \"%s\": %s\n", *analyze_script_file_name, getErrorText(errno)); #endif if (os_info.user_specified) pg_free(user_specification); check_ok(); }
void check_and_dump_old_cluster(bool live_check) { /* -- OLD -- */ if (!live_check) start_postmaster(&old_cluster, true); /* Extract a list of databases and tables from the old cluster */ get_db_and_rel_infos(&old_cluster); init_tablespaces(); get_loadable_libraries(); /* * Check for various failure cases */ check_is_install_user(&old_cluster); check_proper_datallowconn(&old_cluster); check_for_prepared_transactions(&old_cluster); check_for_reg_data_type_usage(&old_cluster); check_for_isn_and_int8_passing_mismatch(&old_cluster); /* * Pre-PG 10 allowed tables with 'unknown' type columns and non WAL logged * hash indexes */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 906) { old_9_6_check_for_unknown_data_type_usage(&old_cluster); if (user_opts.check) old_9_6_invalidate_hash_indexes(&old_cluster, true); } /* 9.5 and below should not have roles starting with pg_ */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 905) check_for_pg_role_prefix(&old_cluster); if (GET_MAJOR_VERSION(old_cluster.major_version) == 904 && old_cluster.controldata.cat_ver < JSONB_FORMAT_CHANGE_CAT_VER) check_for_jsonb_9_4_usage(&old_cluster); /* Pre-PG 9.4 had a different 'line' data type internal format */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 903) old_9_3_check_for_line_data_type_usage(&old_cluster); /* Pre-PG 9.0 had no large object permissions */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) new_9_0_populate_pg_largeobject_metadata(&old_cluster, true); /* * While not a check option, we do this now because this is the only time * the old server is running. */ if (!user_opts.check) generate_old_dump(); if (!live_check) stop_postmaster(false); }
/* * 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 migration, 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(migratorContext *ctx, 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_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 *lang = NULL; /* * Because we test the pg_resetxlog output strings, it has to be in * English. */ if (getenv("LANG")) lang = pg_strdup(ctx, getenv("LANG")); #ifndef WIN32 putenv(pg_strdup(ctx, "LANG=C")); #else SetEnvironmentVariableA("LANG", "C"); #endif sprintf(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(ctx, PG_FATAL, "Could not get control data: %s\n", 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; } #ifdef EDB_NATIVE_LANG /* EDB AS 8.3 is an 8.2 code base */ if (cluster->is_edb_as && GET_MAJOR_VERSION(cluster->major_version) <= 803) { cluster->controldata.toast = TOAST_MAX_CHUNK_SIZE; got_toast = true; } #endif /* we have the result of cmd in "output". so parse it line by line now */ while (fgets(bufin, sizeof(bufin), output)) { if (ctx->debug) fprintf(ctx->debug_fd, 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(ctx, 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 Postgres\n" "8.3 to fix this bug. Postgres 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(ctx, PG_FATAL, "%d: pg_resetxlog problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.ctrl_ver = (uint32) atol(p); } else if ((p = strstr(bufin, "Catalog version number:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.cat_ver = (uint32) atol(p); } else if ((p = strstr(bufin, "First log file ID after reset:")) != NULL || (cluster->is_edb_as && GET_MAJOR_VERSION(cluster->major_version) <= 803 && (p = strstr(bufin, "Current log file ID:")) != NULL)) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.logid = (uint32) atol(p); got_log_id = true; } else if ((p = strstr(bufin, "First log file segment after reset:")) != NULL || (cluster->is_edb_as && GET_MAJOR_VERSION(cluster->major_version) <= 803 && (p = strstr(bufin, "Next log file segment:")) != NULL)) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.nxtlogseg = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_tli = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); op++; /* removing ':' char */ cluster->controldata.chkpnt_nxtxid = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.chkpnt_nxtoid = (uint32) atol(p); got_oid = true; } else if ((p = strstr(bufin, "Maximum data alignment:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.align = (uint32) atol(p); got_align = true; } else if ((p = strstr(bufin, "Database block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.blocksz = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.largesz = (uint32) atol(p); got_largesz = true; } else if ((p = strstr(bufin, "WAL block size:")) != NULL) { p = strchr(p, ':'); if (p == NULL || strlen(p) <= 1) pg_log(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.walsz = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.walseg = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.ident = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.index = (uint32) atol(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(ctx, PG_FATAL, "%d: controldata retrieval problem\n", __LINE__); p++; /* removing ':' char */ cluster->controldata.toast = (uint32) atol(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(ctx, 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(ctx, 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(ctx, 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(ctx, 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(ctx, 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(ctx, p); } } if (output) pclose(output); /* restore LANG */ if (lang) { #ifndef WIN32 char *envstr = (char *) pg_malloc(ctx, strlen(lang) + 6); sprintf(envstr, "LANG=%s", lang); putenv(envstr); #else SetEnvironmentVariableA("LANG", lang); #endif pg_free(lang); } else { #ifndef WIN32 unsetenv("LANG"); #else SetEnvironmentVariableA("LANG", ""); #endif } /* verify that we got all the mandatory pg_control data */ if (!got_xid || !got_oid || (!live_check && !got_log_id) || (!live_check && !got_log_seg) || !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(ctx, PG_REPORT, "Some required control information is missing; cannot find:\n"); if (!got_xid) pg_log(ctx, PG_REPORT, " checkpoint next XID\n"); if (!got_oid) pg_log(ctx, PG_REPORT, " latest checkpoint next OID\n"); if (!live_check && !got_log_id) pg_log(ctx, PG_REPORT, " first log file ID after reset\n"); if (!live_check && !got_log_seg) pg_log(ctx, PG_REPORT, " first log file segment after reset\n"); if (!got_tli) pg_log(ctx, PG_REPORT, " latest checkpoint timeline ID\n"); if (!got_align) pg_log(ctx, PG_REPORT, " maximum alignment\n"); if (!got_blocksz) pg_log(ctx, PG_REPORT, " block size\n"); if (!got_largesz) pg_log(ctx, PG_REPORT, " large relation segment size\n"); if (!got_walsz) pg_log(ctx, PG_REPORT, " WAL block size\n"); if (!got_walseg) pg_log(ctx, PG_REPORT, " WAL segment size\n"); if (!got_ident) pg_log(ctx, PG_REPORT, " maximum identifier length\n"); if (!got_index) pg_log(ctx, PG_REPORT, " maximum number of indexed columns\n"); if (!got_toast) pg_log(ctx, PG_REPORT, " maximum TOAST chunk size\n"); if (!got_date_is_int) pg_log(ctx, PG_REPORT, " dates/times are integers?\n"); /* value added in Postgres 8.4 */ if (!got_float8_pass_by_value) pg_log(ctx, PG_REPORT, " float8 argument passing method\n"); pg_log(ctx, PG_FATAL, "Unable to continue without required control information, terminating\n"); } }
/* * get_rel_infos() * * gets the relinfos for all the user tables of the database referred * by "db". * * NOTE: we assume that relations/entities with oids greater than * FirstNormalObjectId belongs to the user */ static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) { PGconn *conn = connectToServer(cluster, dbinfo->db_name); PGresult *res; RelInfo *relinfos; int ntups; int relnum; int num_rels = 0; char *nspname = NULL; char *relname = NULL; int i_spclocation, i_nspname, i_relname, i_oid, i_relfilenode, i_reltablespace; char query[QUERY_ALLOC]; /* * pg_largeobject contains user data that does not appear in pg_dumpall * --schema-only output, so we have to copy that system table heap and * index. We could grab the pg_largeobject oids from template1, but it is * easy to treat it as a normal table. Order by oid so we can join old/new * structures efficiently. */ snprintf(query, sizeof(query), "CREATE TEMPORARY TABLE info_rels (reloid) AS SELECT c.oid " "FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " "LEFT OUTER JOIN pg_catalog.pg_index i " " ON c.oid = i.indexrelid " "WHERE relkind IN ('r', 'm', 'i'%s) AND " /* * pg_dump only dumps valid indexes; testing indisready is necessary in * 9.2, and harmless in earlier/later versions. */ " i.indisvalid IS DISTINCT FROM false AND " " i.indisready IS DISTINCT FROM false AND " /* exclude possible orphaned temp tables */ " ((n.nspname !~ '^pg_temp_' AND " " n.nspname !~ '^pg_toast_temp_' AND " /* skip pg_toast because toast index have relkind == 'i', not 't' */ " n.nspname NOT IN ('pg_catalog', 'information_schema', " " 'binary_upgrade', 'pg_toast') AND " " c.oid >= %u) " " OR (n.nspname = 'pg_catalog' AND " " relname IN ('pg_largeobject', 'pg_largeobject_loid_pn_index'%s) ));", /* see the comment at the top of old_8_3_create_sequence_script() */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 803) ? "" : ", 'S'", FirstNormalObjectId, /* does pg_largeobject_metadata need to be migrated? */ (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) ? "" : ", 'pg_largeobject_metadata', 'pg_largeobject_metadata_oid_index'"); PQclear(executeQueryOrDie(conn, "%s", query)); /* * Get TOAST tables and indexes; we have to gather the TOAST tables in * later steps because we can't schema-qualify TOAST tables. */ PQclear(executeQueryOrDie(conn, "INSERT INTO info_rels " "SELECT reltoastrelid " "FROM info_rels i JOIN pg_catalog.pg_class c " " ON i.reloid = c.oid " " AND c.reltoastrelid != %u", InvalidOid)); PQclear(executeQueryOrDie(conn, "INSERT INTO info_rels " "SELECT indexrelid " "FROM pg_index " "WHERE indisvalid " " AND indrelid IN (SELECT reltoastrelid " " FROM info_rels i " " JOIN pg_catalog.pg_class c " " ON i.reloid = c.oid " " AND c.reltoastrelid != %u)", InvalidOid)); snprintf(query, sizeof(query), "SELECT c.oid, n.nspname, c.relname, " " c.relfilenode, c.reltablespace, %s " "FROM info_rels i JOIN pg_catalog.pg_class c " " ON i.reloid = c.oid " " JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " LEFT OUTER JOIN pg_catalog.pg_tablespace t " " ON c.reltablespace = t.oid " /* we preserve pg_class.oid so we sort by it to match old/new */ "ORDER BY 1;", /* 9.2 removed the spclocation column */ (GET_MAJOR_VERSION(cluster->major_version) <= 901) ? "t.spclocation" : "pg_catalog.pg_tablespace_location(t.oid) AS spclocation"); res = executeQueryOrDie(conn, "%s", query); ntups = PQntuples(res); relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups); i_oid = PQfnumber(res, "oid"); i_nspname = PQfnumber(res, "nspname"); i_relname = PQfnumber(res, "relname"); i_relfilenode = PQfnumber(res, "relfilenode"); i_reltablespace = PQfnumber(res, "reltablespace"); i_spclocation = PQfnumber(res, "spclocation"); for (relnum = 0; relnum < ntups; relnum++) { RelInfo *curr = &relinfos[num_rels++]; const char *tblspace; curr->reloid = atooid(PQgetvalue(res, relnum, i_oid)); nspname = PQgetvalue(res, relnum, i_nspname); curr->nspname = pg_strdup(nspname); relname = PQgetvalue(res, relnum, i_relname); curr->relname = pg_strdup(relname); curr->relfilenode = atooid(PQgetvalue(res, relnum, i_relfilenode)); if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0) /* Might be "", meaning the cluster default location. */ tblspace = PQgetvalue(res, relnum, i_spclocation); else /* A zero reltablespace indicates the database tablespace. */ tblspace = dbinfo->db_tblspace; strlcpy(curr->tablespace, tblspace, sizeof(curr->tablespace)); } PQclear(res); PQfinish(conn); dbinfo->rel_arr.rels = relinfos; dbinfo->rel_arr.nrels = num_rels; }
/* * check_loadable_libraries() * * Check that the new cluster contains all required libraries. * We do this by actually trying to LOAD each one, thereby testing * compatibility as well as presence. */ void check_loadable_libraries(void) { PGconn *conn = connectToServer(&new_cluster, "template1"); int libnum; FILE *script = NULL; bool found = false; char output_path[MAXPGPATH]; prep_status("Checking for presence of required libraries"); snprintf(output_path, sizeof(output_path), "loadable_libraries.txt"); for (libnum = 0; libnum < os_info.num_libraries; libnum++) { char *lib = os_info.libraries[libnum]; int llen = strlen(lib); char cmd[7 + 2 * MAXPGPATH + 1]; PGresult *res; /* * In Postgres 9.0, Python 3 support was added, and to do that, a * plpython2u language was created with library name plpython2.so * as a symbolic link to plpython.so. In Postgres 9.1, only the * plpython2.so library was created, and both plpythonu and * plpython2u pointing to it. For this reason, any reference to * library name "plpython" in an old PG <= 9.1 cluster must look * for "plpython2" in the new cluster. * * For this case, we could check pg_pltemplate, but that only works * for languages, and does not help with function shared objects, * so we just do a general fix. * * Some systems have plpython_call_handler() that references * "plpython" defined in the "public" schema, causing pg_dump to * dump it an generate an error on pg_dumpall restore; not sure * on the cause, see: * http://archives.postgresql.org/pgsql-hackers/2012-03/msg01101.php */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 901 && strcmp(lib, "$libdir/plpython") == 0) { lib = "$libdir/plpython2"; llen = strlen(lib); } strcpy(cmd, "LOAD '"); PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL); strcat(cmd, "'"); res = PQexec(conn, cmd); if (PQresultStatus(res) != PGRES_COMMAND_OK) { found = true; /* exit and report missing support library with special message */ if (strcmp(lib, PG_UPGRADE_SUPPORT) == 0) pg_log(PG_FATAL, "The pg_upgrade_support module must be created and installed in the new cluster.\n"); if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL) pg_log(PG_FATAL, "Could not open file \"%s\": %s\n", output_path, getErrorText(errno)); fprintf(script, "Could not load library \"%s\"\n%s\n", lib, PQerrorMessage(conn)); } PQclear(res); } PQfinish(conn); if (found) { fclose(script); pg_log(PG_REPORT, "fatal\n"); pg_log(PG_FATAL, "Your installation references loadable libraries that are missing from the\n" "new installation. You can add these libraries to the new installation,\n" "or remove the functions using them from the old installation. A list of\n" "problem libraries is in the file:\n" " %s\n\n", output_path); } else check_ok(); }
/* * create_script_for_old_cluster_deletion() * * This is particularly useful for tablespace deletion. */ void create_script_for_old_cluster_deletion(char **deletion_script_file_name) { FILE *script = NULL; int tblnum; *deletion_script_file_name = pg_malloc(MAXPGPATH); prep_status("Creating script to delete old cluster"); snprintf(*deletion_script_file_name, MAXPGPATH, "%s/delete_old_cluster.%s", os_info.cwd, SCRIPT_EXT); if ((script = fopen(*deletion_script_file_name, "w")) == NULL) pg_log(PG_FATAL, "Could not open file \"%s\": %s\n", *deletion_script_file_name, getErrorText(errno)); #ifndef WIN32 /* add shebang header */ fprintf(script, "#!/bin/sh\n\n"); #endif /* delete old cluster's default tablespace */ fprintf(script, RMDIR_CMD " %s\n", old_cluster.pgdata); /* delete old cluster's alternate tablespaces */ for (tblnum = 0; tblnum < os_info.num_tablespaces; tblnum++) { /* * Do the old cluster's per-database directories share a directory * with a new version-specific tablespace? */ if (strlen(old_cluster.tablespace_suffix) == 0) { /* delete per-database directories */ int dbnum; fprintf(script, "\n"); /* remove PG_VERSION? */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) fprintf(script, RM_CMD " %s%s/PG_VERSION\n", os_info.tablespaces[tblnum], old_cluster.tablespace_suffix); for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++) { fprintf(script, RMDIR_CMD " %s%s/%d\n", os_info.tablespaces[tblnum], old_cluster.tablespace_suffix, old_cluster.dbarr.dbs[dbnum].db_oid); } } else /* * Simply delete the tablespace directory, which might be ".old" * or a version-specific subdirectory. */ fprintf(script, RMDIR_CMD " %s%s\n", os_info.tablespaces[tblnum], old_cluster.tablespace_suffix); } fclose(script); #ifndef WIN32 if (chmod(*deletion_script_file_name, S_IRWXU) != 0) pg_log(PG_FATAL, "Could not add execute permission to file \"%s\": %s\n", *deletion_script_file_name, getErrorText(errno)); #endif check_ok(); }
/* * get_rel_infos() * * gets the relinfos for all the user tables and indexes of the database * referred to by "dbinfo". * * Note: the resulting RelInfo array is assumed to be sorted by OID. * This allows later processing to match up old and new databases efficiently. */ static void get_rel_infos(ClusterInfo *cluster, DbInfo *dbinfo) { PGconn *conn = connectToServer(cluster, dbinfo->db_name); PGresult *res; RelInfo *relinfos; int ntups; int relnum; int num_rels = 0; char *nspname = NULL; char *relname = NULL; char *tablespace = NULL; int i_spclocation, i_nspname, i_relname, i_reloid, i_indtable, i_toastheap, i_relfilenode, i_reltablespace; char query[QUERY_ALLOC]; char *last_namespace = NULL, *last_tablespace = NULL; query[0] = '\0'; /* initialize query string to empty */ /* * Create a CTE that collects OIDs of regular user tables, including * matviews and sequences, but excluding toast tables and indexes. We * assume that relations with OIDs >= FirstNormalObjectId belong to the * user. (That's probably redundant with the namespace-name exclusions, * but let's be safe.) * * pg_largeobject contains user data that does not appear in pg_dump * output, so we have to copy that system table. It's easiest to do that * by treating it as a user table. */ snprintf(query + strlen(query), sizeof(query) - strlen(query), "WITH regular_heap (reloid, indtable, toastheap) AS ( " " SELECT c.oid, 0::oid, 0::oid " " FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " WHERE relkind IN (" CppAsString2(RELKIND_RELATION) ", " CppAsString2(RELKIND_MATVIEW) ") AND " /* exclude possible orphaned temp tables */ " ((n.nspname !~ '^pg_temp_' AND " " n.nspname !~ '^pg_toast_temp_' AND " " n.nspname NOT IN ('pg_catalog', 'information_schema', " " 'binary_upgrade', 'pg_toast') AND " " c.oid >= %u::pg_catalog.oid) OR " " (n.nspname = 'pg_catalog' AND " " relname IN ('pg_largeobject') ))), ", FirstNormalObjectId); /* * Add a CTE that collects OIDs of toast tables belonging to the tables * selected by the regular_heap CTE. (We have to do this separately * because the namespace-name rules above don't work for toast tables.) */ snprintf(query + strlen(query), sizeof(query) - strlen(query), " toast_heap (reloid, indtable, toastheap) AS ( " " SELECT c.reltoastrelid, 0::oid, c.oid " " FROM regular_heap JOIN pg_catalog.pg_class c " " ON regular_heap.reloid = c.oid " " WHERE c.reltoastrelid != 0), "); /* * Add a CTE that collects OIDs of all valid indexes on the previously * selected tables. We can ignore invalid indexes since pg_dump does. * Testing indisready is necessary in 9.2, and harmless in earlier/later * versions. */ snprintf(query + strlen(query), sizeof(query) - strlen(query), " all_index (reloid, indtable, toastheap) AS ( " " SELECT indexrelid, indrelid, 0::oid " " FROM pg_catalog.pg_index " " WHERE indisvalid AND indisready " " AND indrelid IN " " (SELECT reloid FROM regular_heap " " UNION ALL " " SELECT reloid FROM toast_heap)) "); /* * And now we can write the query that retrieves the data we want for each * heap and index relation. Make sure result is sorted by OID. */ snprintf(query + strlen(query), sizeof(query) - strlen(query), "SELECT all_rels.*, n.nspname, c.relname, " " c.relfilenode, c.reltablespace, %s " "FROM (SELECT * FROM regular_heap " " UNION ALL " " SELECT * FROM toast_heap " " UNION ALL " " SELECT * FROM all_index) all_rels " " JOIN pg_catalog.pg_class c " " ON all_rels.reloid = c.oid " " JOIN pg_catalog.pg_namespace n " " ON c.relnamespace = n.oid " " LEFT OUTER JOIN pg_catalog.pg_tablespace t " " ON c.reltablespace = t.oid " "ORDER BY 1;", /* 9.2 removed the pg_tablespace.spclocation column */ (GET_MAJOR_VERSION(cluster->major_version) >= 902) ? "pg_catalog.pg_tablespace_location(t.oid) AS spclocation" : "t.spclocation"); res = executeQueryOrDie(conn, "%s", query); ntups = PQntuples(res); relinfos = (RelInfo *) pg_malloc(sizeof(RelInfo) * ntups); i_reloid = PQfnumber(res, "reloid"); i_indtable = PQfnumber(res, "indtable"); i_toastheap = PQfnumber(res, "toastheap"); i_nspname = PQfnumber(res, "nspname"); i_relname = PQfnumber(res, "relname"); i_relfilenode = PQfnumber(res, "relfilenode"); i_reltablespace = PQfnumber(res, "reltablespace"); i_spclocation = PQfnumber(res, "spclocation"); for (relnum = 0; relnum < ntups; relnum++) { RelInfo *curr = &relinfos[num_rels++]; curr->reloid = atooid(PQgetvalue(res, relnum, i_reloid)); curr->indtable = atooid(PQgetvalue(res, relnum, i_indtable)); curr->toastheap = atooid(PQgetvalue(res, relnum, i_toastheap)); nspname = PQgetvalue(res, relnum, i_nspname); curr->nsp_alloc = false; /* * Many of the namespace and tablespace strings are identical, so we * try to reuse the allocated string pointers where possible to reduce * memory consumption. */ /* Can we reuse the previous string allocation? */ if (last_namespace && strcmp(nspname, last_namespace) == 0) curr->nspname = last_namespace; else { last_namespace = curr->nspname = pg_strdup(nspname); curr->nsp_alloc = true; } relname = PQgetvalue(res, relnum, i_relname); curr->relname = pg_strdup(relname); curr->relfilenode = atooid(PQgetvalue(res, relnum, i_relfilenode)); curr->tblsp_alloc = false; /* Is the tablespace oid non-default? */ if (atooid(PQgetvalue(res, relnum, i_reltablespace)) != 0) { /* * The tablespace location might be "", meaning the cluster * default location, i.e. pg_default or pg_global. */ tablespace = PQgetvalue(res, relnum, i_spclocation); /* Can we reuse the previous string allocation? */ if (last_tablespace && strcmp(tablespace, last_tablespace) == 0) curr->tablespace = last_tablespace; else { last_tablespace = curr->tablespace = pg_strdup(tablespace); curr->tblsp_alloc = true; } } else /* A zero reltablespace oid indicates the database tablespace. */ curr->tablespace = dbinfo->db_tablespace; } PQclear(res); PQfinish(conn); dbinfo->rel_arr.rels = relinfos; dbinfo->rel_arr.nrels = num_rels; }
void check_old_cluster(bool live_check, char **sequence_script_file_name) { /* -- OLD -- */ if (!live_check) start_postmaster(&old_cluster); set_locale_and_encoding(&old_cluster); get_pg_database_relfilenode(&old_cluster); /* Extract a list of databases and tables from the old cluster */ get_db_and_rel_infos(&old_cluster); init_tablespaces(); get_loadable_libraries(); /* * Check for various failure cases */ check_is_super_user(&old_cluster); check_for_prepared_transactions(&old_cluster); check_for_reg_data_type_usage(&old_cluster); check_for_isn_and_int8_passing_mismatch(&old_cluster); /* old = PG 8.3 checks? */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 803) { old_8_3_check_for_name_data_type_usage(&old_cluster); old_8_3_check_for_tsquery_usage(&old_cluster); if (user_opts.check) { old_8_3_rebuild_tsvector_tables(&old_cluster, true); old_8_3_invalidate_hash_gin_indexes(&old_cluster, true); old_8_3_invalidate_bpchar_pattern_ops_indexes(&old_cluster, true); } else /* * While we have the old server running, create the script to * properly restore its sequence values but we report this at the * end. */ *sequence_script_file_name = old_8_3_create_sequence_script(&old_cluster); } /* Pre-PG 9.0 had no large object permissions */ if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804) new_9_0_populate_pg_largeobject_metadata(&old_cluster, true); /* * While not a check option, we do this now because this is the only time * the old server is running. */ if (!user_opts.check) { generate_old_dump(); split_old_dump(); } if (!live_check) stop_postmaster(false); }
/* * 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; 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), 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; } /* 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); #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; } else if ((p = strstr(bufin, "checksum")) != 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.data_checksum_version = str2uint(p); got_data_checksum_version = 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 || !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_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"); /* value added in Postgres 9.3 */ if (!got_data_checksum_version) pg_log(PG_REPORT, " data checksum version\n"); pg_log(PG_FATAL, "Cannot continue without required control information, terminating\n"); } }
/* * create_script_for_cluster_analyze() * * This incrementally generates better optimizer statistics */ void create_script_for_cluster_analyze(char **analyze_script_file_name) { FILE *script = NULL; char *user_specification = ""; prep_status("Creating script to analyze new cluster"); if (os_info.user_specified) user_specification = psprintf("-U \"%s\" ", os_info.user); *analyze_script_file_name = psprintf("analyze_new_cluster.%s", SCRIPT_EXT); if ((script = fopen_priv(*analyze_script_file_name, "w")) == NULL) pg_fatal("Could not open file \"%s\": %s\n", *analyze_script_file_name, getErrorText(errno)); #ifndef WIN32 /* add shebang header */ fprintf(script, "#!/bin/sh\n\n"); #else /* suppress command echoing */ fprintf(script, "@echo off\n"); #endif fprintf(script, "echo %sThis script will generate minimal optimizer statistics rapidly%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sso your system is usable, and then gather statistics twice more%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %swith increasing accuracy. When it is done, your system will%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %shave the default level of optimizer statistics.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sIf you have used ALTER TABLE to modify the statistics target for%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sany tables, you might want to remove them and restore them after%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %srunning this script because they will delay fast statistics generation.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sIf you would like default statistics as quickly as possible, cancel%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sthis script and run:%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s \"%s/vacuumdb\" %s--all %s%s\n", ECHO_QUOTE, new_cluster.bindir, user_specification, /* Did we copy the free space files? */ (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ? "--analyze-only" : "--analyze", ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); #ifndef WIN32 fprintf(script, "sleep 2\n"); fprintf(script, "PGOPTIONS='-c default_statistics_target=1 -c vacuum_cost_delay=0'\n"); /* only need to export once */ fprintf(script, "export PGOPTIONS\n"); #else fprintf(script, "REM simulate sleep 2\n"); fprintf(script, "PING 1.1.1.1 -n 1 -w 2000 > nul\n"); fprintf(script, "SET PGOPTIONS=-c default_statistics_target=1 -c vacuum_cost_delay=0\n"); #endif fprintf(script, "echo %sGenerating minimal optimizer statistics (1 target)%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s--------------------------------------------------%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "\"%s/vacuumdb\" %s--all --analyze-only\n", new_cluster.bindir, user_specification); fprintf(script, "echo%s\n", ECHO_BLANK); fprintf(script, "echo %sThe server is now available with minimal optimizer statistics.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %sQuery performance will be optimal once this script completes.%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo%s\n\n", ECHO_BLANK); #ifndef WIN32 fprintf(script, "sleep 2\n"); fprintf(script, "PGOPTIONS='-c default_statistics_target=10'\n"); #else fprintf(script, "REM simulate sleep\n"); fprintf(script, "PING 1.1.1.1 -n 1 -w 2000 > nul\n"); fprintf(script, "SET PGOPTIONS=-c default_statistics_target=10\n"); #endif fprintf(script, "echo %sGenerating medium optimizer statistics (10 targets)%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s---------------------------------------------------%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "\"%s/vacuumdb\" %s--all --analyze-only\n", new_cluster.bindir, user_specification); fprintf(script, "echo%s\n\n", ECHO_BLANK); #ifndef WIN32 fprintf(script, "unset PGOPTIONS\n"); #else fprintf(script, "SET PGOPTIONS\n"); #endif fprintf(script, "echo %sGenerating default (full) optimizer statistics (100 targets?)%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "echo %s-------------------------------------------------------------%s\n", ECHO_QUOTE, ECHO_QUOTE); fprintf(script, "\"%s/vacuumdb\" %s--all %s\n", new_cluster.bindir, user_specification, /* Did we copy the free space files? */ (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ? "--analyze-only" : "--analyze"); fprintf(script, "echo%s\n\n", ECHO_BLANK); fprintf(script, "echo %sDone%s\n", ECHO_QUOTE, ECHO_QUOTE); fclose(script); #ifndef WIN32 if (chmod(*analyze_script_file_name, S_IRWXU) != 0) pg_fatal("Could not add execute permission to file \"%s\": %s\n", *analyze_script_file_name, getErrorText(errno)); #endif if (os_info.user_specified) pg_free(user_specification); check_ok(); }