Header And Logo

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

function.c

Go to the documentation of this file.
00001 /*
00002  *  function.c
00003  *
00004  *  server-side function support
00005  *
00006  *  Copyright (c) 2010-2013, PostgreSQL Global Development Group
00007  *  contrib/pg_upgrade/function.c
00008  */
00009 
00010 #include "postgres_fe.h"
00011 
00012 #include "pg_upgrade.h"
00013 
00014 #include "access/transam.h"
00015 
00016 #define PG_UPGRADE_SUPPORT  "$libdir/pg_upgrade_support"
00017 
00018 /*
00019  * install_support_functions_in_new_db()
00020  *
00021  * pg_upgrade requires some support functions that enable it to modify
00022  * backend behavior.
00023  */
00024 void
00025 install_support_functions_in_new_db(const char *db_name)
00026 {
00027     PGconn     *conn = connectToServer(&new_cluster, db_name);
00028 
00029     /* suppress NOTICE of dropped objects */
00030     PQclear(executeQueryOrDie(conn,
00031                               "SET client_min_messages = warning;"));
00032     PQclear(executeQueryOrDie(conn,
00033                            "DROP SCHEMA IF EXISTS binary_upgrade CASCADE;"));
00034     PQclear(executeQueryOrDie(conn,
00035                               "RESET client_min_messages;"));
00036 
00037     PQclear(executeQueryOrDie(conn,
00038                               "CREATE SCHEMA binary_upgrade;"));
00039 
00040     PQclear(executeQueryOrDie(conn,
00041                               "CREATE OR REPLACE FUNCTION "
00042                               "binary_upgrade.set_next_pg_type_oid(OID) "
00043                               "RETURNS VOID "
00044                               "AS '$libdir/pg_upgrade_support' "
00045                               "LANGUAGE C STRICT;"));
00046     PQclear(executeQueryOrDie(conn,
00047                               "CREATE OR REPLACE FUNCTION "
00048                             "binary_upgrade.set_next_array_pg_type_oid(OID) "
00049                               "RETURNS VOID "
00050                               "AS '$libdir/pg_upgrade_support' "
00051                               "LANGUAGE C STRICT;"));
00052     PQclear(executeQueryOrDie(conn,
00053                               "CREATE OR REPLACE FUNCTION "
00054                             "binary_upgrade.set_next_toast_pg_type_oid(OID) "
00055                               "RETURNS VOID "
00056                               "AS '$libdir/pg_upgrade_support' "
00057                               "LANGUAGE C STRICT;"));
00058     PQclear(executeQueryOrDie(conn,
00059                               "CREATE OR REPLACE FUNCTION "
00060                             "binary_upgrade.set_next_heap_pg_class_oid(OID) "
00061                               "RETURNS VOID "
00062                               "AS '$libdir/pg_upgrade_support' "
00063                               "LANGUAGE C STRICT;"));
00064     PQclear(executeQueryOrDie(conn,
00065                               "CREATE OR REPLACE FUNCTION "
00066                            "binary_upgrade.set_next_index_pg_class_oid(OID) "
00067                               "RETURNS VOID "
00068                               "AS '$libdir/pg_upgrade_support' "
00069                               "LANGUAGE C STRICT;"));
00070     PQclear(executeQueryOrDie(conn,
00071                               "CREATE OR REPLACE FUNCTION "
00072                            "binary_upgrade.set_next_toast_pg_class_oid(OID) "
00073                               "RETURNS VOID "
00074                               "AS '$libdir/pg_upgrade_support' "
00075                               "LANGUAGE C STRICT;"));
00076     PQclear(executeQueryOrDie(conn,
00077                               "CREATE OR REPLACE FUNCTION "
00078                               "binary_upgrade.set_next_pg_enum_oid(OID) "
00079                               "RETURNS VOID "
00080                               "AS '$libdir/pg_upgrade_support' "
00081                               "LANGUAGE C STRICT;"));
00082     PQclear(executeQueryOrDie(conn,
00083                               "CREATE OR REPLACE FUNCTION "
00084                               "binary_upgrade.set_next_pg_authid_oid(OID) "
00085                               "RETURNS VOID "
00086                               "AS '$libdir/pg_upgrade_support' "
00087                               "LANGUAGE C STRICT;"));
00088     PQclear(executeQueryOrDie(conn,
00089                               "CREATE OR REPLACE FUNCTION "
00090                               "binary_upgrade.create_empty_extension(text, text, bool, text, oid[], text[], text[]) "
00091                               "RETURNS VOID "
00092                               "AS '$libdir/pg_upgrade_support' "
00093                               "LANGUAGE C;"));
00094     PQfinish(conn);
00095 }
00096 
00097 
00098 void
00099 uninstall_support_functions_from_new_cluster(void)
00100 {
00101     int         dbnum;
00102 
00103     prep_status("Removing support functions from new cluster");
00104 
00105     for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++)
00106     {
00107         DbInfo     *new_db = &new_cluster.dbarr.dbs[dbnum];
00108         PGconn     *conn = connectToServer(&new_cluster, new_db->db_name);
00109 
00110         /* suppress NOTICE of dropped objects */
00111         PQclear(executeQueryOrDie(conn,
00112                                   "SET client_min_messages = warning;"));
00113         PQclear(executeQueryOrDie(conn,
00114                                   "DROP SCHEMA binary_upgrade CASCADE;"));
00115         PQclear(executeQueryOrDie(conn,
00116                                   "RESET client_min_messages;"));
00117         PQfinish(conn);
00118     }
00119     check_ok();
00120 }
00121 
00122 
00123 /*
00124  * get_loadable_libraries()
00125  *
00126  *  Fetch the names of all old libraries containing C-language functions.
00127  *  We will later check that they all exist in the new installation.
00128  */
00129 void
00130 get_loadable_libraries(void)
00131 {
00132     PGresult  **ress;
00133     int         totaltups;
00134     int         dbnum;
00135     bool        found_public_plpython_handler = false;
00136 
00137     ress = (PGresult **) pg_malloc(old_cluster.dbarr.ndbs * sizeof(PGresult *));
00138     totaltups = 0;
00139 
00140     /* Fetch all library names, removing duplicates within each DB */
00141     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
00142     {
00143         DbInfo     *active_db = &old_cluster.dbarr.dbs[dbnum];
00144         PGconn     *conn = connectToServer(&old_cluster, active_db->db_name);
00145 
00146         /*
00147          * Fetch all libraries referenced in this DB.  We can't exclude the
00148          * "pg_catalog" schema because, while such functions are not
00149          * explicitly dumped by pg_dump, they do reference implicit objects
00150          * that pg_dump does dump, e.g. CREATE LANGUAGE plperl.
00151          */
00152         ress[dbnum] = executeQueryOrDie(conn,
00153                                         "SELECT DISTINCT probin "
00154                                         "FROM   pg_catalog.pg_proc "
00155                                         "WHERE  prolang = 13 /* C */ AND "
00156                                         "probin IS NOT NULL AND "
00157                                         "oid >= %u;",
00158                                         FirstNormalObjectId);
00159         totaltups += PQntuples(ress[dbnum]);
00160 
00161         /*
00162          * Systems that install plpython before 8.1 have
00163          * plpython_call_handler() defined in the "public" schema, causing
00164          * pg_dumpall to dump it.  However that function still references
00165          * "plpython" (no "2"), so it throws an error on restore.  This code
00166          * checks for the problem function, reports affected databases to the
00167          * user and explains how to remove them. 8.1 git commit:
00168          * e0dedd0559f005d60c69c9772163e69c204bac69
00169          * http://archives.postgresql.org/pgsql-hackers/2012-03/msg01101.php
00170          * http://archives.postgresql.org/pgsql-bugs/2012-05/msg00206.php
00171          */
00172         if (GET_MAJOR_VERSION(old_cluster.major_version) < 901)
00173         {
00174             PGresult   *res;
00175 
00176             res = executeQueryOrDie(conn,
00177                                     "SELECT 1 "
00178                            "FROM    pg_catalog.pg_proc JOIN pg_namespace "
00179                              "      ON pronamespace = pg_namespace.oid "
00180                                "WHERE proname = 'plpython_call_handler' AND "
00181                                     "nspname = 'public' AND "
00182                                     "prolang = 13 /* C */ AND "
00183                                     "probin = '$libdir/plpython' AND "
00184                                     "pg_proc.oid >= %u;",
00185                                     FirstNormalObjectId);
00186             if (PQntuples(res) > 0)
00187             {
00188                 if (!found_public_plpython_handler)
00189                 {
00190                     pg_log(PG_WARNING,
00191                            "\nThe old cluster has a \"plpython_call_handler\" function defined\n"
00192                            "in the \"public\" schema which is a duplicate of the one defined\n"
00193                            "in the \"pg_catalog\" schema.  You can confirm this by executing\n"
00194                            "in psql:\n"
00195                            "\n"
00196                            "    \\df *.plpython_call_handler\n"
00197                            "\n"
00198                            "The \"public\" schema version of this function was created by a\n"
00199                            "pre-8.1 install of plpython, and must be removed for pg_upgrade\n"
00200                            "to complete because it references a now-obsolete \"plpython\"\n"
00201                            "shared object file.  You can remove the \"public\" schema version\n"
00202                        "of this function by running the following command:\n"
00203                            "\n"
00204                          "  DROP FUNCTION public.plpython_call_handler()\n"
00205                            "\n"
00206                            "in each affected database:\n"
00207                            "\n");
00208                 }
00209                 pg_log(PG_WARNING, "    %s\n", active_db->db_name);
00210                 found_public_plpython_handler = true;
00211             }
00212             PQclear(res);
00213         }
00214 
00215         PQfinish(conn);
00216     }
00217 
00218     if (found_public_plpython_handler)
00219         pg_log(PG_FATAL,
00220          "Remove the problem functions from the old cluster to continue.\n");
00221 
00222     totaltups++;                /* reserve for pg_upgrade_support */
00223 
00224     /* Allocate what's certainly enough space */
00225     os_info.libraries = (char **) pg_malloc(totaltups * sizeof(char *));
00226 
00227     /*
00228      * Now remove duplicates across DBs.  This is pretty inefficient code, but
00229      * there probably aren't enough entries to matter.
00230      */
00231     totaltups = 0;
00232     os_info.libraries[totaltups++] = pg_strdup(PG_UPGRADE_SUPPORT);
00233 
00234     for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++)
00235     {
00236         PGresult   *res = ress[dbnum];
00237         int         ntups;
00238         int         rowno;
00239 
00240         ntups = PQntuples(res);
00241         for (rowno = 0; rowno < ntups; rowno++)
00242         {
00243             char       *lib = PQgetvalue(res, rowno, 0);
00244             bool        dup = false;
00245             int         n;
00246 
00247             for (n = 0; n < totaltups; n++)
00248             {
00249                 if (strcmp(lib, os_info.libraries[n]) == 0)
00250                 {
00251                     dup = true;
00252                     break;
00253                 }
00254             }
00255             if (!dup)
00256                 os_info.libraries[totaltups++] = pg_strdup(lib);
00257         }
00258 
00259         PQclear(res);
00260     }
00261 
00262     os_info.num_libraries = totaltups;
00263 
00264     pg_free(ress);
00265 }
00266 
00267 
00268 /*
00269  * check_loadable_libraries()
00270  *
00271  *  Check that the new cluster contains all required libraries.
00272  *  We do this by actually trying to LOAD each one, thereby testing
00273  *  compatibility as well as presence.
00274  */
00275 void
00276 check_loadable_libraries(void)
00277 {
00278     PGconn     *conn = connectToServer(&new_cluster, "template1");
00279     int         libnum;
00280     FILE       *script = NULL;
00281     bool        found = false;
00282     char        output_path[MAXPGPATH];
00283 
00284     prep_status("Checking for presence of required libraries");
00285 
00286     snprintf(output_path, sizeof(output_path), "loadable_libraries.txt");
00287 
00288     for (libnum = 0; libnum < os_info.num_libraries; libnum++)
00289     {
00290         char       *lib = os_info.libraries[libnum];
00291         int         llen = strlen(lib);
00292         char        cmd[7 + 2 * MAXPGPATH + 1];
00293         PGresult   *res;
00294 
00295         /*
00296          * In Postgres 9.0, Python 3 support was added, and to do that, a
00297          * plpython2u language was created with library name plpython2.so as a
00298          * symbolic link to plpython.so.  In Postgres 9.1, only the
00299          * plpython2.so library was created, and both plpythonu and plpython2u
00300          * pointing to it.  For this reason, any reference to library name
00301          * "plpython" in an old PG <= 9.1 cluster must look for "plpython2" in
00302          * the new cluster.
00303          *
00304          * For this case, we could check pg_pltemplate, but that only works
00305          * for languages, and does not help with function shared objects, so
00306          * we just do a general fix.
00307          */
00308         if (GET_MAJOR_VERSION(old_cluster.major_version) < 901 &&
00309             strcmp(lib, "$libdir/plpython") == 0)
00310         {
00311             lib = "$libdir/plpython2";
00312             llen = strlen(lib);
00313         }
00314 
00315         strcpy(cmd, "LOAD '");
00316         PQescapeStringConn(conn, cmd + strlen(cmd), lib, llen, NULL);
00317         strcat(cmd, "'");
00318 
00319         res = PQexec(conn, cmd);
00320 
00321         if (PQresultStatus(res) != PGRES_COMMAND_OK)
00322         {
00323             found = true;
00324 
00325             /* exit and report missing support library with special message */
00326             if (strcmp(lib, PG_UPGRADE_SUPPORT) == 0)
00327                 pg_log(PG_FATAL,
00328                        "The pg_upgrade_support module must be created and installed in the new cluster.\n");
00329 
00330             if (script == NULL && (script = fopen_priv(output_path, "w")) == NULL)
00331                 pg_log(PG_FATAL, "Could not open file \"%s\": %s\n",
00332                        output_path, getErrorText(errno));
00333             fprintf(script, "Could not load library \"%s\"\n%s\n",
00334                     lib,
00335                     PQerrorMessage(conn));
00336         }
00337 
00338         PQclear(res);
00339     }
00340 
00341     PQfinish(conn);
00342 
00343     if (found)
00344     {
00345         fclose(script);
00346         pg_log(PG_REPORT, "fatal\n");
00347         pg_log(PG_FATAL,
00348                "Your installation references loadable libraries that are missing from the\n"
00349                "new installation.  You can add these libraries to the new installation,\n"
00350                "or remove the functions using them from the old installation.  A list of\n"
00351                "problem libraries is in the file:\n"
00352                "    %s\n\n", output_path);
00353     }
00354     else
00355         check_ok();
00356 }