
The existing facility of vacuumdb to handle parallel connections into a given database with an authentication set is moved to a common file in src/bin/scripts/, named scripts_parallel.c. This introduces a set of routines to initialize, wait and terminate a set of connections, simplifying a bit the code of vacuumdb on the way. More routines related to result handling and database connection are moved to common.c. The initial plan is to use that for reindexdb, but it could be applied to other tools like clusterdb. While on it, clean up a set of variables "progname" which were defined as routine arguments for error messages. Since most of the callers have switched to pg_log_error() and such there is no need for this variable. Author: Julien Rouhaud Reviewed-by: Michael Paquier, Álvaro Herrera Discussion: https://postgr.es/m/CAOBaU_YrnH_Jqo46NhaJ7uRBiWWEcS40VNRQxgFbqYo9kApUsg@mail.gmail.com
295 lines
7.7 KiB
C
295 lines
7.7 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* clusterdb
|
|
*
|
|
* Portions Copyright (c) 2002-2019, PostgreSQL Global Development Group
|
|
*
|
|
* src/bin/scripts/clusterdb.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres_fe.h"
|
|
#include "common.h"
|
|
#include "common/logging.h"
|
|
#include "fe_utils/simple_list.h"
|
|
#include "fe_utils/string_utils.h"
|
|
|
|
|
|
static void cluster_one_database(const char *dbname, bool verbose, const char *table,
|
|
const char *host, const char *port,
|
|
const char *username, enum trivalue prompt_password,
|
|
const char *progname, bool echo);
|
|
static void cluster_all_databases(bool verbose, const char *maintenance_db,
|
|
const char *host, const char *port,
|
|
const char *username, enum trivalue prompt_password,
|
|
const char *progname, bool echo, bool quiet);
|
|
|
|
static void help(const char *progname);
|
|
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
static struct option long_options[] = {
|
|
{"host", required_argument, NULL, 'h'},
|
|
{"port", required_argument, NULL, 'p'},
|
|
{"username", required_argument, NULL, 'U'},
|
|
{"no-password", no_argument, NULL, 'w'},
|
|
{"password", no_argument, NULL, 'W'},
|
|
{"echo", no_argument, NULL, 'e'},
|
|
{"quiet", no_argument, NULL, 'q'},
|
|
{"dbname", required_argument, NULL, 'd'},
|
|
{"all", no_argument, NULL, 'a'},
|
|
{"table", required_argument, NULL, 't'},
|
|
{"verbose", no_argument, NULL, 'v'},
|
|
{"maintenance-db", required_argument, NULL, 2},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
const char *progname;
|
|
int optindex;
|
|
int c;
|
|
|
|
const char *dbname = NULL;
|
|
const char *maintenance_db = NULL;
|
|
char *host = NULL;
|
|
char *port = NULL;
|
|
char *username = NULL;
|
|
enum trivalue prompt_password = TRI_DEFAULT;
|
|
bool echo = false;
|
|
bool quiet = false;
|
|
bool alldb = false;
|
|
bool verbose = false;
|
|
SimpleStringList tables = {NULL, NULL};
|
|
|
|
pg_logging_init(argv[0]);
|
|
progname = get_progname(argv[0]);
|
|
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pgscripts"));
|
|
|
|
handle_help_version_opts(argc, argv, "clusterdb", help);
|
|
|
|
while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:at:v", long_options, &optindex)) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'h':
|
|
host = pg_strdup(optarg);
|
|
break;
|
|
case 'p':
|
|
port = pg_strdup(optarg);
|
|
break;
|
|
case 'U':
|
|
username = pg_strdup(optarg);
|
|
break;
|
|
case 'w':
|
|
prompt_password = TRI_NO;
|
|
break;
|
|
case 'W':
|
|
prompt_password = TRI_YES;
|
|
break;
|
|
case 'e':
|
|
echo = true;
|
|
break;
|
|
case 'q':
|
|
quiet = true;
|
|
break;
|
|
case 'd':
|
|
dbname = pg_strdup(optarg);
|
|
break;
|
|
case 'a':
|
|
alldb = true;
|
|
break;
|
|
case 't':
|
|
simple_string_list_append(&tables, optarg);
|
|
break;
|
|
case 'v':
|
|
verbose = true;
|
|
break;
|
|
case 2:
|
|
maintenance_db = pg_strdup(optarg);
|
|
break;
|
|
default:
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Non-option argument specifies database name as long as it wasn't
|
|
* already specified with -d / --dbname
|
|
*/
|
|
if (optind < argc && dbname == NULL)
|
|
{
|
|
dbname = argv[optind];
|
|
optind++;
|
|
}
|
|
|
|
if (optind < argc)
|
|
{
|
|
pg_log_error("too many command-line arguments (first is \"%s\")",
|
|
argv[optind]);
|
|
fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
|
|
exit(1);
|
|
}
|
|
|
|
setup_cancel_handler();
|
|
|
|
if (alldb)
|
|
{
|
|
if (dbname)
|
|
{
|
|
pg_log_error("cannot cluster all databases and a specific one at the same time");
|
|
exit(1);
|
|
}
|
|
|
|
if (tables.head != NULL)
|
|
{
|
|
pg_log_error("cannot cluster specific table(s) in all databases");
|
|
exit(1);
|
|
}
|
|
|
|
cluster_all_databases(verbose, maintenance_db, host, port, username, prompt_password,
|
|
progname, echo, quiet);
|
|
}
|
|
else
|
|
{
|
|
if (dbname == NULL)
|
|
{
|
|
if (getenv("PGDATABASE"))
|
|
dbname = getenv("PGDATABASE");
|
|
else if (getenv("PGUSER"))
|
|
dbname = getenv("PGUSER");
|
|
else
|
|
dbname = get_user_name_or_exit(progname);
|
|
}
|
|
|
|
if (tables.head != NULL)
|
|
{
|
|
SimpleStringListCell *cell;
|
|
|
|
for (cell = tables.head; cell; cell = cell->next)
|
|
{
|
|
cluster_one_database(dbname, verbose, cell->val,
|
|
host, port, username, prompt_password,
|
|
progname, echo);
|
|
}
|
|
}
|
|
else
|
|
cluster_one_database(dbname, verbose, NULL,
|
|
host, port, username, prompt_password,
|
|
progname, echo);
|
|
}
|
|
|
|
exit(0);
|
|
}
|
|
|
|
|
|
static void
|
|
cluster_one_database(const char *dbname, bool verbose, const char *table,
|
|
const char *host, const char *port,
|
|
const char *username, enum trivalue prompt_password,
|
|
const char *progname, bool echo)
|
|
{
|
|
PQExpBufferData sql;
|
|
|
|
PGconn *conn;
|
|
|
|
conn = connectDatabase(dbname, host, port, username, prompt_password,
|
|
progname, echo, false, false);
|
|
|
|
initPQExpBuffer(&sql);
|
|
|
|
appendPQExpBufferStr(&sql, "CLUSTER");
|
|
if (verbose)
|
|
appendPQExpBufferStr(&sql, " VERBOSE");
|
|
if (table)
|
|
{
|
|
appendPQExpBufferChar(&sql, ' ');
|
|
appendQualifiedRelation(&sql, table, conn, echo);
|
|
}
|
|
appendPQExpBufferChar(&sql, ';');
|
|
|
|
if (!executeMaintenanceCommand(conn, sql.data, echo))
|
|
{
|
|
if (table)
|
|
pg_log_error("clustering of table \"%s\" in database \"%s\" failed: %s",
|
|
table, PQdb(conn), PQerrorMessage(conn));
|
|
else
|
|
pg_log_error("clustering of database \"%s\" failed: %s",
|
|
PQdb(conn), PQerrorMessage(conn));
|
|
PQfinish(conn);
|
|
exit(1);
|
|
}
|
|
PQfinish(conn);
|
|
termPQExpBuffer(&sql);
|
|
}
|
|
|
|
|
|
static void
|
|
cluster_all_databases(bool verbose, const char *maintenance_db,
|
|
const char *host, const char *port,
|
|
const char *username, enum trivalue prompt_password,
|
|
const char *progname, bool echo, bool quiet)
|
|
{
|
|
PGconn *conn;
|
|
PGresult *result;
|
|
PQExpBufferData connstr;
|
|
int i;
|
|
|
|
conn = connectMaintenanceDatabase(maintenance_db, host, port, username,
|
|
prompt_password, progname, echo);
|
|
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", echo);
|
|
PQfinish(conn);
|
|
|
|
initPQExpBuffer(&connstr);
|
|
for (i = 0; i < PQntuples(result); i++)
|
|
{
|
|
char *dbname = PQgetvalue(result, i, 0);
|
|
|
|
if (!quiet)
|
|
{
|
|
printf(_("%s: clustering database \"%s\"\n"), progname, dbname);
|
|
fflush(stdout);
|
|
}
|
|
|
|
resetPQExpBuffer(&connstr);
|
|
appendPQExpBufferStr(&connstr, "dbname=");
|
|
appendConnStrVal(&connstr, dbname);
|
|
|
|
cluster_one_database(connstr.data, verbose, NULL,
|
|
host, port, username, prompt_password,
|
|
progname, echo);
|
|
}
|
|
termPQExpBuffer(&connstr);
|
|
|
|
PQclear(result);
|
|
}
|
|
|
|
|
|
static void
|
|
help(const char *progname)
|
|
{
|
|
printf(_("%s clusters all previously clustered tables in a database.\n\n"), progname);
|
|
printf(_("Usage:\n"));
|
|
printf(_(" %s [OPTION]... [DBNAME]\n"), progname);
|
|
printf(_("\nOptions:\n"));
|
|
printf(_(" -a, --all cluster all databases\n"));
|
|
printf(_(" -d, --dbname=DBNAME database to cluster\n"));
|
|
printf(_(" -e, --echo show the commands being sent to the server\n"));
|
|
printf(_(" -q, --quiet don't write any messages\n"));
|
|
printf(_(" -t, --table=TABLE cluster specific table(s) only\n"));
|
|
printf(_(" -v, --verbose write a lot of output\n"));
|
|
printf(_(" -V, --version output version information, then exit\n"));
|
|
printf(_(" -?, --help show this help, then exit\n"));
|
|
printf(_("\nConnection options:\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(_(" --maintenance-db=DBNAME alternate maintenance database\n"));
|
|
printf(_("\nRead the description of the SQL command CLUSTER for details.\n"));
|
|
printf(_("\nReport bugs to <pgsql-bugs@lists.postgresql.org>.\n"));
|
|
}
|