An alternative way to fix #168 which is not as invasive as the changes in #171. This currently breaks the current behaviour of the program as the tables specified on command line are not found.
829 lines
17 KiB
C
829 lines
17 KiB
C
/*-------------------------------------------------------------------------
|
|
* pgut-fe.c
|
|
*
|
|
* Portions Copyright (c) 2008-2011, NIPPON TELEGRAPH AND TELEPHONE CORPORATION
|
|
* Portions Copyright (c) 2011, Itagaki Takahiro
|
|
* Portions Copyright (c) 2012-2015, The Reorg Development Team
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#define FRONTEND
|
|
#include "pgut-fe.h"
|
|
|
|
#ifdef HAVE_GETOPT_H
|
|
#include <getopt.h>
|
|
#else
|
|
#include <getopt_long.h>
|
|
#endif
|
|
|
|
char *dbname = NULL;
|
|
char *host = NULL;
|
|
char *port = NULL;
|
|
char *username = NULL;
|
|
char *password = NULL;
|
|
YesNo prompt_password = DEFAULT;
|
|
|
|
PGconn *connection = NULL;
|
|
PGconn *conn2 = NULL;
|
|
|
|
worker_conns workers = {
|
|
.num_workers = 0,
|
|
.conns = NULL
|
|
};
|
|
|
|
|
|
static bool parse_pair(const char buffer[], char key[], char value[]);
|
|
static char *get_username(void);
|
|
|
|
|
|
/*
|
|
* Set up worker conns which will be used for concurrent index rebuilds.
|
|
* 'num_workers' is the desired number of worker connections, i.e. from
|
|
* --jobs flag. Due to max_connections we might not actually be able to
|
|
* set up that many workers, but don't treat that as a fatal error.
|
|
*/
|
|
void
|
|
setup_workers(int num_workers)
|
|
{
|
|
StringInfoData buf;
|
|
int i;
|
|
PGconn *conn;
|
|
|
|
elog(DEBUG2, "In setup_workers(), target num_workers = %d", num_workers);
|
|
|
|
if (num_workers > 1 && num_workers > workers.num_workers)
|
|
{
|
|
initStringInfo(&buf);
|
|
if (dbname && dbname[0])
|
|
appendStringInfo(&buf, "dbname=%s ", dbname);
|
|
if (host && host[0])
|
|
appendStringInfo(&buf, "host=%s ", host);
|
|
if (port && port[0])
|
|
appendStringInfo(&buf, "port=%s ", port);
|
|
if (username && username[0])
|
|
appendStringInfo(&buf, "user=%s ", username);
|
|
if (password && password[0])
|
|
appendStringInfo(&buf, "password=%s ", password);
|
|
|
|
if (workers.conns == NULL)
|
|
{
|
|
elog(NOTICE, "Setting up workers.conns");
|
|
workers.conns = (PGconn **) pgut_malloc(sizeof(PGconn *) * num_workers);
|
|
}
|
|
else
|
|
{
|
|
elog(ERROR, "TODO: Implement pool resizing.");
|
|
}
|
|
|
|
for (i = 0; i < num_workers; i++)
|
|
{
|
|
/* Don't prompt for password again; we should have gotten
|
|
* it already from reconnect().
|
|
*/
|
|
elog(DEBUG2, "Setting up worker conn %d", i);
|
|
|
|
/* Don't confuse pgut_connections by using pgut_connect()
|
|
*
|
|
* XXX: could use PQconnectStart() and PQconnectPoll() to
|
|
* open these connections in non-blocking manner.
|
|
*/
|
|
conn = PQconnectdb(buf.data);
|
|
if (PQstatus(conn) == CONNECTION_OK)
|
|
{
|
|
workers.conns[i] = conn;
|
|
}
|
|
else
|
|
{
|
|
elog(WARNING, "Unable to set up worker conn #%d: %s", i,
|
|
PQerrorMessage(conn));
|
|
break;
|
|
}
|
|
|
|
/* Hardcode a search path to avoid injections into public or pg_temp */
|
|
pgut_command(conn, "SET search_path TO pg_catalog, pg_temp", 0, NULL);
|
|
|
|
/* Make sure each worker connection can work in non-blocking
|
|
* mode.
|
|
*/
|
|
if (PQsetnonblocking(workers.conns[i], 1))
|
|
{
|
|
elog(ERROR, "Unable to set worker connection %d "
|
|
"non-blocking.", i);
|
|
}
|
|
}
|
|
/* In case we bailed out of setting up all workers, record
|
|
* how many successful worker conns we actually have.
|
|
*/
|
|
workers.num_workers = i;
|
|
|
|
termStringInfo(&buf);
|
|
}
|
|
}
|
|
|
|
/* Disconnect all our worker conns. */
|
|
void disconnect_workers(void)
|
|
{
|
|
int i;
|
|
|
|
if (!(workers.num_workers))
|
|
elog(DEBUG2, "No workers to disconnect.");
|
|
else
|
|
{
|
|
for (i = 0; i < workers.num_workers; i++)
|
|
{
|
|
if (workers.conns[i])
|
|
{
|
|
elog(DEBUG2, "Disconnecting worker %d.", i);
|
|
PQfinish(workers.conns[i]);
|
|
workers.conns[i] = NULL;
|
|
}
|
|
else
|
|
{
|
|
elog(NOTICE, "Worker %d already disconnected?", i);
|
|
}
|
|
}
|
|
workers.num_workers = 0;
|
|
free(workers.conns);
|
|
workers.conns = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* the result is also available with the global variable 'connection'.
|
|
*/
|
|
void
|
|
reconnect(int elevel)
|
|
{
|
|
StringInfoData buf;
|
|
char *new_password;
|
|
|
|
disconnect();
|
|
initStringInfo(&buf);
|
|
if (dbname && dbname[0])
|
|
appendStringInfo(&buf, "dbname=%s ", dbname);
|
|
if (host && host[0])
|
|
appendStringInfo(&buf, "host=%s ", host);
|
|
if (port && port[0])
|
|
appendStringInfo(&buf, "port=%s ", port);
|
|
if (username && username[0])
|
|
appendStringInfo(&buf, "user=%s ", username);
|
|
if (password && password[0])
|
|
appendStringInfo(&buf, "password=%s ", password);
|
|
|
|
connection = pgut_connect(buf.data, prompt_password, elevel);
|
|
conn2 = pgut_connect(buf.data, prompt_password, elevel);
|
|
|
|
/* update password */
|
|
if (connection)
|
|
{
|
|
new_password = PQpass(connection);
|
|
if (new_password && new_password[0] &&
|
|
(password == NULL || strcmp(new_password, password) != 0))
|
|
{
|
|
free(password);
|
|
password = pgut_strdup(new_password);
|
|
}
|
|
}
|
|
|
|
termStringInfo(&buf);
|
|
}
|
|
|
|
void
|
|
disconnect(void)
|
|
{
|
|
if (connection)
|
|
{
|
|
pgut_disconnect(connection);
|
|
connection = NULL;
|
|
}
|
|
if (conn2)
|
|
{
|
|
pgut_disconnect(conn2);
|
|
conn2 = NULL;
|
|
}
|
|
disconnect_workers();
|
|
}
|
|
|
|
static void
|
|
option_from_env(pgut_option options[])
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; options && options[i].type; i++)
|
|
{
|
|
pgut_option *opt = &options[i];
|
|
char name[256];
|
|
size_t j;
|
|
const char *s;
|
|
const char *value;
|
|
|
|
if (opt->source > SOURCE_ENV ||
|
|
opt->allowed == SOURCE_DEFAULT || opt->allowed > SOURCE_ENV)
|
|
continue;
|
|
|
|
for (s = opt->lname, j = 0; *s && j < lengthof(name) - 1; s++, j++)
|
|
{
|
|
if (strchr("-_ ", *s))
|
|
name[j] = '_'; /* - to _ */
|
|
else
|
|
name[j] = toupper(*s);
|
|
}
|
|
name[j] = '\0';
|
|
|
|
if ((value = getenv(name)) != NULL)
|
|
pgut_setopt(opt, value, SOURCE_ENV);
|
|
}
|
|
}
|
|
|
|
/* compare two strings ignore cases and ignore -_ */
|
|
bool
|
|
pgut_keyeq(const char *lhs, const char *rhs)
|
|
{
|
|
for (; *lhs && *rhs; lhs++, rhs++)
|
|
{
|
|
if (strchr("-_ ", *lhs))
|
|
{
|
|
if (!strchr("-_ ", *rhs))
|
|
return false;
|
|
}
|
|
else if (ToLower(*lhs) != ToLower(*rhs))
|
|
return false;
|
|
}
|
|
|
|
return *lhs == '\0' && *rhs == '\0';
|
|
}
|
|
|
|
void
|
|
pgut_setopt(pgut_option *opt, const char *optarg, pgut_optsrc src)
|
|
{
|
|
const char *message;
|
|
|
|
if (opt == NULL)
|
|
{
|
|
fprintf(stderr, "Try \"%s --help\" for more information.\n", PROGRAM_NAME);
|
|
exit(EINVAL);
|
|
}
|
|
|
|
if (opt->source > src)
|
|
{
|
|
/* high prior value has been set already. */
|
|
return;
|
|
}
|
|
else if (src >= SOURCE_CMDLINE && opt->source >= src && opt->type != 'l')
|
|
{
|
|
/* duplicated option in command line -- don't worry if the option
|
|
* type is 'l' i.e. SimpleStringList, since we are allowed to have
|
|
* multiples of these.
|
|
*/
|
|
message = "specified only once";
|
|
}
|
|
else
|
|
{
|
|
/* can be overwritten if non-command line source */
|
|
opt->source = src;
|
|
|
|
switch (opt->type)
|
|
{
|
|
case 'b':
|
|
case 'B':
|
|
if (optarg == NULL)
|
|
{
|
|
*((bool *) opt->var) = (opt->type == 'b');
|
|
return;
|
|
}
|
|
else if (parse_bool(optarg, (bool *) opt->var))
|
|
{
|
|
return;
|
|
}
|
|
message = "a boolean";
|
|
break;
|
|
case 'f':
|
|
((pgut_optfn) opt->var)(opt, optarg);
|
|
return;
|
|
case 'i':
|
|
if (parse_int32(optarg, opt->var))
|
|
return;
|
|
message = "a 32bit signed integer";
|
|
break;
|
|
case 'l':
|
|
message = "a List";
|
|
simple_string_list_append(opt->var, optarg);
|
|
return;
|
|
case 'u':
|
|
if (parse_uint32(optarg, opt->var))
|
|
return;
|
|
message = "a 32bit unsigned integer";
|
|
break;
|
|
case 'I':
|
|
if (parse_int64(optarg, opt->var))
|
|
return;
|
|
message = "a 64bit signed integer";
|
|
break;
|
|
case 'U':
|
|
if (parse_uint64(optarg, opt->var))
|
|
return;
|
|
message = "a 64bit unsigned integer";
|
|
break;
|
|
case 's':
|
|
if (opt->source != SOURCE_DEFAULT)
|
|
free(*(char **) opt->var);
|
|
*(char **) opt->var = pgut_strdup(optarg);
|
|
return;
|
|
case 't':
|
|
if (parse_time(optarg, opt->var))
|
|
return;
|
|
message = "a time";
|
|
break;
|
|
case 'y':
|
|
case 'Y':
|
|
if (optarg == NULL)
|
|
{
|
|
*(YesNo *) opt->var = (opt->type == 'y' ? YES : NO);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
bool value;
|
|
if (parse_bool(optarg, &value))
|
|
{
|
|
*(YesNo *) opt->var = (value ? YES : NO);
|
|
return;
|
|
}
|
|
}
|
|
message = "a boolean";
|
|
break;
|
|
default:
|
|
ereport(ERROR,
|
|
(errcode(EINVAL),
|
|
errmsg("invalid option type: %c", opt->type)));
|
|
return; /* keep compiler quiet */
|
|
}
|
|
}
|
|
|
|
if (isprint(opt->sname))
|
|
ereport(ERROR,
|
|
(errcode(EINVAL),
|
|
errmsg("option -%c, --%s should be %s: '%s'",
|
|
opt->sname, opt->lname, message, optarg)));
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(EINVAL),
|
|
errmsg("option --%s should be %s: '%s'",
|
|
opt->lname, message, optarg)));
|
|
}
|
|
|
|
/*
|
|
* Get configuration from configuration file.
|
|
*/
|
|
void
|
|
pgut_readopt(const char *path, pgut_option options[], int elevel)
|
|
{
|
|
FILE *fp;
|
|
char buf[1024];
|
|
char key[1024];
|
|
char value[1024];
|
|
|
|
if (!options)
|
|
return;
|
|
|
|
if ((fp = pgut_fopen(path, "Rt")) == NULL)
|
|
return;
|
|
|
|
while (fgets(buf, lengthof(buf), fp))
|
|
{
|
|
size_t i;
|
|
|
|
for (i = strlen(buf); i > 0 && IsSpace(buf[i - 1]); i--)
|
|
buf[i - 1] = '\0';
|
|
|
|
if (parse_pair(buf, key, value))
|
|
{
|
|
for (i = 0; options[i].type; i++)
|
|
{
|
|
pgut_option *opt = &options[i];
|
|
|
|
if (pgut_keyeq(key, opt->lname))
|
|
{
|
|
if (opt->allowed == SOURCE_DEFAULT ||
|
|
opt->allowed > SOURCE_FILE)
|
|
elog(elevel, "option %s cannot specified in file", opt->lname);
|
|
else if (opt->source <= SOURCE_FILE)
|
|
pgut_setopt(opt, value, SOURCE_FILE);
|
|
break;
|
|
}
|
|
}
|
|
if (!options[i].type)
|
|
elog(elevel, "invalid option \"%s\"", key);
|
|
}
|
|
}
|
|
|
|
fclose(fp);
|
|
}
|
|
|
|
static const char *
|
|
skip_space(const char *str, const char *line)
|
|
{
|
|
while (IsSpace(*str)) { str++; }
|
|
return str;
|
|
}
|
|
|
|
static const char *
|
|
get_next_token(const char *src, char *dst, const char *line)
|
|
{
|
|
const char *s;
|
|
size_t i;
|
|
size_t j;
|
|
|
|
if ((s = skip_space(src, line)) == NULL)
|
|
return NULL;
|
|
|
|
/* parse quoted string */
|
|
if (*s == '\'')
|
|
{
|
|
s++;
|
|
for (i = 0, j = 0; s[i] != '\0'; i++)
|
|
{
|
|
if (s[i] == '\\')
|
|
{
|
|
i++;
|
|
switch (s[i])
|
|
{
|
|
case 'b':
|
|
dst[j] = '\b';
|
|
break;
|
|
case 'f':
|
|
dst[j] = '\f';
|
|
break;
|
|
case 'n':
|
|
dst[j] = '\n';
|
|
break;
|
|
case 'r':
|
|
dst[j] = '\r';
|
|
break;
|
|
case 't':
|
|
dst[j] = '\t';
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
{
|
|
int k;
|
|
long octVal = 0;
|
|
|
|
for (k = 0;
|
|
s[i + k] >= '0' && s[i + k] <= '7' && k < 3;
|
|
k++)
|
|
octVal = (octVal << 3) + (s[i + k] - '0');
|
|
i += k - 1;
|
|
dst[j] = ((char) octVal);
|
|
}
|
|
break;
|
|
default:
|
|
dst[j] = s[i];
|
|
break;
|
|
}
|
|
}
|
|
else if (s[i] == '\'')
|
|
{
|
|
i++;
|
|
/* doubled quote becomes just one quote */
|
|
if (s[i] == '\'')
|
|
dst[j] = s[i];
|
|
else
|
|
break;
|
|
}
|
|
else
|
|
dst[j] = s[i];
|
|
j++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
i = j = strcspn(s, "# \n\r\t\v");
|
|
memcpy(dst, s, j);
|
|
}
|
|
|
|
dst[j] = '\0';
|
|
return s + i;
|
|
}
|
|
|
|
static bool
|
|
parse_pair(const char buffer[], char key[], char value[])
|
|
{
|
|
const char *start;
|
|
const char *end;
|
|
|
|
key[0] = value[0] = '\0';
|
|
|
|
/*
|
|
* parse key
|
|
*/
|
|
start = buffer;
|
|
if ((start = skip_space(start, buffer)) == NULL)
|
|
return false;
|
|
|
|
end = start + strcspn(start, "=# \n\r\t\v");
|
|
|
|
/* skip blank buffer */
|
|
if (end - start <= 0)
|
|
{
|
|
if (*start == '=')
|
|
elog(WARNING, "syntax error in \"%s\"", buffer);
|
|
return false;
|
|
}
|
|
|
|
/* key found */
|
|
strncpy(key, start, end - start);
|
|
key[end - start] = '\0';
|
|
|
|
/* find key and value split char */
|
|
if ((start = skip_space(end, buffer)) == NULL)
|
|
return false;
|
|
|
|
if (*start != '=')
|
|
{
|
|
elog(WARNING, "syntax error in \"%s\"", buffer);
|
|
return false;
|
|
}
|
|
|
|
start++;
|
|
|
|
/*
|
|
* parse value
|
|
*/
|
|
if ((end = get_next_token(start, value, buffer)) == NULL)
|
|
return false;
|
|
|
|
if ((start = skip_space(end, buffer)) == NULL)
|
|
return false;
|
|
|
|
if (*start != '\0' && *start != '#')
|
|
{
|
|
elog(WARNING, "syntax error in \"%s\"", buffer);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* execute - Execute a SQL and return the result.
|
|
*/
|
|
PGresult *
|
|
execute(const char *query, int nParams, const char **params)
|
|
{
|
|
return pgut_execute(connection, query, nParams, params);
|
|
}
|
|
|
|
PGresult *
|
|
execute_elevel(const char *query, int nParams, const char **params, int elevel)
|
|
{
|
|
return pgut_execute_elevel(connection, query, nParams, params, elevel);
|
|
}
|
|
|
|
/*
|
|
* command - Execute a SQL and discard the result.
|
|
*/
|
|
ExecStatusType
|
|
command(const char *query, int nParams, const char **params)
|
|
{
|
|
return pgut_command(connection, query, nParams, params);
|
|
}
|
|
|
|
static void
|
|
set_elevel(pgut_option *opt, const char *arg)
|
|
{
|
|
pgut_log_level = parse_elevel(arg);
|
|
}
|
|
|
|
static pgut_option default_options[] =
|
|
{
|
|
{ 'b', 'e', "echo" , &pgut_echo },
|
|
{ 'f', 'E', "elevel" , set_elevel },
|
|
{ 's', 'd', "dbname" , &dbname },
|
|
{ 's', 'h', "host" , &host },
|
|
{ 's', 'p', "port" , &port },
|
|
{ 's', 'U', "username" , &username },
|
|
{ 'Y', 'w', "no-password" , &prompt_password },
|
|
{ 'y', 'W', "password" , &prompt_password },
|
|
{ 0 }
|
|
};
|
|
|
|
static size_t
|
|
option_length(const pgut_option opts[])
|
|
{
|
|
size_t len;
|
|
for (len = 0; opts && opts[len].type; len++) { }
|
|
return len;
|
|
}
|
|
|
|
static pgut_option *
|
|
option_find(int c, pgut_option opts1[], pgut_option opts2[])
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; opts1 && opts1[i].type; i++)
|
|
if (opts1[i].sname == c)
|
|
return &opts1[i];
|
|
for (i = 0; opts2 && opts2[i].type; i++)
|
|
if (opts2[i].sname == c)
|
|
return &opts2[i];
|
|
|
|
return NULL; /* not found */
|
|
}
|
|
|
|
/*
|
|
* Returns the current user name.
|
|
*/
|
|
static char *
|
|
get_username(void)
|
|
{
|
|
char *ret;
|
|
|
|
#ifndef WIN32
|
|
struct passwd *pw;
|
|
|
|
pw = getpwuid(geteuid());
|
|
ret = (pw ? pw->pw_name : NULL);
|
|
#else
|
|
static char username[128]; /* remains after function execute */
|
|
DWORD len = sizeof(username) - 1;
|
|
|
|
if (GetUserNameA(username, &len))
|
|
ret = username;
|
|
else
|
|
{
|
|
_dosmaperr(GetLastError());
|
|
ret = NULL;
|
|
}
|
|
#endif
|
|
|
|
if (ret == NULL)
|
|
ereport(ERROR,
|
|
(errcode_errno(),
|
|
errmsg("could not get current user name: ")));
|
|
return ret;
|
|
}
|
|
|
|
static int
|
|
option_has_arg(char type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case 'b':
|
|
case 'B':
|
|
case 'y':
|
|
case 'Y':
|
|
return no_argument;
|
|
default:
|
|
return required_argument;
|
|
}
|
|
}
|
|
|
|
static void
|
|
option_copy(struct option dst[], const pgut_option opts[], size_t len)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
dst[i].name = opts[i].lname;
|
|
dst[i].has_arg = option_has_arg(opts[i].type);
|
|
dst[i].flag = NULL;
|
|
dst[i].val = opts[i].sname;
|
|
}
|
|
}
|
|
|
|
static struct option *
|
|
option_merge(const pgut_option opts1[], const pgut_option opts2[])
|
|
{
|
|
struct option *result;
|
|
size_t len1 = option_length(opts1);
|
|
size_t len2 = option_length(opts2);
|
|
size_t n = len1 + len2;
|
|
|
|
result = pgut_newarray(struct option, n + 1);
|
|
option_copy(result, opts1, len1);
|
|
option_copy(result + len1, opts2, len2);
|
|
memset(&result[n], 0, sizeof(pgut_option));
|
|
|
|
return result;
|
|
}
|
|
|
|
static char *
|
|
longopts_to_optstring(const struct option opts[])
|
|
{
|
|
size_t len;
|
|
char *result;
|
|
char *s;
|
|
|
|
for (len = 0; opts[len].name; len++) { }
|
|
result = pgut_malloc(len * 2 + 1);
|
|
|
|
s = result;
|
|
for (len = 0; opts[len].name; len++)
|
|
{
|
|
if (!isprint(opts[len].val))
|
|
continue;
|
|
*s++ = opts[len].val;
|
|
if (opts[len].has_arg != no_argument)
|
|
*s++ = ':';
|
|
}
|
|
*s = '\0';
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
pgut_getopt(int argc, char **argv, pgut_option options[])
|
|
{
|
|
int c;
|
|
int optindex = 0;
|
|
char *optstring;
|
|
struct option *longopts;
|
|
pgut_option *opt;
|
|
|
|
pgut_init(argc, argv);
|
|
|
|
/* Help message and version are handled at first. */
|
|
if (argc > 1)
|
|
{
|
|
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
|
|
{
|
|
help(true);
|
|
exit(0);
|
|
}
|
|
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
|
|
{
|
|
printf("%s %s\n", PROGRAM_NAME, PROGRAM_VERSION);
|
|
exit(0);
|
|
}
|
|
if (strcmp(argv[1], "--configuration") == 0)
|
|
{
|
|
printf("%s\n", PG_VERSION_STR);
|
|
exit(0);
|
|
}
|
|
}
|
|
|
|
/* Merge default and user options. */
|
|
longopts = option_merge(default_options, options);
|
|
optstring = longopts_to_optstring(longopts);
|
|
|
|
/* Assign named options */
|
|
while ((c = getopt_long(argc, argv, optstring, longopts, &optindex)) != -1)
|
|
{
|
|
opt = option_find(c, default_options, options);
|
|
pgut_setopt(opt, optarg, SOURCE_CMDLINE);
|
|
}
|
|
|
|
/* Read environment variables */
|
|
option_from_env(options);
|
|
(void) (dbname ||
|
|
(dbname = getenv("PGDATABASE")) ||
|
|
(dbname = getenv("PGUSER")) ||
|
|
(dbname = get_username()));
|
|
|
|
return optind;
|
|
}
|
|
|
|
void
|
|
help(bool details)
|
|
{
|
|
pgut_help(details);
|
|
|
|
if (details)
|
|
{
|
|
printf("\nConnection options:\n");
|
|
printf(" -d, --dbname=DBNAME database to connect\n");
|
|
printf(" -h, --host=HOSTNAME database server host or socket directory\n");
|
|
printf(" -p, --port=PORT database server port\n");
|
|
printf(" -U, --username=USERNAME user name to connect as\n");
|
|
printf(" -w, --no-password never prompt for password\n");
|
|
printf(" -W, --password force password prompt\n");
|
|
}
|
|
|
|
printf("\nGeneric options:\n");
|
|
if (details)
|
|
{
|
|
printf(" -e, --echo echo queries\n");
|
|
printf(" -E, --elevel=LEVEL set output message level\n");
|
|
}
|
|
printf(" --help show this help, then exit\n");
|
|
printf(" --version output version information, then exit\n");
|
|
|
|
if (details && (PROGRAM_URL || PROGRAM_EMAIL))
|
|
{
|
|
printf("\n");
|
|
if (PROGRAM_URL)
|
|
printf("Read the website for details: <%s>.\n", PROGRAM_URL);
|
|
if (PROGRAM_EMAIL)
|
|
printf("Report bugs to <%s>.\n", PROGRAM_EMAIL);
|
|
}
|
|
}
|