Add a --socketdir option to pg_upgrade.

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
This commit is contained in:
Tom Lane 2018-12-01 15:45:11 -05:00
parent 7d4524aed3
commit 2d34ad8430
3 changed files with 77 additions and 44 deletions

View File

@ -163,6 +163,14 @@
</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

View 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
{
/*

View 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,