/* * parseCommandLine() * * Parses the command line (argc, argv[]) and loads structures */ void parseCommandLine(int argc, char *argv[]) { static struct option long_options[] = { {"old-datadir", required_argument, NULL, 'd'}, {"new-datadir", required_argument, NULL, 'D'}, {"old-bindir", required_argument, NULL, 'b'}, {"new-bindir", required_argument, NULL, 'B'}, {"old-options", required_argument, NULL, 'o'}, {"new-options", required_argument, NULL, 'O'}, {"old-port", required_argument, NULL, 'p'}, {"new-port", required_argument, NULL, 'P'}, {"username", required_argument, NULL, 'U'}, {"check", no_argument, NULL, 'c'}, {"link", no_argument, NULL, 'k'}, {"retain", no_argument, NULL, 'r'}, {"jobs", required_argument, NULL, 'j'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; int option; /* Command line option */ int optindex = 0; /* used by getopt_long */ int os_user_effective_id; FILE *fp; char **filename; time_t run_time = time(NULL); user_opts.transfer_mode = TRANSFER_MODE_COPY; os_info.progname = get_progname(argv[0]); /* Process libpq env. variables; load values here for usage() output */ old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT; new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT; os_user_effective_id = get_user_info(&os_info.user); /* we override just the database user name; we got the OS id above */ if (getenv("PGUSER")) { pg_free(os_info.user); /* must save value, getenv()'s pointer is not stable */ os_info.user = pg_strdup(getenv("PGUSER")); } if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_upgrade (PostgreSQL) " PG_VERSION); exit(0); } } /* Allow help and version to be run as root, so do the test here. */ if (os_user_effective_id == 0) pg_fatal("%s: cannot be run as root\n", os_info.progname); if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL) pg_fatal("cannot write to log file %s\n", INTERNAL_LOG_FILE); while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rU:v", long_options, &optindex)) != -1) { switch (option) { case 'b': old_cluster.bindir = pg_strdup(optarg); break; case 'B': new_cluster.bindir = pg_strdup(optarg); break; case 'c': user_opts.check = true; break; case 'd': old_cluster.pgdata = pg_strdup(optarg); old_cluster.pgconfig = pg_strdup(optarg); break; case 'D': new_cluster.pgdata = pg_strdup(optarg); new_cluster.pgconfig = pg_strdup(optarg); break; case 'j': user_opts.jobs = atoi(optarg); break; case 'k': user_opts.transfer_mode = TRANSFER_MODE_LINK; break; case 'o': old_cluster.pgopts = pg_strdup(optarg); break; case 'O': new_cluster.pgopts = pg_strdup(optarg); break; /* * Someday, the port number option could be removed and passed * using -o/-O, but that requires postmaster -C to be * supported on all old/new versions. */ case 'p': if ((old_cluster.port = atoi(optarg)) <= 0) { pg_fatal("invalid old port number\n"); exit(1); } break; case 'P': if ((new_cluster.port = atoi(optarg)) <= 0) { pg_fatal("invalid new port number\n"); exit(1); } break; case 'r': log_opts.retain = true; break; case 'U': pg_free(os_info.user); os_info.user = pg_strdup(optarg); os_info.user_specified = true; /* * Push the user name into the environment so pre-9.1 * pg_ctl/libpq uses it. */ pg_putenv("PGUSER", os_info.user); break; case 'v': pg_log(PG_REPORT, "Running in verbose mode\n"); log_opts.verbose = true; break; default: pg_fatal("Try \"%s --help\" for more information.\n", os_info.progname); break; } } /* label start of upgrade in logfiles */ for (filename = output_files; *filename != NULL; filename++) { if ((fp = fopen_priv(*filename, "a")) == NULL) pg_fatal("cannot write to log file %s\n", *filename); /* Start with newline because we might be appending to a file. */ fprintf(fp, "\n" "-----------------------------------------------------------------\n" " pg_upgrade run on %s" "-----------------------------------------------------------------\n\n", ctime(&run_time)); fclose(fp); } /* Turn off read-only mode; add prefix to PGOPTIONS? */ if (getenv("PGOPTIONS")) { char *pgoptions = psprintf("%s %s", FIX_DEFAULT_READ_ONLY, getenv("PGOPTIONS")); pg_putenv("PGOPTIONS", pgoptions); pfree(pgoptions); } else pg_putenv("PGOPTIONS", FIX_DEFAULT_READ_ONLY); /* Get values from env if not already set */ check_required_directory(&old_cluster.bindir, NULL, "PGBINOLD", "-b", "old cluster binaries reside"); check_required_directory(&new_cluster.bindir, NULL, "PGBINNEW", "-B", "new cluster binaries reside"); check_required_directory(&old_cluster.pgdata, &old_cluster.pgconfig, "PGDATAOLD", "-d", "old cluster data resides"); check_required_directory(&new_cluster.pgdata, &new_cluster.pgconfig, "PGDATANEW", "-D", "new cluster data resides"); }
int main(int argc, char **argv) { char *analyze_script_file_name = NULL; char *deletion_script_file_name = NULL; bool live_check = false; parseCommandLine(argc, argv); get_restricted_token(os_info.progname); adjust_data_dir(&old_cluster); adjust_data_dir(&new_cluster); setup(argv[0], &live_check); output_check_banner(live_check); check_cluster_versions(); get_sock_dir(&old_cluster, live_check); get_sock_dir(&new_cluster, false); check_cluster_compatibility(live_check); check_and_dump_old_cluster(live_check); /* -- NEW -- */ start_postmaster(&new_cluster, true); check_new_cluster(); report_clusters_compatible(); pg_log(PG_REPORT, "\nPerforming Upgrade\n"); pg_log(PG_REPORT, "------------------\n"); prepare_new_cluster(); stop_postmaster(false); /* * Destructive Changes to New Cluster */ copy_clog_xlog_xid(); /* New now using xids of the old system */ /* -- NEW -- */ start_postmaster(&new_cluster, true); prepare_new_databases(); create_new_objects(); stop_postmaster(false); /* * Most failures happen in create_new_objects(), which has completed at * this point. We do this here because it is just before linking, which * will link the old and new cluster data files, preventing the old * cluster from being safely started once the new cluster is started. */ if (user_opts.transfer_mode == TRANSFER_MODE_LINK) disable_old_cluster(); transfer_all_new_tablespaces(&old_cluster.dbarr, &new_cluster.dbarr, old_cluster.pgdata, new_cluster.pgdata); /* * Assuming OIDs are only used in system tables, there is no need to * restore the OID counter because we have not transferred any OIDs from * the old system, but we do it anyway just in case. We do it late here * because there is no need to have the schema load use new oids. */ prep_status("Setting next OID for new cluster"); exec_prog(UTILITY_LOG_FILE, NULL, true, "\"%s/pg_resetxlog\" -o %u \"%s\"", new_cluster.bindir, old_cluster.controldata.chkpnt_nxtoid, new_cluster.pgdata); check_ok(); prep_status("Sync data directory to disk"); exec_prog(UTILITY_LOG_FILE, NULL, true, "\"%s/initdb\" --sync-only \"%s\"", new_cluster.bindir, new_cluster.pgdata); check_ok(); create_script_for_cluster_analyze(&analyze_script_file_name); create_script_for_old_cluster_deletion(&deletion_script_file_name); issue_warnings(); pg_log(PG_REPORT, "\nUpgrade Complete\n"); pg_log(PG_REPORT, "----------------\n"); output_completion_banner(analyze_script_file_name, deletion_script_file_name); pg_free(analyze_script_file_name); pg_free(deletion_script_file_name); cleanup(); return 0; }
/* * Output the pivoted resultset with the printTable* functions. Return true * if successful, false otherwise. */ static bool printCrosstab(const PGresult *results, int num_columns, pivot_field *piv_columns, int field_for_columns, int num_rows, pivot_field *piv_rows, int field_for_rows, int field_for_data) { printQueryOpt popt = pset.popt; printTableContent cont; int i, rn; char col_align; int *horiz_map; bool retval = false; printTableInit(&cont, &popt.topt, popt.title, num_columns + 1, num_rows); /* Step 1: set target column names (horizontal header) */ /* The name of the first column is kept unchanged by the pivoting */ printTableAddHeader(&cont, PQfname(results, field_for_rows), false, column_type_alignment(PQftype(results, field_for_rows))); /* * To iterate over piv_columns[] by piv_columns[].rank, create a reverse * map associating each piv_columns[].rank to its index in piv_columns. * This avoids an O(N^2) loop later. */ horiz_map = (int *) pg_malloc(sizeof(int) * num_columns); for (i = 0; i < num_columns; i++) horiz_map[piv_columns[i].rank] = i; /* * The display alignment depends on its PQftype(). */ col_align = column_type_alignment(PQftype(results, field_for_data)); for (i = 0; i < num_columns; i++) { char *colname; colname = piv_columns[horiz_map[i]].name ? piv_columns[horiz_map[i]].name : (popt.nullPrint ? popt.nullPrint : ""); printTableAddHeader(&cont, colname, false, col_align); } pg_free(horiz_map); /* Step 2: set row names in the first output column (vertical header) */ for (i = 0; i < num_rows; i++) { int k = piv_rows[i].rank; cont.cells[k * (num_columns + 1)] = piv_rows[i].name ? piv_rows[i].name : (popt.nullPrint ? popt.nullPrint : ""); } cont.cellsadded = num_rows * (num_columns + 1); /* * Step 3: fill in the content cells. */ for (rn = 0; rn < PQntuples(results); rn++) { int row_number; int col_number; pivot_field *p; pivot_field elt; /* Find target row */ if (!PQgetisnull(results, rn, field_for_rows)) elt.name = PQgetvalue(results, rn, field_for_rows); else elt.name = NULL; p = (pivot_field *) bsearch(&elt, piv_rows, num_rows, sizeof(pivot_field), pivotFieldCompare); Assert(p != NULL); row_number = p->rank; /* Find target column */ if (!PQgetisnull(results, rn, field_for_columns)) elt.name = PQgetvalue(results, rn, field_for_columns); else elt.name = NULL; p = (pivot_field *) bsearch(&elt, piv_columns, num_columns, sizeof(pivot_field), pivotFieldCompare); Assert(p != NULL); col_number = p->rank; /* Place value into cell */ if (col_number >= 0 && row_number >= 0) { int idx; /* index into the cont.cells array */ idx = 1 + col_number + row_number * (num_columns + 1); /* * If the cell already contains a value, raise an error. */ if (cont.cells[idx] != NULL) { psql_error(_("data cell already contains a value: (row: \"%s\", column: \"%s\")\n"), piv_rows[row_number].name ? piv_rows[row_number].name : popt.nullPrint ? popt.nullPrint : "(null)", piv_columns[col_number].name ? piv_columns[col_number].name : popt.nullPrint ? popt.nullPrint : "(null)"); goto error; } cont.cells[idx] = !PQgetisnull(results, rn, field_for_data) ? PQgetvalue(results, rn, field_for_data) : (popt.nullPrint ? popt.nullPrint : ""); } } /* * The non-initialized cells must be set to an empty string for the print * functions */ for (i = 0; i < cont.cellsadded; i++) { if (cont.cells[i] == NULL) cont.cells[i] = ""; } printTable(&cont, pset.queryFout, false, pset.logfile); retval = true; error: printTableCleanup(&cont); return retval; }
/* * 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(); }
int dp_free( char *gobname ) { return( pg_free( gobname ) ); }
/* * parallel_exec_prog * * This has the same API as exec_prog, except it does parallel execution, * and therefore must throw errors and doesn't return an error status. */ void parallel_exec_prog(const char *log_file, const char *opt_log_file, const char *fmt,...) { va_list args; char cmd[MAX_STRING]; #ifndef WIN32 pid_t child; #else HANDLE child; exec_thread_arg *new_arg; #endif va_start(args, fmt); vsnprintf(cmd, sizeof(cmd), fmt, args); va_end(args); if (user_opts.jobs <= 1) /* throw_error must be true to allow jobs */ exec_prog(log_file, opt_log_file, true, "%s", cmd); else { /* parallel */ #ifdef WIN32 if (thread_handles == NULL) thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE)); if (exec_thread_args == NULL) { int i; exec_thread_args = pg_malloc(user_opts.jobs * sizeof(exec_thread_arg *)); /* * For safety and performance, we keep the args allocated during * the entire life of the process, and we don't free the args in a * thread different from the one that allocated it. */ for (i = 0; i < user_opts.jobs; i++) exec_thread_args[i] = pg_malloc0(sizeof(exec_thread_arg)); } cur_thread_args = (void **) exec_thread_args; #endif /* harvest any dead children */ while (reap_child(false) == true) ; /* must we wait for a dead child? */ if (parallel_jobs >= user_opts.jobs) reap_child(true); /* set this before we start the job */ parallel_jobs++; /* Ensure stdio state is quiesced before forking */ fflush(NULL); #ifndef WIN32 child = fork(); if (child == 0) /* use _exit to skip atexit() functions */ _exit(!exec_prog(log_file, opt_log_file, true, "%s", cmd)); else if (child < 0) /* fork failed */ pg_fatal("could not create worker process: %s\n", strerror(errno)); #else /* empty array element are always at the end */ new_arg = exec_thread_args[parallel_jobs - 1]; /* Can only pass one pointer into the function, so use a struct */ if (new_arg->log_file) pg_free(new_arg->log_file); new_arg->log_file = pg_strdup(log_file); if (new_arg->opt_log_file) pg_free(new_arg->opt_log_file); new_arg->opt_log_file = opt_log_file ? pg_strdup(opt_log_file) : NULL; if (new_arg->cmd) pg_free(new_arg->cmd); new_arg->cmd = pg_strdup(cmd); child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog, new_arg, 0, NULL); if (child == 0) pg_fatal("could not create worker thread: %s\n", strerror(errno)); thread_handles[parallel_jobs - 1] = child; #endif } return; }
/* * 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(); }
/* * 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_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; 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 */ 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]); PQfinish(conn); } /* Allocate what's certainly enough space */ if (totaltups > 0) os_info.libraries = (char **) pg_malloc(totaltups * sizeof(char *)); else os_info.libraries = NULL; /* * Now remove duplicates across DBs. This is pretty inefficient code, but * there probably aren't enough entries to matter. */ totaltups = 0; 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); }
/* * 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(); }
/* * Main entry point to this module. * * Process the data from *res according to the options in pset (global), * to generate the horizontal and vertical headers contents, * then call printCrosstab() for the actual output. */ bool PrintResultsInCrosstab(const PGresult *res) { bool retval = false; avl_tree piv_columns; avl_tree piv_rows; pivot_field *array_columns = NULL; pivot_field *array_rows = NULL; int num_columns = 0; int num_rows = 0; int field_for_rows; int field_for_columns; int field_for_data; int sort_field_for_columns; int rn; avlInit(&piv_rows); avlInit(&piv_columns); if (PQresultStatus(res) != PGRES_TUPLES_OK) { psql_error("\\crosstabview: query must return results to be shown in crosstab\n"); goto error_return; } if (PQnfields(res) < 3) { psql_error("\\crosstabview: query must return at least three columns\n"); goto error_return; } /* Process first optional arg (vertical header column) */ if (pset.ctv_args[0] == NULL) field_for_rows = 0; else { field_for_rows = indexOfColumn(pset.ctv_args[0], res); if (field_for_rows < 0) goto error_return; } /* Process second optional arg (horizontal header column) */ if (pset.ctv_args[1] == NULL) field_for_columns = 1; else { field_for_columns = indexOfColumn(pset.ctv_args[1], res); if (field_for_columns < 0) goto error_return; } /* Insist that header columns be distinct */ if (field_for_columns == field_for_rows) { psql_error("\\crosstabview: vertical and horizontal headers must be different columns\n"); goto error_return; } /* Process third optional arg (data column) */ if (pset.ctv_args[2] == NULL) { int i; /* * If the data column was not specified, we search for the one not * used as either vertical or horizontal headers. Must be exactly * three columns, or this won't be unique. */ if (PQnfields(res) != 3) { psql_error("\\crosstabview: data column must be specified when query returns more than three columns\n"); goto error_return; } field_for_data = -1; for (i = 0; i < PQnfields(res); i++) { if (i != field_for_rows && i != field_for_columns) { field_for_data = i; break; } } Assert(field_for_data >= 0); } else { field_for_data = indexOfColumn(pset.ctv_args[2], res); if (field_for_data < 0) goto error_return; } /* Process fourth optional arg (horizontal header sort column) */ if (pset.ctv_args[3] == NULL) sort_field_for_columns = -1; /* no sort column */ else { sort_field_for_columns = indexOfColumn(pset.ctv_args[3], res); if (sort_field_for_columns < 0) goto error_return; } /* * First part: accumulate the names that go into the vertical and * horizontal headers, each into an AVL binary tree to build the set of * DISTINCT values. */ for (rn = 0; rn < PQntuples(res); rn++) { char *val; char *val1; /* horizontal */ val = PQgetisnull(res, rn, field_for_columns) ? NULL : PQgetvalue(res, rn, field_for_columns); val1 = NULL; if (sort_field_for_columns >= 0 && !PQgetisnull(res, rn, sort_field_for_columns)) val1 = PQgetvalue(res, rn, sort_field_for_columns); avlMergeValue(&piv_columns, val, val1); if (piv_columns.count > CROSSTABVIEW_MAX_COLUMNS) { psql_error("\\crosstabview: maximum number of columns (%d) exceeded\n", CROSSTABVIEW_MAX_COLUMNS); goto error_return; } /* vertical */ val = PQgetisnull(res, rn, field_for_rows) ? NULL : PQgetvalue(res, rn, field_for_rows); avlMergeValue(&piv_rows, val, NULL); } /* * Second part: Generate sorted arrays from the AVL trees. */ num_columns = piv_columns.count; num_rows = piv_rows.count; array_columns = (pivot_field *) pg_malloc(sizeof(pivot_field) * num_columns); array_rows = (pivot_field *) pg_malloc(sizeof(pivot_field) * num_rows); avlCollectFields(&piv_columns, piv_columns.root, array_columns, 0); avlCollectFields(&piv_rows, piv_rows.root, array_rows, 0); /* * Third part: optionally, process the ranking data for the horizontal * header */ if (sort_field_for_columns >= 0) rankSort(num_columns, array_columns); /* * Fourth part: print the crosstab'ed results. */ retval = printCrosstab(res, num_columns, array_columns, field_for_columns, num_rows, array_rows, field_for_rows, field_for_data); error_return: avlFree(&piv_columns, piv_columns.root); avlFree(&piv_rows, piv_rows.root); pg_free(array_columns); pg_free(array_rows); return retval; }
/* * 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); }
void pfree(void *pointer) { pg_free(pointer); }
int main(int argc, char **argv) { char *sequence_script_file_name = NULL; char *deletion_script_file_name = NULL; bool live_check = false; parseCommandLine(argc, argv); output_check_banner(&live_check); setup(argv[0], live_check); check_cluster_versions(); check_cluster_compatibility(live_check); check_old_cluster(live_check, &sequence_script_file_name); /* -- NEW -- */ start_postmaster(&new_cluster); check_new_cluster(); report_clusters_compatible(); pg_log(PG_REPORT, "\nPerforming Upgrade\n"); pg_log(PG_REPORT, "------------------\n"); disable_old_cluster(); prepare_new_cluster(); stop_postmaster(false); /* * Destructive Changes to New Cluster */ copy_clog_xlog_xid(); /* New now using xids of the old system */ /* -- NEW -- */ start_postmaster(&new_cluster); prepare_new_databases(); create_new_objects(); stop_postmaster(false); transfer_all_new_dbs(&old_cluster.dbarr, &new_cluster.dbarr, old_cluster.pgdata, new_cluster.pgdata); /* * Assuming OIDs are only used in system tables, there is no need to * restore the OID counter because we have not transferred any OIDs from * the old system, but we do it anyway just in case. We do it late here * because there is no need to have the schema load use new oids. */ prep_status("Setting next OID for new cluster"); exec_prog(true, SYSTEMQUOTE "\"%s/pg_resetxlog\" -o %u \"%s\" > " DEVNULL SYSTEMQUOTE, new_cluster.bindir, old_cluster.controldata.chkpnt_nxtoid, new_cluster.pgdata); check_ok(); create_script_for_old_cluster_deletion(&deletion_script_file_name); issue_warnings(sequence_script_file_name); pg_log(PG_REPORT, "\nUpgrade complete\n"); pg_log(PG_REPORT, "----------------\n"); output_completion_banner(deletion_script_file_name); pg_free(deletion_script_file_name); pg_free(sequence_script_file_name); cleanup(); return 0; }
/* * 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); } }
static int copy_file(const char *srcfile, const char *dstfile, bool force) { #define COPY_BUF_SIZE (50 * BLCKSZ) int src_fd; int dest_fd; char *buffer; int ret = 0; int save_errno = 0; if ((srcfile == NULL) || (dstfile == NULL)) { errno = EINVAL; return -1; } if ((src_fd = open(srcfile, O_RDONLY, 0)) < 0) return -1; if ((dest_fd = open(dstfile, O_RDWR | O_CREAT | (force ? 0 : O_EXCL), S_IRUSR | S_IWUSR)) < 0) { save_errno = errno; if (src_fd != 0) close(src_fd); errno = save_errno; return -1; } buffer = (char *) pg_malloc(COPY_BUF_SIZE); /* perform data copying i.e read src source, write to destination */ while (true) { ssize_t nbytes = read(src_fd, buffer, COPY_BUF_SIZE); if (nbytes < 0) { save_errno = errno; ret = -1; break; } if (nbytes == 0) break; errno = 0; if (write(dest_fd, buffer, nbytes) != nbytes) { /* if write didn't set errno, assume problem is no disk space */ if (errno == 0) errno = ENOSPC; save_errno = errno; ret = -1; break; } } pg_free(buffer); if (src_fd != 0) close(src_fd); if (dest_fd != 0) close(dest_fd); if (save_errno != 0) errno = save_errno; return ret; }
/* * transfer_all_new_dbs() * * Responsible for upgrading all database. invokes routines to generate mappings and then * physically link the databases. */ const char * transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata) { int old_dbnum, new_dbnum; const char *msg = NULL; prep_status("%s user relation files\n", user_opts.transfer_mode == TRANSFER_MODE_LINK ? "Linking" : "Copying"); /* Scan the old cluster databases and transfer their files */ for (old_dbnum = new_dbnum = 0; old_dbnum < old_db_arr->ndbs; old_dbnum++, new_dbnum++) { DbInfo *old_db = &old_db_arr->dbs[old_dbnum], *new_db = NULL; FileNameMap *mappings; int n_maps; pageCnvCtx *pageConverter = NULL; /* * Advance past any databases that exist in the new cluster but not in * the old, e.g. "postgres". (The user might have removed the * 'postgres' database from the old cluster.) */ for (; new_dbnum < new_db_arr->ndbs; new_dbnum++) { new_db = &new_db_arr->dbs[new_dbnum]; if (strcmp(old_db->db_name, new_db->db_name) == 0) break; } if (new_dbnum >= new_db_arr->ndbs) pg_log(PG_FATAL, "old database \"%s\" not found in the new cluster\n", old_db->db_name); n_maps = 0; mappings = gen_db_file_maps(old_db, new_db, &n_maps, old_pgdata, new_pgdata); if (n_maps) { print_maps(mappings, n_maps, new_db->db_name); #ifdef PAGE_CONVERSION msg = setupPageConverter(&pageConverter); #endif transfer_single_new_db(pageConverter, mappings, n_maps); pg_free(mappings); } } prep_status(" "); /* in case nothing printed; pass a space so * gcc doesn't complain about empty format * string */ check_ok(); return msg; }
/* * 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"); } }
/* * Make a database connection with the given parameters. * * A password can be given, but if not (or if user forces us to) we prompt * interactively for one, unless caller prohibited us from doing so. */ PGconn * connectDatabase(const char *dbname, const char *pghost, const char *pgport, const char *pguser, const char *pgpassword, enum trivalue prompt_password, const char *progname, bool fail_ok) { PGconn *conn; char *password; bool new_pass; password = pgpassword ? strdup(pgpassword) : NULL; if (prompt_password == TRI_YES && !pgpassword) password = simple_prompt("Password: "******"host"; values[0] = pghost; keywords[1] = "port"; values[1] = pgport; keywords[2] = "user"; values[2] = pguser; keywords[3] = "password"; values[3] = password; keywords[4] = "dbname"; values[4] = dbname; keywords[5] = "fallback_application_name"; values[5] = progname; keywords[6] = NULL; values[6] = NULL; new_pass = false; conn = PQconnectdbParams(keywords, values, true); if (!conn) { fprintf(stderr, _("%s: could not connect to database %s: out of memory\n"), progname, dbname); exit(1); } pg_free(keywords); pg_free(values); /* * No luck? Trying asking (again) for a password. */ if (PQstatus(conn) == CONNECTION_BAD && PQconnectionNeedsPassword(conn) && prompt_password != TRI_NO) { PQfinish(conn); if (password) free(password); password = simple_prompt("Password: "******"%s: could not connect to database %s: %s"), progname, dbname, PQerrorMessage(conn)); exit(1); } return conn; }
/* * parallel_transfer_all_new_dbs * * This has the same API as transfer_all_new_dbs, except it does parallel execution * by transfering multiple tablespaces in parallel */ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata, char *old_tablespace) { #ifndef WIN32 pid_t child; #else HANDLE child; transfer_thread_arg *new_arg; #endif if (user_opts.jobs <= 1) /* throw_error must be true to allow jobs */ transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, NULL); else { /* parallel */ #ifdef WIN32 if (thread_handles == NULL) thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE)); if (transfer_thread_args == NULL) { int i; transfer_thread_args = pg_malloc(user_opts.jobs * sizeof(transfer_thread_arg *)); /* * For safety and performance, we keep the args allocated during * the entire life of the process, and we don't free the args in a * thread different from the one that allocated it. */ for (i = 0; i < user_opts.jobs; i++) transfer_thread_args[i] = pg_malloc0(sizeof(transfer_thread_arg)); } cur_thread_args = (void **) transfer_thread_args; #endif /* harvest any dead children */ while (reap_child(false) == true) ; /* must we wait for a dead child? */ if (parallel_jobs >= user_opts.jobs) reap_child(true); /* set this before we start the job */ parallel_jobs++; /* Ensure stdio state is quiesced before forking */ fflush(NULL); #ifndef WIN32 child = fork(); if (child == 0) { transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, old_tablespace); /* if we take another exit path, it will be non-zero */ /* use _exit to skip atexit() functions */ _exit(0); } else if (child < 0) /* fork failed */ pg_fatal("could not create worker process: %s\n", strerror(errno)); #else /* empty array element are always at the end */ new_arg = transfer_thread_args[parallel_jobs - 1]; /* Can only pass one pointer into the function, so use a struct */ new_arg->old_db_arr = old_db_arr; new_arg->new_db_arr = new_db_arr; if (new_arg->old_pgdata) pg_free(new_arg->old_pgdata); new_arg->old_pgdata = pg_strdup(old_pgdata); if (new_arg->new_pgdata) pg_free(new_arg->new_pgdata); new_arg->new_pgdata = pg_strdup(new_pgdata); if (new_arg->old_tablespace) pg_free(new_arg->old_tablespace); new_arg->old_tablespace = old_tablespace ? pg_strdup(old_tablespace) : NULL; child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_transfer_all_new_dbs, new_arg, 0, NULL); if (child == 0) pg_fatal("could not create worker thread: %s\n", strerror(errno)); thread_handles[parallel_jobs - 1] = child; #endif } return; }
/* * 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_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; /* * 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 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 */ cluster->controldata.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 */ cluster->controldata.nxtlogseg = 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, "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); /* 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(PG_REPORT, "Some required control information is missing; cannot find:\n"); if (!got_xid) pg_log(PG_REPORT, " checkpoint next XID\n"); if (!got_oid) pg_log(PG_REPORT, " latest checkpoint next OID\n"); if (!live_check && !got_log_id) pg_log(PG_REPORT, " first log file ID after reset\n"); if (!live_check && !got_log_seg) pg_log(PG_REPORT, " first log file 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"); } }
/* * Main entry point to this module. * * Process the data from *res according the display options in pset (global), * to generate the horizontal and vertical headers contents, * then call printCrosstab() for the actual output. */ bool PrintResultsInCrosstab(const PGresult *res) { char *opt_field_for_rows = pset.ctv_col_V; char *opt_field_for_columns = pset.ctv_col_H; char *opt_field_for_data = pset.ctv_col_D; int rn; avl_tree piv_columns; avl_tree piv_rows; pivot_field *array_columns = NULL; pivot_field *array_rows = NULL; int num_columns = 0; int num_rows = 0; int *colsV = NULL, *colsH = NULL, *colsD = NULL; int n; int field_for_columns; int sort_field_for_columns = -1; int field_for_rows; int field_for_data = -1; bool retval = false; avlInit(&piv_rows); avlInit(&piv_columns); if (res == NULL) { psql_error(_("No result\n")); goto error_return; } if (PQresultStatus(res) != PGRES_TUPLES_OK) { psql_error(_("The query must return results to be shown in crosstab\n")); goto error_return; } if (opt_field_for_rows && !opt_field_for_columns) { psql_error(_("A second column must be specified for the horizontal header\n")); goto error_return; } if (PQnfields(res) <= 2) { psql_error(_("The query must return at least two columns to be shown in crosstab\n")); goto error_return; } /* * Arguments processing for the vertical header (1st arg) displayed in the * left-most column. Only a reference to a field is accepted (no sort * column). */ if (opt_field_for_rows == NULL) { field_for_rows = 0; } else { n = parseColumnRefs(opt_field_for_rows, res, &colsV, 1, ':'); if (n != 1) goto error_return; field_for_rows = colsV[0]; } if (field_for_rows < 0) goto error_return; /*---------- * Arguments processing for the horizontal header (2nd arg) * (pivoted column that gets displayed as the first row). * Determine: * - the field number for the horizontal header column * - the field number of the associated sort column, if any */ if (opt_field_for_columns == NULL) field_for_columns = 1; else { n = parseColumnRefs(opt_field_for_columns, res, &colsH, 2, ':'); if (n <= 0) goto error_return; if (n == 1) field_for_columns = colsH[0]; else { field_for_columns = colsH[0]; sort_field_for_columns = colsH[1]; } if (field_for_columns < 0) goto error_return; } if (field_for_columns == field_for_rows) { psql_error(_("The same column cannot be used for both vertical and horizontal headers\n")); goto error_return; } /* * Arguments processing for the data columns (3rd arg). Determine the * column to display in the grid. */ if (opt_field_for_data == NULL) { int i; /* * If the data column was not specified, we search for the one not * used as either vertical or horizontal headers. If the result has * more than three columns, raise an error. */ if (PQnfields(res) > 3) { psql_error(_("Data column must be specified when the result set has more than three columns\n")); goto error_return; } for (i = 0; i < PQnfields(res); i++) { if (i != field_for_rows && i != field_for_columns) { field_for_data = i; break; } } Assert(field_for_data >= 0); } else { int num_fields; /* If a field was given, find out what it is. Only one is allowed. */ num_fields = parseColumnRefs(opt_field_for_data, res, &colsD, 1, ','); if (num_fields < 1) goto error_return; field_for_data = colsD[0]; } /* * First part: accumulate the names that go into the vertical and * horizontal headers, each into an AVL binary tree to build the set of * DISTINCT values. */ for (rn = 0; rn < PQntuples(res); rn++) { char *val; char *val1; /* horizontal */ val = PQgetisnull(res, rn, field_for_columns) ? NULL : PQgetvalue(res, rn, field_for_columns); val1 = NULL; if (sort_field_for_columns >= 0 && !PQgetisnull(res, rn, sort_field_for_columns)) val1 = PQgetvalue(res, rn, sort_field_for_columns); avlMergeValue(&piv_columns, val, val1); if (piv_columns.count > CROSSTABVIEW_MAX_COLUMNS) { psql_error(_("Maximum number of columns (%d) exceeded\n"), CROSSTABVIEW_MAX_COLUMNS); goto error_return; } /* vertical */ val = PQgetisnull(res, rn, field_for_rows) ? NULL : PQgetvalue(res, rn, field_for_rows); avlMergeValue(&piv_rows, val, NULL); } /* * Second part: Generate sorted arrays from the AVL trees. */ num_columns = piv_columns.count; num_rows = piv_rows.count; array_columns = (pivot_field *) pg_malloc(sizeof(pivot_field) * num_columns); array_rows = (pivot_field *) pg_malloc(sizeof(pivot_field) * num_rows); avlCollectFields(&piv_columns, piv_columns.root, array_columns, 0); avlCollectFields(&piv_rows, piv_rows.root, array_rows, 0); /* * Third part: optionally, process the ranking data for the horizontal * header */ if (sort_field_for_columns >= 0) rankSort(num_columns, array_columns); /* * Fourth part: print the crosstab'ed results. */ retval = printCrosstab(res, num_columns, array_columns, field_for_columns, num_rows, array_rows, field_for_rows, field_for_data); error_return: avlFree(&piv_columns, piv_columns.root); avlFree(&piv_rows, piv_rows.root); pg_free(array_columns); pg_free(array_rows); pg_free(colsV); pg_free(colsH); pg_free(colsD); return retval; }
static void free_rel_infos(RelInfoArr *rel_arr) { pg_free(rel_arr->rels); rel_arr->nrels = 0; }
/* * Parse "arg", which is a string of column IDs separated by "separator". * * Each column ID can be: * - a number from 1 to PQnfields(res) * - an unquoted column name matching (case insensitively) one of PQfname(res,...) * - a quoted column name matching (case sensitively) one of PQfname(res,...) * * If max_columns > 0, it is the max number of column IDs allowed. * * On success, return number of column IDs found (possibly 0), and return a * malloc'd array of the matching column numbers of "res" into *col_numbers. * * On failure, return -1 and set *col_numbers to NULL. */ static int parseColumnRefs(const char *arg, const PGresult *res, int **col_numbers, int max_columns, char separator) { const char *p = arg; char c; int num_cols = 0; *col_numbers = NULL; while ((c = *p) != '\0') { const char *field_start = p; bool quoted_field = false; /* first char */ if (c == '"') { quoted_field = true; p++; } while ((c = *p) != '\0') { if (c == separator && !quoted_field) break; if (c == '"') /* end of field or embedded double quote */ { p++; if (*p == '"') { if (quoted_field) { p++; continue; } } else if (quoted_field && *p == separator) break; } if (*p) p += PQmblen(p, pset.encoding); } if (p != field_start) { char *col_name; int col_num; /* enforce max_columns limit */ if (max_columns > 0 && num_cols == max_columns) { psql_error(_("No more than %d column references expected\n"), max_columns); goto errfail; } /* look up the column and add its index into *col_numbers */ col_name = pg_malloc(p - field_start + 1); memcpy(col_name, field_start, p - field_start); col_name[p - field_start] = '\0'; col_num = indexOfColumn(col_name, res); pg_free(col_name); if (col_num < 0) goto errfail; *col_numbers = (int *) pg_realloc(*col_numbers, (num_cols + 1) * sizeof(int)); (*col_numbers)[num_cols++] = col_num; } else { psql_error(_("Empty column reference\n")); goto errfail; } if (*p) p += PQmblen(p, pset.encoding); } return num_cols; errfail: pg_free(*col_numbers); *col_numbers = NULL; return -1; }
/* * parseCommandLine() * * Parses the command line (argc, argv[]) and loads structures */ void parseCommandLine(int argc, char *argv[]) { static struct option long_options[] = { {"old-datadir", required_argument, NULL, 'd'}, {"new-datadir", required_argument, NULL, 'D'}, {"old-bindir", required_argument, NULL, 'b'}, {"new-bindir", required_argument, NULL, 'B'}, {"old-port", required_argument, NULL, 'p'}, {"new-port", required_argument, NULL, 'P'}, {"user", required_argument, NULL, 'u'}, {"check", no_argument, NULL, 'c'}, {"debug", no_argument, NULL, 'g'}, {"debugfile", required_argument, NULL, 'G'}, {"link", no_argument, NULL, 'k'}, {"logfile", required_argument, NULL, 'l'}, {"verbose", no_argument, NULL, 'v'}, {NULL, 0, NULL, 0} }; int option; /* Command line option */ int optindex = 0; /* used by getopt_long */ int os_user_effective_id; user_opts.transfer_mode = TRANSFER_MODE_COPY; os_info.progname = get_progname(argv[0]); /* Process libpq env. variables; load values here for usage() output */ old_cluster.port = getenv("PGPORTOLD") ? atoi(getenv("PGPORTOLD")) : DEF_PGUPORT; new_cluster.port = getenv("PGPORTNEW") ? atoi(getenv("PGPORTNEW")) : DEF_PGUPORT; os_user_effective_id = get_user_info(&os_info.user); /* we override just the database user name; we got the OS id above */ if (getenv("PGUSER")) { pg_free(os_info.user); /* must save value, getenv()'s pointer is not stable */ os_info.user = pg_strdup(getenv("PGUSER")); } if (argc > 1) { if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "-?") == 0) { usage(); exit(0); } if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0) { puts("pg_upgrade (PostgreSQL) " PG_VERSION); exit(0); } } /* Allow help and version to be run as root, so do the test here. */ if (os_user_effective_id == 0) pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname); getcwd(os_info.cwd, MAXPGPATH); while ((option = getopt_long(argc, argv, "d:D:b:B:cgG:kl:p:P:u:v", long_options, &optindex)) != -1) { switch (option) { case 'b': old_cluster.bindir = pg_strdup(optarg); break; case 'B': new_cluster.bindir = pg_strdup(optarg); break; case 'c': user_opts.check = true; break; case 'd': old_cluster.pgdata = pg_strdup(optarg); break; case 'D': new_cluster.pgdata = pg_strdup(optarg); break; case 'g': pg_log(PG_REPORT, "Running in debug mode\n"); log_opts.debug = true; break; case 'G': if ((log_opts.debug_fd = fopen(optarg, "w")) == NULL) { pg_log(PG_FATAL, "cannot open debug file\n"); exit(1); } break; case 'k': user_opts.transfer_mode = TRANSFER_MODE_LINK; break; case 'l': log_opts.filename = pg_strdup(optarg); break; case 'p': if ((old_cluster.port = atoi(optarg)) <= 0) { pg_log(PG_FATAL, "invalid old port number\n"); exit(1); } break; case 'P': if ((new_cluster.port = atoi(optarg)) <= 0) { pg_log(PG_FATAL, "invalid new port number\n"); exit(1); } break; case 'u': pg_free(os_info.user); os_info.user = pg_strdup(optarg); /* * Push the user name into the environment so pre-9.1 * pg_ctl/libpq uses it. */ pg_putenv("PGUSER", os_info.user); break; case 'v': pg_log(PG_REPORT, "Running in verbose mode\n"); log_opts.verbose = true; break; default: pg_log(PG_FATAL, "Try \"%s --help\" for more information.\n", os_info.progname); break; } } if (log_opts.filename != NULL) { /* * We must use append mode so output generated by child processes via * ">>" will not be overwritten, and we want the file truncated on * start. */ /* truncate */ if ((log_opts.fd = fopen(log_opts.filename, "w")) == NULL) pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename); fclose(log_opts.fd); if ((log_opts.fd = fopen(log_opts.filename, "a")) == NULL) pg_log(PG_FATAL, "cannot write to log file %s\n", log_opts.filename); } else log_opts.filename = strdup(DEVNULL); /* if no debug file name, output to the terminal */ if (log_opts.debug && !log_opts.debug_fd) { log_opts.debug_fd = fopen(DEVTTY, "w"); if (!log_opts.debug_fd) pg_log(PG_FATAL, "cannot write to terminal\n"); } /* Get values from env if not already set */ check_required_directory(&old_cluster.bindir, "PGBINOLD", "-b", "old cluster binaries reside"); check_required_directory(&new_cluster.bindir, "PGBINNEW", "-B", "new cluster binaries reside"); check_required_directory(&old_cluster.pgdata, "PGDATAOLD", "-d", "old cluster data resides"); check_required_directory(&new_cluster.pgdata, "PGDATANEW", "-D", "new cluster data resides"); }