]> granicus.if.org Git - postgresql/commitdiff
Add a --socketdir option to pg_upgrade.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 1 Dec 2018 20:45:11 +0000 (15:45 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 1 Dec 2018 20:45:11 +0000 (15:45 -0500)
This allows control of the directory in which the postmaster sockets
are created for the temporary postmasters started by pg_upgrade.
The default location remains the current working directory, which is
typically fine, but if it is deeply nested then its pathname might
be too long to be a socket name.

In passing, clean up some messiness in pg_upgrade's option handling,
particularly the confusing and undocumented way that configuration-only
datadirs were handled.  And fix check_required_directory's substantially
under-baked cleanup of directory pathnames.

Daniel Gustafsson, reviewed by Hironobu Suzuki, some code cleanup by me

Discussion: https://postgr.es/m/E72DD5C3-2268-48A5-A907-ED4B34BEC223@yesql.se

doc/src/sgml/ref/pgupgrade.sgml
src/bin/pg_upgrade/option.c
src/bin/pg_upgrade/pg_upgrade.h

index 978fa252e400f6cb60b4a1e6ff51711c8caaff5a..7e1afaf0a50be25309a0fc5609d2ff6ef676abde 100644 (file)
       </para></listitem>
      </varlistentry>
 
+     <varlistentry>
+      <term><option>-s</option> <replaceable>dir</replaceable></term>
+      <term><option>--socketdir=</option><replaceable>dir</replaceable></term>
+      <listitem><para>directory to use for postmaster sockets during upgrade;
+      default is current working directory; environment
+      variable <envar>PGSOCKETDIR</envar></para></listitem>
+     </varlistentry>
+
      <varlistentry>
       <term><option>-U</option> <replaceable>username</replaceable></term>
       <term><option>--username=</option><replaceable>username</replaceable></term>
@@ -709,11 +717,21 @@ psql --username=postgres --file=script.sql postgres
   <title>Notes</title>
 
   <para>
-   <application>pg_upgrade</application> does not support upgrading of databases
-   containing table columns using these <type>reg*</type> OID-referencing system data types:
-   <type>regproc</type>, <type>regprocedure</type>, <type>regoper</type>,
-   <type>regoperator</type>, <type>regconfig</type>, and
-   <type>regdictionary</type>.  (<type>regtype</type> can be upgraded.)
+   <application>pg_upgrade</application> creates various working files, such
+   as schema dumps, in the current working directory.  For security, be sure
+   that that directory is not readable or writable by any other users.
+  </para>
+
+  <para>
+   <application>pg_upgrade</application> launches short-lived postmasters in
+   the old and new data directories.  Temporary Unix socket files for
+   communication with these postmasters are, by default, made in the current
+   working directory.  In some situations the path name for the current
+   directory might be too long to be a valid socket name.  In that case you
+   can use the <option>-s</option> option to put the socket files in some
+   directory with a shorter path name.  For security, be sure that that
+   directory is not readable or writable by any other users.
+   (This is not relevant on Windows.)
   </para>
 
   <para>
@@ -732,6 +750,14 @@ psql --username=postgres --file=script.sql postgres
    insert dummy data, and upgrade that.
   </para>
 
+  <para>
+   <application>pg_upgrade</application> does not support upgrading of databases
+   containing table columns using these <type>reg*</type> OID-referencing system data types:
+   <type>regproc</type>, <type>regprocedure</type>, <type>regoper</type>,
+   <type>regoperator</type>, <type>regconfig</type>, and
+   <type>regdictionary</type>.  (<type>regtype</type> can be upgraded.)
+  </para>
+
   <para>
    If you are upgrading a pre-<productname>PostgreSQL</productname> 9.2 cluster
    that uses a configuration-file-only directory, you must pass the
index 7cc92f2dcb39f5d2d81bd74e0f4f2acd1ee4aada..ef8dd28adb4487671a11c3049062d92bcf5ea61a 100644 (file)
@@ -21,8 +21,9 @@
 
 
 static void usage(void);
-static void check_required_directory(char **dirpath, char **configpath,
-                                                const char *envVarName, const char *cmdLineOption, const char *description);
+static void check_required_directory(char **dirpath,
+                                                const char *envVarName, bool useCwd,
+                                                const char *cmdLineOption, const char *description);
 #define FIX_DEFAULT_READ_ONLY "-c default_transaction_read_only=false"
 
 
@@ -52,6 +53,7 @@ parseCommandLine(int argc, char *argv[])
                {"link", no_argument, NULL, 'k'},
                {"retain", no_argument, NULL, 'r'},
                {"jobs", required_argument, NULL, 'j'},
+               {"socketdir", required_argument, NULL, 's'},
                {"verbose", no_argument, NULL, 'v'},
                {"clone", no_argument, NULL, 1},
 
@@ -102,7 +104,7 @@ parseCommandLine(int argc, char *argv[])
        if ((log_opts.internal = fopen_priv(INTERNAL_LOG_FILE, "a")) == NULL)
                pg_fatal("could not 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",
+       while ((option = getopt_long(argc, argv, "d:D:b:B:cj:ko:O:p:P:rs:U:v",
                                                                 long_options, &optindex)) != -1)
        {
                switch (option)
@@ -121,12 +123,10 @@ parseCommandLine(int argc, char *argv[])
 
                        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':
@@ -188,6 +188,10 @@ parseCommandLine(int argc, char *argv[])
                                log_opts.retain = true;
                                break;
 
+                       case 's':
+                               user_opts.socketdir = pg_strdup(optarg);
+                               break;
+
                        case 'U':
                                pg_free(os_info.user);
                                os_info.user = pg_strdup(optarg);
@@ -244,14 +248,16 @@ parseCommandLine(int argc, char *argv[])
                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"));
+       check_required_directory(&old_cluster.bindir, "PGBINOLD", false,
+                                                        "-b", _("old cluster binaries reside"));
+       check_required_directory(&new_cluster.bindir, "PGBINNEW", false,
+                                                        "-B", _("new cluster binaries reside"));
+       check_required_directory(&old_cluster.pgdata, "PGDATAOLD", false,
+                                                        "-d", _("old cluster data resides"));
+       check_required_directory(&new_cluster.pgdata, "PGDATANEW", false,
+                                                        "-D", _("new cluster data resides"));
+       check_required_directory(&user_opts.socketdir, "PGSOCKETDIR", true,
+                                                        "-s", _("sockets will be created"));
 
 #ifdef WIN32
 
@@ -296,6 +302,7 @@ usage(void)
        printf(_("  -p, --old-port=PORT           old cluster port number (default %d)\n"), old_cluster.port);
        printf(_("  -P, --new-port=PORT           new cluster port number (default %d)\n"), new_cluster.port);
        printf(_("  -r, --retain                  retain SQL and log files after success\n"));
+       printf(_("  -s, --socketdir=DIR           socket directory to use (default CWD)\n"));
        printf(_("  -U, --username=NAME           cluster superuser (default \"%s\")\n"), os_info.user);
        printf(_("  -v, --verbose                 enable verbose internal logging\n"));
        printf(_("  -V, --version                 display version information, then exit\n"));
@@ -337,29 +344,32 @@ usage(void)
  * check_required_directory()
  *
  * Checks a directory option.
- *     dirpath           - the directory name supplied on the command line
- *     configpath        - optional configuration directory
+ *     dirpath           - the directory name supplied on the command line, or NULL
  *     envVarName        - the name of an environment variable to get if dirpath is NULL
- *     cmdLineOption - the command line option corresponds to this directory (-o, -O, -n, -N)
+ *     useCwd            - true if OK to default to CWD
+ *     cmdLineOption - the command line option for this directory
  *     description   - a description of this directory option
  *
  * We use the last two arguments to construct a meaningful error message if the
  * user hasn't provided the required directory name.
  */
 static void
-check_required_directory(char **dirpath, char **configpath,
-                                                const char *envVarName, const char *cmdLineOption,
-                                                const char *description)
+check_required_directory(char **dirpath, const char *envVarName, bool useCwd,
+                                                const char *cmdLineOption, const char *description)
 {
        if (*dirpath == NULL || strlen(*dirpath) == 0)
        {
                const char *envVar;
 
                if ((envVar = getenv(envVarName)) && strlen(envVar))
-               {
                        *dirpath = pg_strdup(envVar);
-                       if (configpath)
-                               *configpath = pg_strdup(envVar);
+               else if (useCwd)
+               {
+                       char            cwd[MAXPGPATH];
+
+                       if (!getcwd(cwd, MAXPGPATH))
+                               pg_fatal("could not determine current directory\n");
+                       *dirpath = pg_strdup(cwd);
                }
                else
                        pg_fatal("You must identify the directory where the %s.\n"
@@ -368,16 +378,10 @@ check_required_directory(char **dirpath, char **configpath,
        }
 
        /*
-        * Trim off any trailing path separators because we construct paths by
-        * appending to this path.
+        * Clean up the path, in particular trimming any trailing path separators,
+        * because we construct paths by appending to this path.
         */
-#ifndef WIN32
-       if ((*dirpath)[strlen(*dirpath) - 1] == '/')
-#else
-       if ((*dirpath)[strlen(*dirpath) - 1] == '/' ||
-               (*dirpath)[strlen(*dirpath) - 1] == '\\')
-#endif
-               (*dirpath)[strlen(*dirpath) - 1] = 0;
+       canonicalize_path(*dirpath);
 }
 
 /*
@@ -386,6 +390,10 @@ check_required_directory(char **dirpath, char **configpath,
  * If a configuration-only directory was specified, find the real data dir
  * by querying the running server.  This has limited checking because we
  * can't check for a running server because we can't find postmaster.pid.
+ *
+ * On entry, cluster->pgdata has been set from command line or env variable,
+ * but cluster->pgconfig isn't set.  We fill both variables with corrected
+ * values.
  */
 void
 adjust_data_dir(ClusterInfo *cluster)
@@ -396,6 +404,9 @@ adjust_data_dir(ClusterInfo *cluster)
        FILE       *fp,
                           *output;
 
+       /* Initially assume config dir and data dir are the same */
+       cluster->pgconfig = pg_strdup(cluster->pgdata);
+
        /* If there is no postgresql.conf, it can't be a config-only dir */
        snprintf(filename, sizeof(filename), "%s/postgresql.conf", cluster->pgconfig);
        if ((fp = fopen(filename, "r")) == NULL)
@@ -462,12 +473,7 @@ get_sock_dir(ClusterInfo *cluster, bool live_check)
        if (GET_MAJOR_VERSION(cluster->major_version) >= 901)
        {
                if (!live_check)
-               {
-                       /* Use the current directory for the socket */
-                       cluster->sockdir = pg_malloc(MAXPGPATH);
-                       if (!getcwd(cluster->sockdir, MAXPGPATH))
-                               pg_fatal("could not determine current directory\n");
-               }
+                       cluster->sockdir = user_opts.socketdir;
                else
                {
                        /*
index 51bd211d46a060401c5df7b734f5602eb8c39215..611a20768b1ec910e0d04fadf333564db1b3f098 100644 (file)
@@ -298,7 +298,8 @@ typedef struct
        bool            check;                  /* true -> ask user for permission to make
                                                                 * changes */
        transferMode transfer_mode; /* copy files or link them? */
-       int                     jobs;
+       int                     jobs;                   /* number of processes/threads to use */
+       char       *socketdir;          /* directory to use for Unix sockets */
 } UserOpts;
 
 typedef struct
@@ -374,7 +375,7 @@ bool                pid_lock_file_exists(const char *datadir);
 /* file.c */
 
 void cloneFile(const char *src, const char *dst,
-                const char *schemaName, const char *relName);
+                 const char *schemaName, const char *relName);
 void copyFile(const char *src, const char *dst,
                 const char *schemaName, const char *relName);
 void linkFile(const char *src, const char *dst,