Header And Logo

PostgreSQL
| The world's most advanced open source database.

check.c

Go to the documentation of this file.
00001 /*
00002  *  check.c
00003  *
00004  *  server checks and output routines
00005  *
00006  *  Copyright (c) 2010-2013, PostgreSQL Global Development Group
00007  *  contrib/pg_upgrade/check.c
00008  */
00009 
00010 #include "postgres_fe.h"
00011 
00012 #include "pg_upgrade.h"
00013 
00014 
00015 static void set_locale_and_encoding(ClusterInfo *cluster);
00016 static void check_new_cluster_is_empty(void);
00017 static void check_locale_and_encoding(ControlData *oldctrl,
00018                           ControlData *newctrl);
00019 static void check_is_super_user(ClusterInfo *cluster);
00020 static void check_for_prepared_transactions(ClusterInfo *cluster);
00021 static void check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster);
00022 static void check_for_reg_data_type_usage(ClusterInfo *cluster);
00023 static void get_bin_version(ClusterInfo *cluster);
00024 static char *get_canonical_locale_name(int category, const char *locale);
00025 
00026 
00027 /*
00028  * fix_path_separator
00029  * For non-Windows, just return the argument.
00030  * For Windows convert any forward slash to a backslash
00031  * such as is suitable for arguments to builtin commands 
00032  * like RMDIR and DEL.
00033  */
00034 static char *
00035 fix_path_separator(char *path)
00036 {
00037 #ifdef WIN32
00038 
00039     char *result;
00040     char *c;
00041 
00042     result = pg_strdup(path);
00043 
00044     for (c = result; *c != '\0'; c++)
00045         if (*c == '/')
00046             *c = '\\';
00047 
00048     return result;
00049 
00050 #else
00051 
00052     return path;
00053 
00054 #endif
00055 }
00056 
00057 void
00058 output_check_banner(bool live_check)
00059 {
00060     if (user_opts.check && live_check)
00061     {
00062         pg_log(PG_REPORT, "Performing Consistency Checks on Old Live Server\n");
00063         pg_log(PG_REPORT, "------------------------------------------------\n");
00064     }
00065     else
00066     {
00067         pg_log(PG_REPORT, "Performing Consistency Checks\n");
00068         pg_log(PG_REPORT, "-----------------------------\n");
00069     }
00070 }
00071 
00072 
00073 void
00074 check_and_dump_old_cluster(bool live_check, char **sequence_script_file_name)
00075 {
00076     /* -- OLD -- */
00077 
00078     if (!live_check)
00079         start_postmaster(&old_cluster, true);
00080 
00081     set_locale_and_encoding(&old_cluster);
00082 
00083     get_pg_database_relfilenode(&old_cluster);
00084 
00085     /* Extract a list of databases and tables from the old cluster */
00086     get_db_and_rel_infos(&old_cluster);
00087 
00088     init_tablespaces();
00089 
00090     get_loadable_libraries();
00091 
00092 
00093     /*
00094      * Check for various failure cases
00095      */
00096     check_is_super_user(&old_cluster);
00097     check_for_prepared_transactions(&old_cluster);
00098     check_for_reg_data_type_usage(&old_cluster);
00099     check_for_isn_and_int8_passing_mismatch(&old_cluster);
00100 
00101     /* old = PG 8.3 checks? */
00102     if (GET_MAJOR_VERSION(old_cluster.major_version) <= 803)
00103     {
00104         old_8_3_check_for_name_data_type_usage(&old_cluster);
00105         old_8_3_check_for_tsquery_usage(&old_cluster);
00106         old_8_3_check_ltree_usage(&old_cluster);
00107         if (user_opts.check)
00108         {
00109             old_8_3_rebuild_tsvector_tables(&old_cluster, true);
00110             old_8_3_invalidate_hash_gin_indexes(&old_cluster, true);
00111             old_8_3_invalidate_bpchar_pattern_ops_indexes(&old_cluster, true);
00112         }
00113         else
00114 
00115             /*
00116              * While we have the old server running, create the script to
00117              * properly restore its sequence values but we report this at the
00118              * end.
00119              */
00120             *sequence_script_file_name =
00121                 old_8_3_create_sequence_script(&old_cluster);
00122     }
00123 
00124     /* Pre-PG 9.0 had no large object permissions */
00125     if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804)
00126         new_9_0_populate_pg_largeobject_metadata(&old_cluster, true);
00127 
00128     /*
00129      * While not a check option, we do this now because this is the only time
00130      * the old server is running.
00131      */
00132     if (!user_opts.check)
00133         generate_old_dump();
00134 
00135     if (!live_check)
00136         stop_postmaster(false);
00137 }
00138 
00139 
00140 void
00141 check_new_cluster(void)
00142 {
00143     set_locale_and_encoding(&new_cluster);
00144 
00145     check_locale_and_encoding(&old_cluster.controldata, &new_cluster.controldata);
00146 
00147     get_db_and_rel_infos(&new_cluster);
00148 
00149     check_new_cluster_is_empty();
00150 
00151     check_loadable_libraries();
00152 
00153     if (user_opts.transfer_mode == TRANSFER_MODE_LINK)
00154         check_hard_link();
00155 
00156     check_is_super_user(&new_cluster);
00157 
00158     /*
00159      *  We don't restore our own user, so both clusters must match have
00160      *  matching install-user oids.
00161      */
00162     if (old_cluster.install_role_oid != new_cluster.install_role_oid)
00163         pg_log(PG_FATAL,
00164         "Old and new cluster install users have different values for pg_authid.oid.\n");
00165 
00166     /*
00167      *  We only allow the install user in the new cluster because other
00168      *  defined users might match users defined in the old cluster and
00169      *  generate an error during pg_dump restore.
00170      */
00171     if (new_cluster.role_count != 1)
00172         pg_log(PG_FATAL, "Only the install user can be defined in the new cluster.\n");
00173     
00174     check_for_prepared_transactions(&new_cluster);
00175 }
00176 
00177 
00178 void
00179 report_clusters_compatible(void)
00180 {
00181     if (user_opts.check)
00182     {
00183         pg_log(PG_REPORT, "\n*Clusters are compatible*\n");
00184         /* stops new cluster */
00185         stop_postmaster(false);
00186         exit(0);
00187     }
00188 
00189     pg_log(PG_REPORT, "\n"
00190            "If pg_upgrade fails after this point, you must re-initdb the\n"
00191            "new cluster before continuing.\n");
00192 }
00193 
00194 
00195 void
00196 issue_warnings(char *sequence_script_file_name)
00197 {
00198     /* old = PG 8.3 warnings? */
00199     if (GET_MAJOR_VERSION(old_cluster.major_version) <= 803)
00200     {
00201         start_postmaster(&new_cluster, true);
00202 
00203         /* restore proper sequence values using file created from old server */
00204         if (sequence_script_file_name)
00205         {
00206             prep_status("Adjusting sequences");
00207             exec_prog(UTILITY_LOG_FILE, NULL, true,
00208                       "\"%s/psql\" " EXEC_PSQL_ARGS " %s -f \"%s\"",
00209                       new_cluster.bindir, cluster_conn_opts(&new_cluster),
00210                       sequence_script_file_name);
00211             unlink(sequence_script_file_name);
00212             check_ok();
00213         }
00214 
00215         old_8_3_rebuild_tsvector_tables(&new_cluster, false);
00216         old_8_3_invalidate_hash_gin_indexes(&new_cluster, false);
00217         old_8_3_invalidate_bpchar_pattern_ops_indexes(&new_cluster, false);
00218         stop_postmaster(false);
00219     }
00220 
00221     /* Create dummy large object permissions for old < PG 9.0? */
00222     if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804)
00223     {
00224         start_postmaster(&new_cluster, true);
00225         new_9_0_populate_pg_largeobject_metadata(&new_cluster, false);
00226         stop_postmaster(false);
00227     }
00228 }
00229 
00230 
00231 void
00232 output_completion_banner(char *analyze_script_file_name,
00233                          char *deletion_script_file_name)
00234 {
00235     /* Did we copy the free space files? */
00236     if (GET_MAJOR_VERSION(old_cluster.major_version) >= 804)
00237         pg_log(PG_REPORT,
00238                "Optimizer statistics are not transferred by pg_upgrade so,\n"
00239                "once you start the new server, consider running:\n"
00240                "    %s\n\n", analyze_script_file_name);
00241     else
00242         pg_log(PG_REPORT,
00243                "Optimizer statistics and free space information are not transferred\n"
00244         "by pg_upgrade so, once you start the new server, consider running:\n"
00245                "    %s\n\n", analyze_script_file_name);
00246 
00247 
00248     if (deletion_script_file_name)
00249         pg_log(PG_REPORT,
00250                "Running this script will delete the old cluster's data files:\n"
00251                "    %s\n",
00252                deletion_script_file_name);
00253     else
00254         pg_log(PG_REPORT,
00255                "Could not create a script to delete the old cluster's data\n"
00256                "files because user-defined tablespaces exist in the old cluster\n"
00257                "directory.  The old cluster's contents must be deleted manually.\n");
00258 }
00259 
00260 
00261 void
00262 check_cluster_versions(void)
00263 {
00264     prep_status("Checking cluster versions");
00265 
00266     /* get old and new cluster versions */
00267     old_cluster.major_version = get_major_server_version(&old_cluster);
00268     new_cluster.major_version = get_major_server_version(&new_cluster);
00269 
00270     /*
00271      * We allow upgrades from/to the same major version for alpha/beta
00272      * upgrades
00273      */
00274 
00275     if (GET_MAJOR_VERSION(old_cluster.major_version) < 803)
00276         pg_log(PG_FATAL, "This utility can only upgrade from PostgreSQL version 8.3 and later.\n");
00277 
00278     /* Only current PG version is supported as a target */
00279     if (GET_MAJOR_VERSION(new_cluster.major_version) != GET_MAJOR_VERSION(PG_VERSION_NUM))
00280         pg_log(PG_FATAL, "This utility can only upgrade to PostgreSQL version %s.\n",
00281                PG_MAJORVERSION);
00282 
00283     /*
00284      * We can't allow downgrading because we use the target pg_dumpall, and
00285      * pg_dumpall cannot operate on new database versions, only older
00286      * versions.
00287      */
00288     if (old_cluster.major_version > new_cluster.major_version)
00289         pg_log(PG_FATAL, "This utility cannot be used to downgrade to older major PostgreSQL versions.\n");
00290 
00291     /* get old and new binary versions */
00292     get_bin_version(&old_cluster);
00293     get_bin_version(&new_cluster);
00294 
00295     /* Ensure binaries match the designated data directories */
00296     if (GET_MAJOR_VERSION(old_cluster.major_version) !=
00297         GET_MAJOR_VERSION(old_cluster.bin_version))
00298         pg_log(PG_FATAL,
00299                "Old cluster data and binary directories are from different major versions.\n");
00300     if (GET_MAJOR_VERSION(new_cluster.major_version) !=
00301         GET_MAJOR_VERSION(new_cluster.bin_version))
00302         pg_log(PG_FATAL,
00303                "New cluster data and binary directories are from different major versions.\n");
00304 
00305     check_ok();
00306 }
00307 
00308 
00309 void
00310 check_cluster_compatibility(bool live_check)
00311 {
00312     /* get/check pg_control data of servers */
00313     get_control_data(&old_cluster, live_check);
00314     get_control_data(&new_cluster, false);
00315     check_control_data(&old_cluster.controldata, &new_cluster.controldata);
00316 
00317     /* Is it 9.0 but without tablespace directories? */
00318     if (GET_MAJOR_VERSION(new_cluster.major_version) == 900 &&
00319         new_cluster.controldata.cat_ver < TABLE_SPACE_SUBDIRS_CAT_VER)
00320         pg_log(PG_FATAL, "This utility can only upgrade to PostgreSQL version 9.0 after 2010-01-11\n"
00321                "because of backend API changes made during development.\n");
00322 
00323     /* We read the real port number for PG >= 9.1 */
00324     if (live_check && GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
00325         old_cluster.port == DEF_PGUPORT)
00326             pg_log(PG_FATAL, "When checking a pre-PG 9.1 live old server, "
00327                    "you must specify the old server's port number.\n");
00328 
00329     if (live_check && old_cluster.port == new_cluster.port)
00330         pg_log(PG_FATAL, "When checking a live server, "
00331                "the old and new port numbers must be different.\n");
00332 }
00333 
00334 
00335 /*
00336  * set_locale_and_encoding()
00337  *
00338  * query the database to get the template0 locale
00339  */
00340 static void
00341 set_locale_and_encoding(ClusterInfo *cluster)
00342 {
00343     ControlData *ctrl = &cluster->controldata;
00344     PGconn     *conn;
00345     PGresult   *res;
00346     int         i_encoding;
00347     int         cluster_version = cluster->major_version;
00348 
00349     conn = connectToServer(cluster, "template1");
00350 
00351     /* for pg < 80400, we got the values from pg_controldata */
00352     if (cluster_version >= 80400)
00353     {
00354         int         i_datcollate;
00355         int         i_datctype;
00356 
00357         res = executeQueryOrDie(conn,
00358                                 "SELECT datcollate, datctype "
00359                                 "FROM   pg_catalog.pg_database "
00360                                 "WHERE  datname = 'template0' ");
00361         assert(PQntuples(res) == 1);
00362 
00363         i_datcollate = PQfnumber(res, "datcollate");
00364         i_datctype = PQfnumber(res, "datctype");
00365 
00366         if (GET_MAJOR_VERSION(cluster->major_version) < 902)
00367         {
00368             /*
00369              *  Pre-9.2 did not canonicalize the supplied locale names
00370              *  to match what the system returns, while 9.2+ does, so
00371              *  convert pre-9.2 to match.
00372              */
00373             ctrl->lc_collate = get_canonical_locale_name(LC_COLLATE,
00374                                pg_strdup(PQgetvalue(res, 0, i_datcollate)));
00375             ctrl->lc_ctype = get_canonical_locale_name(LC_CTYPE,
00376                                pg_strdup(PQgetvalue(res, 0, i_datctype)));
00377         }
00378         else
00379         {
00380             ctrl->lc_collate = pg_strdup(PQgetvalue(res, 0, i_datcollate));
00381             ctrl->lc_ctype = pg_strdup(PQgetvalue(res, 0, i_datctype));
00382         }
00383 
00384         PQclear(res);
00385     }
00386 
00387     res = executeQueryOrDie(conn,
00388                             "SELECT pg_catalog.pg_encoding_to_char(encoding) "
00389                             "FROM   pg_catalog.pg_database "
00390                             "WHERE  datname = 'template0' ");
00391     assert(PQntuples(res) == 1);
00392 
00393     i_encoding = PQfnumber(res, "pg_encoding_to_char");
00394     ctrl->encoding = pg_strdup(PQgetvalue(res, 0, i_encoding));
00395 
00396     PQclear(res);
00397 
00398     PQfinish(conn);
00399 }
00400 
00401 
00402 /*
00403  * check_locale_and_encoding()
00404  *
00405  *  locale is not in pg_controldata in 8.4 and later so
00406  *  we probably had to get via a database query.
00407  */
00408 static void
00409 check_locale_and_encoding(ControlData *oldctrl,
00410                           ControlData *newctrl)
00411 {
00412     /*
00413      *  These are often defined with inconsistent case, so use pg_strcasecmp().
00414      *  They also often use inconsistent hyphenation, which we cannot fix, e.g.
00415      *  UTF-8 vs. UTF8, so at least we display the mismatching values.
00416      */
00417     if (pg_strcasecmp(oldctrl->lc_collate, newctrl->lc_collate) != 0)
00418         pg_log(PG_FATAL,
00419                "lc_collate cluster values do not match:  old \"%s\", new \"%s\"\n",
00420                oldctrl->lc_collate, newctrl->lc_collate);
00421     if (pg_strcasecmp(oldctrl->lc_ctype, newctrl->lc_ctype) != 0)
00422         pg_log(PG_FATAL,
00423                "lc_ctype cluster values do not match:  old \"%s\", new \"%s\"\n",
00424                oldctrl->lc_ctype, newctrl->lc_ctype);
00425     if (pg_strcasecmp(oldctrl->encoding, newctrl->encoding) != 0)
00426         pg_log(PG_FATAL,
00427                "encoding cluster values do not match:  old \"%s\", new \"%s\"\n",
00428                oldctrl->encoding, newctrl->encoding);
00429 }
00430 
00431 
00432 static void
00433 check_new_cluster_is_empty(void)
00434 {
00435     int         dbnum;
00436 
00437     for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
00438     {
00439         int         relnum;
00440         RelInfoArr *rel_arr = &new_cluster.dbarr.dbs[dbnum].rel_arr;
00441 
00442         for (relnum = 0; relnum < rel_arr->nrels;
00443              relnum++)
00444         {
00445             /* pg_largeobject and its index should be skipped */
00446             if (strcmp(rel_arr->rels[relnum].nspname, "pg_catalog") != 0)
00447                 pg_log(PG_FATAL, "New cluster database \"%s\" is not empty\n",
00448                        new_cluster.dbarr.dbs[dbnum].db_name);
00449         }
00450     }
00451 
00452 }
00453 
00454 
00455 /*
00456  * create_script_for_cluster_analyze()
00457  *
00458  *  This incrementally generates better optimizer statistics
00459  */
00460 void
00461 create_script_for_cluster_analyze(char **analyze_script_file_name)
00462 {
00463     FILE       *script = NULL;
00464 
00465     *analyze_script_file_name = pg_malloc(MAXPGPATH);
00466 
00467     prep_status("Creating script to analyze new cluster");
00468 
00469     snprintf(*analyze_script_file_name, MAXPGPATH, "analyze_new_cluster.%s",
00470              SCRIPT_EXT);
00471 
00472     if ((script = fopen_priv(*analyze_script_file_name, "w")) == NULL)
00473         pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
00474                *analyze_script_file_name, getErrorText(errno));
00475 
00476 #ifndef WIN32
00477     /* add shebang header */
00478     fprintf(script, "#!/bin/sh\n\n");
00479 #else
00480     /* suppress command echoing */
00481     fprintf(script, "@echo off\n");
00482 #endif
00483 
00484     fprintf(script, "echo %sThis script will generate minimal optimizer statistics rapidly%s\n",
00485             ECHO_QUOTE, ECHO_QUOTE);
00486     fprintf(script, "echo %sso your system is usable, and then gather statistics twice more%s\n",
00487             ECHO_QUOTE, ECHO_QUOTE);
00488     fprintf(script, "echo %swith increasing accuracy.  When it is done, your system will%s\n",
00489             ECHO_QUOTE, ECHO_QUOTE);
00490     fprintf(script, "echo %shave the default level of optimizer statistics.%s\n",
00491             ECHO_QUOTE, ECHO_QUOTE);
00492     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00493 
00494     fprintf(script, "echo %sIf you have used ALTER TABLE to modify the statistics target for%s\n",
00495             ECHO_QUOTE, ECHO_QUOTE);
00496     fprintf(script, "echo %sany tables, you might want to remove them and restore them after%s\n",
00497             ECHO_QUOTE, ECHO_QUOTE);
00498     fprintf(script, "echo %srunning this script because they will delay fast statistics generation.%s\n",
00499             ECHO_QUOTE, ECHO_QUOTE);
00500     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00501 
00502     fprintf(script, "echo %sIf you would like default statistics as quickly as possible, cancel%s\n",
00503             ECHO_QUOTE, ECHO_QUOTE);
00504     fprintf(script, "echo %sthis script and run:%s\n",
00505             ECHO_QUOTE, ECHO_QUOTE);
00506     fprintf(script, "echo %s    \"%s/vacuumdb\" --all %s%s\n", ECHO_QUOTE, new_cluster.bindir,
00507     /* Did we copy the free space files? */
00508             (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ?
00509             "--analyze-only" : "--analyze", ECHO_QUOTE);
00510     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00511 
00512 #ifndef WIN32
00513     fprintf(script, "sleep 2\n");
00514     fprintf(script, "PGOPTIONS='-c default_statistics_target=1 -c vacuum_cost_delay=0'\n");
00515     /* only need to export once */
00516     fprintf(script, "export PGOPTIONS\n");
00517 #else
00518     fprintf(script, "REM simulate sleep 2\n");
00519     fprintf(script, "PING 1.1.1.1 -n 1 -w 2000 > nul\n");
00520     fprintf(script, "SET PGOPTIONS=-c default_statistics_target=1 -c vacuum_cost_delay=0\n");
00521 #endif
00522 
00523     fprintf(script, "echo %sGenerating minimal optimizer statistics (1 target)%s\n",
00524             ECHO_QUOTE, ECHO_QUOTE);
00525     fprintf(script, "echo %s--------------------------------------------------%s\n",
00526             ECHO_QUOTE, ECHO_QUOTE);
00527     fprintf(script, "\"%s/vacuumdb\" --all --analyze-only\n", new_cluster.bindir);
00528     fprintf(script, "echo%s\n", ECHO_BLANK);
00529     fprintf(script, "echo %sThe server is now available with minimal optimizer statistics.%s\n",
00530             ECHO_QUOTE, ECHO_QUOTE);
00531     fprintf(script, "echo %sQuery performance will be optimal once this script completes.%s\n",
00532             ECHO_QUOTE, ECHO_QUOTE);
00533     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00534 
00535 #ifndef WIN32
00536     fprintf(script, "sleep 2\n");
00537     fprintf(script, "PGOPTIONS='-c default_statistics_target=10'\n");
00538 #else
00539     fprintf(script, "REM simulate sleep\n");
00540     fprintf(script, "PING 1.1.1.1 -n 1 -w 2000 > nul\n");
00541     fprintf(script, "SET PGOPTIONS=-c default_statistics_target=10\n");
00542 #endif
00543 
00544     fprintf(script, "echo %sGenerating medium optimizer statistics (10 targets)%s\n",
00545             ECHO_QUOTE, ECHO_QUOTE);
00546     fprintf(script, "echo %s---------------------------------------------------%s\n",
00547             ECHO_QUOTE, ECHO_QUOTE);
00548     fprintf(script, "\"%s/vacuumdb\" --all --analyze-only\n", new_cluster.bindir);
00549     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00550 
00551 #ifndef WIN32
00552     fprintf(script, "unset PGOPTIONS\n");
00553 #else
00554     fprintf(script, "SET PGOPTIONS\n");
00555 #endif
00556 
00557     fprintf(script, "echo %sGenerating default (full) optimizer statistics (100 targets?)%s\n",
00558             ECHO_QUOTE, ECHO_QUOTE);
00559     fprintf(script, "echo %s-------------------------------------------------------------%s\n",
00560             ECHO_QUOTE, ECHO_QUOTE);
00561     fprintf(script, "\"%s/vacuumdb\" --all %s\n", new_cluster.bindir,
00562     /* Did we copy the free space files? */
00563             (GET_MAJOR_VERSION(old_cluster.major_version) >= 804) ?
00564             "--analyze-only" : "--analyze");
00565 
00566     fprintf(script, "echo%s\n\n", ECHO_BLANK);
00567     fprintf(script, "echo %sDone%s\n",
00568             ECHO_QUOTE, ECHO_QUOTE);
00569 
00570     fclose(script);
00571 
00572 #ifndef WIN32
00573     if (chmod(*analyze_script_file_name, S_IRWXU) != 0)
00574         pg_log(PG_FATAL, "Could not add execute permission to file \"%s\": %s\n",
00575                *analyze_script_file_name, getErrorText(errno));
00576 #endif
00577 
00578     check_ok();
00579 }
00580 
00581 
00582 /*
00583  * create_script_for_old_cluster_deletion()
00584  *
00585  *  This is particularly useful for tablespace deletion.
00586  */
00587 void
00588 create_script_for_old_cluster_deletion(char **deletion_script_file_name)
00589 {
00590     FILE       *script = NULL;
00591     int         tblnum;
00592     char        old_cluster_pgdata[MAXPGPATH];
00593 
00594     *deletion_script_file_name = pg_malloc(MAXPGPATH);
00595 
00596     snprintf(*deletion_script_file_name, MAXPGPATH, "delete_old_cluster.%s",
00597              SCRIPT_EXT);
00598 
00599     /*
00600      *  Some users (oddly) create tablespaces inside the cluster data
00601      *  directory.  We can't create a proper old cluster delete script
00602      *  in that case.
00603      */
00604     strlcpy(old_cluster_pgdata, old_cluster.pgdata, MAXPGPATH);
00605     canonicalize_path(old_cluster_pgdata);
00606     for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
00607     {
00608         char        old_tablespace_dir[MAXPGPATH];
00609         
00610         strlcpy(old_tablespace_dir, os_info.old_tablespaces[tblnum], MAXPGPATH);
00611         canonicalize_path(old_tablespace_dir);
00612         if (path_is_prefix_of_path(old_cluster_pgdata, old_tablespace_dir))
00613         {
00614             /* Unlink file in case it is left over from a previous run. */
00615             unlink(*deletion_script_file_name);
00616             pg_free(*deletion_script_file_name);
00617             *deletion_script_file_name = NULL;
00618             return;
00619         }
00620     }
00621 
00622     prep_status("Creating script to delete old cluster");
00623 
00624     if ((script = fopen_priv(*deletion_script_file_name, "w")) == NULL)
00625         pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
00626                *deletion_script_file_name, getErrorText(errno));
00627 
00628 #ifndef WIN32
00629     /* add shebang header */
00630     fprintf(script, "#!/bin/sh\n\n");
00631 #endif
00632 
00633     /* delete old cluster's default tablespace */
00634     fprintf(script, RMDIR_CMD " %s\n", fix_path_separator(old_cluster.pgdata));
00635 
00636     /* delete old cluster's alternate tablespaces */
00637     for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++)
00638     {
00639         /*
00640          * Do the old cluster's per-database directories share a directory
00641          * with a new version-specific tablespace?
00642          */
00643         if (strlen(old_cluster.tablespace_suffix) == 0)
00644         {
00645             /* delete per-database directories */
00646             int         dbnum;
00647 
00648             fprintf(script, "\n");
00649             /* remove PG_VERSION? */
00650             if (GET_MAJOR_VERSION(old_cluster.major_version) <= 804)
00651                 fprintf(script, RM_CMD " %s%s%cPG_VERSION\n",
00652                         fix_path_separator(os_info.old_tablespaces[tblnum]), 
00653                         fix_path_separator(old_cluster.tablespace_suffix),
00654                         PATH_SEPARATOR);
00655 
00656             for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
00657             {
00658                 fprintf(script, RMDIR_CMD " %s%s%c%d\n",
00659                         fix_path_separator(os_info.old_tablespaces[tblnum]),
00660                         fix_path_separator(old_cluster.tablespace_suffix),
00661                         PATH_SEPARATOR, old_cluster.dbarr.dbs[dbnum].db_oid);
00662             }
00663         }
00664         else
00665 
00666             /*
00667              * Simply delete the tablespace directory, which might be ".old"
00668              * or a version-specific subdirectory.
00669              */
00670             fprintf(script, RMDIR_CMD " %s%s\n",
00671                     fix_path_separator(os_info.old_tablespaces[tblnum]), 
00672                     fix_path_separator(old_cluster.tablespace_suffix));
00673     }
00674 
00675     fclose(script);
00676 
00677 #ifndef WIN32
00678     if (chmod(*deletion_script_file_name, S_IRWXU) != 0)
00679         pg_log(PG_FATAL, "Could not add execute permission to file \"%s\": %s\n",
00680                *deletion_script_file_name, getErrorText(errno));
00681 #endif
00682 
00683     check_ok();
00684 }
00685 
00686 
00687 /*
00688  *  check_is_super_user()
00689  *
00690  *  Check we are superuser, and out user id and user count
00691  */
00692 static void
00693 check_is_super_user(ClusterInfo *cluster)
00694 {
00695     PGresult   *res;
00696     PGconn     *conn = connectToServer(cluster, "template1");
00697 
00698     prep_status("Checking database user is a superuser");
00699 
00700     /* Can't use pg_authid because only superusers can view it. */
00701     res = executeQueryOrDie(conn,
00702                             "SELECT rolsuper, oid "
00703                             "FROM pg_catalog.pg_roles "
00704                             "WHERE rolname = current_user");
00705 
00706     if (PQntuples(res) != 1 || strcmp(PQgetvalue(res, 0, 0), "t") != 0)
00707         pg_log(PG_FATAL, "database user \"%s\" is not a superuser\n",
00708                os_info.user);
00709 
00710     cluster->install_role_oid = atooid(PQgetvalue(res, 0, 1));
00711 
00712     PQclear(res);
00713 
00714     res = executeQueryOrDie(conn,
00715                             "SELECT COUNT(*) "
00716                             "FROM pg_catalog.pg_roles ");
00717 
00718     if (PQntuples(res) != 1)
00719         pg_log(PG_FATAL, "could not determine the number of users\n");
00720 
00721     cluster->role_count = atoi(PQgetvalue(res, 0, 0));
00722 
00723     PQclear(res);
00724 
00725     PQfinish(conn);
00726 
00727     check_ok();
00728 }
00729 
00730 
00731 /*
00732  *  check_for_prepared_transactions()
00733  *
00734  *  Make sure there are no prepared transactions because the storage format
00735  *  might have changed.
00736  */
00737 static void
00738 check_for_prepared_transactions(ClusterInfo *cluster)
00739 {
00740     PGresult   *res;
00741     PGconn     *conn = connectToServer(cluster, "template1");
00742 
00743     prep_status("Checking for prepared transactions");
00744 
00745     res = executeQueryOrDie(conn,
00746                             "SELECT * "
00747                             "FROM pg_catalog.pg_prepared_xacts");
00748 
00749     if (PQntuples(res) != 0)
00750         pg_log(PG_FATAL, "The %s cluster contains prepared transactions\n",
00751                CLUSTER_NAME(cluster));
00752 
00753     PQclear(res);
00754 
00755     PQfinish(conn);
00756 
00757     check_ok();
00758 }
00759 
00760 
00761 /*
00762  *  check_for_isn_and_int8_passing_mismatch()
00763  *
00764  *  contrib/isn relies on data type int8, and in 8.4 int8 can now be passed
00765  *  by value.  The schema dumps the CREATE TYPE PASSEDBYVALUE setting so
00766  *  it must match for the old and new servers.
00767  */
00768 static void
00769 check_for_isn_and_int8_passing_mismatch(ClusterInfo *cluster)
00770 {
00771     int         dbnum;
00772     FILE       *script = NULL;
00773     bool        found = false;
00774     char        output_path[MAXPGPATH];
00775 
00776     prep_status("Checking for contrib/isn with bigint-passing mismatch");
00777 
00778     if (old_cluster.controldata.float8_pass_by_value ==
00779         new_cluster.controldata.float8_pass_by_value)
00780     {
00781         /* no mismatch */
00782         check_ok();
00783         return;
00784     }
00785 
00786     snprintf(output_path, sizeof(output_path),
00787              "contrib_isn_and_int8_pass_by_value.txt");
00788 
00789     for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
00790     {
00791         PGresult   *res;
00792         bool        db_used = false;
00793         int         ntups;
00794         int         rowno;
00795         int         i_nspname,
00796                     i_proname;
00797         DbInfo     *active_db = &cluster->dbarr.dbs[dbnum];
00798         PGconn     *conn = connectToServer(cluster, active_db->db_name);
00799 
00800         /* Find any functions coming from contrib/isn */
00801         res = executeQueryOrDie(conn,
00802                                 "SELECT n.nspname, p.proname "
00803                                 "FROM   pg_catalog.pg_proc p, "
00804                                 "       pg_catalog.pg_namespace n "
00805                                 "WHERE  p.pronamespace = n.oid AND "
00806                                 "       p.probin = '$libdir/isn'");
00807 
00808         ntups = PQntuples(res);
00809         i_nspname = PQfnumber(res, "nspname");
00810         i_proname = PQfnumber(res, "proname");
00811         for (rowno = 0; rowno < ntups; rowno++)
00812         {
00813             found = true;
00814             if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
00815                 pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
00816                        output_path, getErrorText(errno));
00817             if (!db_used)
00818             {
00819                 fprintf(script, "Database: %s\n", active_db->db_name);
00820                 db_used = true;
00821             }
00822             fprintf(script, "  %s.%s\n",
00823                     PQgetvalue(res, rowno, i_nspname),
00824                     PQgetvalue(res, rowno, i_proname));
00825         }
00826 
00827         PQclear(res);
00828 
00829         PQfinish(conn);
00830     }
00831 
00832     if (script)
00833         fclose(script);
00834 
00835     if (found)
00836     {
00837         pg_log(PG_REPORT, "fatal\n");
00838         pg_log(PG_FATAL,
00839                "Your installation contains \"contrib/isn\" functions which rely on the\n"
00840           "bigint data type.  Your old and new clusters pass bigint values\n"
00841         "differently so this cluster cannot currently be upgraded.  You can\n"
00842                "manually upgrade databases that use \"contrib/isn\" facilities and remove\n"
00843                "\"contrib/isn\" from the old cluster and restart the upgrade.  A list of\n"
00844                "the problem functions is in the file:\n"
00845                "    %s\n\n", output_path);
00846     }
00847     else
00848         check_ok();
00849 }
00850 
00851 
00852 /*
00853  * check_for_reg_data_type_usage()
00854  *  pg_upgrade only preserves these system values:
00855  *      pg_class.oid
00856  *      pg_type.oid
00857  *      pg_enum.oid
00858  *
00859  *  Many of the reg* data types reference system catalog info that is
00860  *  not preserved, and hence these data types cannot be used in user
00861  *  tables upgraded by pg_upgrade.
00862  */
00863 static void
00864 check_for_reg_data_type_usage(ClusterInfo *cluster)
00865 {
00866     int         dbnum;
00867     FILE       *script = NULL;
00868     bool        found = false;
00869     char        output_path[MAXPGPATH];
00870 
00871     prep_status("Checking for reg* system OID user data types");
00872 
00873     snprintf(output_path, sizeof(output_path), "tables_using_reg.txt");
00874 
00875     for (dbnum = 0; dbnum < cluster->dbarr.ndbs; dbnum++)
00876     {
00877         PGresult   *res;
00878         bool        db_used = false;
00879         int         ntups;
00880         int         rowno;
00881         int         i_nspname,
00882                     i_relname,
00883                     i_attname;
00884         DbInfo     *active_db = &cluster->dbarr.dbs[dbnum];
00885         PGconn     *conn = connectToServer(cluster, active_db->db_name);
00886 
00887         /*
00888          * While several relkinds don't store any data, e.g. views, they can
00889          * be used to define data types of other columns, so we check all
00890          * relkinds.
00891          */
00892         res = executeQueryOrDie(conn,
00893                                 "SELECT n.nspname, c.relname, a.attname "
00894                                 "FROM   pg_catalog.pg_class c, "
00895                                 "       pg_catalog.pg_namespace n, "
00896                                 "       pg_catalog.pg_attribute a "
00897                                 "WHERE  c.oid = a.attrelid AND "
00898                                 "       NOT a.attisdropped AND "
00899                                 "       a.atttypid IN ( "
00900           "         'pg_catalog.regproc'::pg_catalog.regtype, "
00901                                 "           'pg_catalog.regprocedure'::pg_catalog.regtype, "
00902           "         'pg_catalog.regoper'::pg_catalog.regtype, "
00903                                 "           'pg_catalog.regoperator'::pg_catalog.regtype, "
00904         /* regclass.oid is preserved, so 'regclass' is OK */
00905         /* regtype.oid is preserved, so 'regtype' is OK */
00906         "           'pg_catalog.regconfig'::pg_catalog.regtype, "
00907                                 "           'pg_catalog.regdictionary'::pg_catalog.regtype) AND "
00908                                 "       c.relnamespace = n.oid AND "
00909                               "     n.nspname != 'pg_catalog' AND "
00910                          "      n.nspname != 'information_schema'");
00911 
00912         ntups = PQntuples(res);
00913         i_nspname = PQfnumber(res, "nspname");
00914         i_relname = PQfnumber(res, "relname");
00915         i_attname = PQfnumber(res, "attname");
00916         for (rowno = 0; rowno < ntups; rowno++)
00917         {
00918             found = true;
00919             if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
00920                 pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
00921                        output_path, getErrorText(errno));
00922             if (!db_used)
00923             {
00924                 fprintf(script, "Database: %s\n", active_db->db_name);
00925                 db_used = true;
00926             }
00927             fprintf(script, "  %s.%s.%s\n",
00928                     PQgetvalue(res, rowno, i_nspname),
00929                     PQgetvalue(res, rowno, i_relname),
00930                     PQgetvalue(res, rowno, i_attname));
00931         }
00932 
00933         PQclear(res);
00934 
00935         PQfinish(conn);
00936     }
00937 
00938     if (script)
00939         fclose(script);
00940 
00941     if (found)
00942     {
00943         pg_log(PG_REPORT, "fatal\n");
00944         pg_log(PG_FATAL,
00945                "Your installation contains one of the reg* data types in user tables.\n"
00946          "These data types reference system OIDs that are not preserved by\n"
00947         "pg_upgrade, so this cluster cannot currently be upgraded.  You can\n"
00948                "remove the problem tables and restart the upgrade.  A list of the problem\n"
00949                "columns is in the file:\n"
00950                "    %s\n\n", output_path);
00951     }
00952     else
00953         check_ok();
00954 }
00955 
00956 
00957 static void
00958 get_bin_version(ClusterInfo *cluster)
00959 {
00960     char        cmd[MAXPGPATH],
00961                 cmd_output[MAX_STRING];
00962     FILE       *output;
00963     int         pre_dot,
00964                 post_dot;
00965 
00966     snprintf(cmd, sizeof(cmd), "\"%s/pg_ctl\" --version", cluster->bindir);
00967 
00968     if ((output = popen(cmd, "r")) == NULL ||
00969         fgets(cmd_output, sizeof(cmd_output), output) == NULL)
00970         pg_log(PG_FATAL, "Could not get pg_ctl version data using %s: %s\n",
00971                cmd, getErrorText(errno));
00972 
00973     pclose(output);
00974 
00975     /* Remove trailing newline */
00976     if (strchr(cmd_output, '\n') != NULL)
00977         *strchr(cmd_output, '\n') = '\0';
00978 
00979     if (sscanf(cmd_output, "%*s %*s %d.%d", &pre_dot, &post_dot) != 2)
00980         pg_log(PG_FATAL, "could not get version from %s\n", cmd);
00981 
00982     cluster->bin_version = (pre_dot * 100 + post_dot) * 100;
00983 }
00984 
00985 
00986 /*
00987  * get_canonical_locale_name
00988  *
00989  * Send the locale name to the system, and hope we get back a canonical
00990  * version.  This should match the backend's check_locale() function.
00991  */
00992 static char *
00993 get_canonical_locale_name(int category, const char *locale)
00994 {
00995     char       *save;
00996     char       *res;
00997 
00998     save = setlocale(category, NULL);
00999     if (!save)
01000         pg_log(PG_FATAL, "failed to get the current locale\n");
01001 
01002     /* 'save' may be pointing at a modifiable scratch variable, so copy it. */
01003     save = pg_strdup(save);
01004 
01005     /* set the locale with setlocale, to see if it accepts it. */
01006     res = setlocale(category, locale);
01007 
01008     if (!res)
01009         pg_log(PG_FATAL, "failed to get system local name for \"%s\"\n", res);
01010 
01011     res = pg_strdup(res);
01012 
01013     /* restore old value. */
01014     if (!setlocale(category, save))
01015         pg_log(PG_FATAL, "failed to restore old locale \"%s\"\n", save);
01016 
01017     pg_free(save);
01018 
01019     return res;
01020 }