summaryrefslogtreecommitdiff
path: root/src/bin/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/scripts')
-rw-r--r--src/bin/scripts/clusterdb.c12
-rw-r--r--src/bin/scripts/common.c137
-rw-r--r--src/bin/scripts/common.h10
-rw-r--r--src/bin/scripts/createdb.c2
-rw-r--r--src/bin/scripts/createuser.c2
-rw-r--r--src/bin/scripts/dropdb.c3
-rw-r--r--src/bin/scripts/dropuser.c2
-rw-r--r--src/bin/scripts/reindexdb.c23
-rw-r--r--src/bin/scripts/t/010_clusterdb.pl2
-rw-r--r--src/bin/scripts/t/090_reindexdb.pl6
-rw-r--r--src/bin/scripts/t/100_vacuumdb.pl28
-rw-r--r--src/bin/scripts/vacuumdb.c29
12 files changed, 207 insertions, 49 deletions
diff --git a/src/bin/scripts/clusterdb.c b/src/bin/scripts/clusterdb.c
index 92c42f62bf3..650d2ae2610 100644
--- a/src/bin/scripts/clusterdb.c
+++ b/src/bin/scripts/clusterdb.c
@@ -195,17 +195,21 @@ cluster_one_database(const char *dbname, bool verbose, const char *table,
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)
- appendPQExpBuffer(&sql, " %s", table);
+ {
+ appendPQExpBufferChar(&sql, ' ');
+ appendQualifiedRelation(&sql, table, conn, progname, echo);
+ }
appendPQExpBufferChar(&sql, ';');
- conn = connectDatabase(dbname, host, port, username, prompt_password,
- progname, false, false);
if (!executeMaintenanceCommand(conn, sql.data, echo))
{
if (table)
@@ -234,7 +238,7 @@ cluster_all_databases(bool verbose, const char *maintenance_db,
int i;
conn = connectMaintenanceDatabase(maintenance_db, host, port, username,
- prompt_password, progname);
+ prompt_password, progname, echo);
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo);
PQfinish(conn);
diff --git a/src/bin/scripts/common.c b/src/bin/scripts/common.c
index e20a5e91468..db2b9f0d683 100644
--- a/src/bin/scripts/common.c
+++ b/src/bin/scripts/common.c
@@ -18,6 +18,8 @@
#include <unistd.h>
#include "common.h"
+#include "fe_utils/connect.h"
+#include "fe_utils/string_utils.h"
static PGcancel *volatile cancelConn = NULL;
@@ -63,9 +65,10 @@ handle_help_version_opts(int argc, char *argv[],
* as before, else we might create password exposure hazards.)
*/
PGconn *
-connectDatabase(const char *dbname, const char *pghost, const char *pgport,
- const char *pguser, enum trivalue prompt_password,
- const char *progname, bool fail_ok, bool allow_password_reuse)
+connectDatabase(const char *dbname, const char *pghost,
+ const char *pgport, const char *pguser,
+ enum trivalue prompt_password, const char *progname,
+ bool echo, bool fail_ok, bool allow_password_reuse)
{
PGconn *conn;
bool new_pass;
@@ -142,6 +145,10 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
exit(1);
}
+ if (PQserverVersion(conn) >= 70300)
+ PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL,
+ progname, echo));
+
return conn;
}
@@ -149,24 +156,24 @@ connectDatabase(const char *dbname, const char *pghost, const char *pgport,
* Try to connect to the appropriate maintenance database.
*/
PGconn *
-connectMaintenanceDatabase(const char *maintenance_db, const char *pghost,
- const char *pgport, const char *pguser,
- enum trivalue prompt_password,
- const char *progname)
+connectMaintenanceDatabase(const char *maintenance_db,
+ const char *pghost, const char *pgport,
+ const char *pguser, enum trivalue prompt_password,
+ const char *progname, bool echo)
{
PGconn *conn;
/* If a maintenance database name was specified, just connect to it. */
if (maintenance_db)
return connectDatabase(maintenance_db, pghost, pgport, pguser,
- prompt_password, progname, false, false);
+ prompt_password, progname, echo, false, false);
/* Otherwise, try postgres first and then template1. */
conn = connectDatabase("postgres", pghost, pgport, pguser, prompt_password,
- progname, true, false);
+ progname, echo, true, false);
if (!conn)
conn = connectDatabase("template1", pghost, pgport, pguser,
- prompt_password, progname, false, false);
+ prompt_password, progname, echo, false, false);
return conn;
}
@@ -252,6 +259,116 @@ executeMaintenanceCommand(PGconn *conn, const char *query, bool echo)
return r;
}
+
+/*
+ * Split TABLE[(COLUMNS)] into TABLE and [(COLUMNS)] portions. When you
+ * finish using them, pg_free(*table). *columns is a pointer into "spec",
+ * possibly to its NUL terminator.
+ */
+static void
+split_table_columns_spec(const char *spec, int encoding,
+ char **table, const char **columns)
+{
+ bool inquotes = false;
+ const char *cp = spec;
+
+ /*
+ * Find the first '(' not identifier-quoted. Based on
+ * dequote_downcase_identifier().
+ */
+ while (*cp && (*cp != '(' || inquotes))
+ {
+ if (*cp == '"')
+ {
+ if (inquotes && cp[1] == '"')
+ cp++; /* pair does not affect quoting */
+ else
+ inquotes = !inquotes;
+ cp++;
+ }
+ else
+ cp += PQmblen(cp, encoding);
+ }
+ *table = pg_strdup(spec);
+ (*table)[cp - spec] = '\0'; /* no strndup */
+ *columns = cp;
+}
+
+/*
+ * Break apart TABLE[(COLUMNS)] of "spec". With the reset_val of search_path
+ * in effect, have regclassin() interpret the TABLE portion. Append to "buf"
+ * the qualified name of TABLE, followed by any (COLUMNS). Exit on failure.
+ * We use this to interpret --table=foo under the search path psql would get,
+ * in advance of "ANALYZE public.foo" under the always-secure search path.
+ */
+void
+appendQualifiedRelation(PQExpBuffer buf, const char *spec,
+ PGconn *conn, const char *progname, bool echo)
+{
+ char *table;
+ const char *columns;
+ PQExpBufferData sql;
+ PGresult *res;
+ int ntups;
+
+ /* Before 7.3, the concept of qualifying a name did not exist. */
+ if (PQserverVersion(conn) < 70300)
+ {
+ appendPQExpBufferStr(&sql, spec);
+ return;
+ }
+
+ split_table_columns_spec(spec, PQclientEncoding(conn), &table, &columns);
+
+ /*
+ * Query must remain ABSOLUTELY devoid of unqualified names. This would
+ * be unnecessary given a regclassin() variant taking a search_path
+ * argument.
+ */
+ initPQExpBuffer(&sql);
+ appendPQExpBufferStr(&sql,
+ "SELECT c.relname, ns.nspname\n"
+ " FROM pg_catalog.pg_class c,"
+ " pg_catalog.pg_namespace ns\n"
+ " WHERE c.relnamespace OPERATOR(pg_catalog.=) ns.oid\n"
+ " AND c.oid OPERATOR(pg_catalog.=) ");
+ appendStringLiteralConn(&sql, table, conn);
+ appendPQExpBufferStr(&sql, "::pg_catalog.regclass;");
+
+ executeCommand(conn, "RESET search_path", progname, echo);
+
+ /*
+ * One row is a typical result, as is a nonexistent relation ERROR.
+ * regclassin() unconditionally accepts all-digits input as an OID; if no
+ * relation has that OID; this query returns no rows. Catalog corruption
+ * might elicit other row counts.
+ */
+ res = executeQuery(conn, sql.data, progname, echo);
+ ntups = PQntuples(res);
+ if (ntups != 1)
+ {
+ fprintf(stderr,
+ ngettext("%s: query returned %d row instead of one: %s\n",
+ "%s: query returned %d rows instead of one: %s\n",
+ ntups),
+ progname, ntups, sql.data);
+ PQfinish(conn);
+ exit(1);
+ }
+ appendPQExpBufferStr(buf,
+ fmtQualifiedId(PQserverVersion(conn),
+ PQgetvalue(res, 0, 1),
+ PQgetvalue(res, 0, 0)));
+ appendPQExpBufferStr(buf, columns);
+ PQclear(res);
+ termPQExpBuffer(&sql);
+ pg_free(table);
+
+ PQclear(executeQuery(conn, ALWAYS_SECURE_SEARCH_PATH_SQL,
+ progname, echo));
+}
+
+
/*
* Check yes/no answer in a localized way. 1=yes, 0=no, -1=neither.
*/
diff --git a/src/bin/scripts/common.h b/src/bin/scripts/common.h
index a660d6848ad..30a39a62475 100644
--- a/src/bin/scripts/common.h
+++ b/src/bin/scripts/common.h
@@ -32,11 +32,12 @@ extern void handle_help_version_opts(int argc, char *argv[],
extern PGconn *connectDatabase(const char *dbname, const char *pghost,
const char *pgport, const char *pguser,
enum trivalue prompt_password, const char *progname,
- bool fail_ok, bool allow_password_reuse);
+ bool echo, bool fail_ok, bool allow_password_reuse);
extern PGconn *connectMaintenanceDatabase(const char *maintenance_db,
- const char *pghost, const char *pgport, const char *pguser,
- enum trivalue prompt_password, const char *progname);
+ const char *pghost, const char *pgport,
+ const char *pguser, enum trivalue prompt_password,
+ const char *progname, bool echo);
extern PGresult *executeQuery(PGconn *conn, const char *query,
const char *progname, bool echo);
@@ -47,6 +48,9 @@ extern void executeCommand(PGconn *conn, const char *query,
extern bool executeMaintenanceCommand(PGconn *conn, const char *query,
bool echo);
+extern void appendQualifiedRelation(PQExpBuffer buf, const char *name,
+ PGconn *conn, const char *progname, bool echo);
+
extern bool yesno_prompt(const char *question);
extern void setup_cancel_handler(void);
diff --git a/src/bin/scripts/createdb.c b/src/bin/scripts/createdb.c
index 81a81921366..fc108882e43 100644
--- a/src/bin/scripts/createdb.c
+++ b/src/bin/scripts/createdb.c
@@ -202,7 +202,7 @@ main(int argc, char *argv[])
maintenance_db = "template1";
conn = connectMaintenanceDatabase(maintenance_db, host, port, username,
- prompt_password, progname);
+ prompt_password, progname, echo);
if (echo)
printf("%s\n", sql.data);
diff --git a/src/bin/scripts/createuser.c b/src/bin/scripts/createuser.c
index c488c018e08..3420e62fdd9 100644
--- a/src/bin/scripts/createuser.c
+++ b/src/bin/scripts/createuser.c
@@ -252,7 +252,7 @@ main(int argc, char *argv[])
login = TRI_YES;
conn = connectDatabase("postgres", host, port, username, prompt_password,
- progname, false, false);
+ progname, echo, false, false);
initPQExpBuffer(&sql);
diff --git a/src/bin/scripts/dropdb.c b/src/bin/scripts/dropdb.c
index 81929c43c41..ba0038891d6 100644
--- a/src/bin/scripts/dropdb.c
+++ b/src/bin/scripts/dropdb.c
@@ -129,7 +129,8 @@ main(int argc, char *argv[])
maintenance_db = "template1";
conn = connectMaintenanceDatabase(maintenance_db,
- host, port, username, prompt_password, progname);
+ host, port, username, prompt_password,
+ progname, echo);
if (echo)
printf("%s\n", sql.data);
diff --git a/src/bin/scripts/dropuser.c b/src/bin/scripts/dropuser.c
index e3191afc311..d9e7f7b0364 100644
--- a/src/bin/scripts/dropuser.c
+++ b/src/bin/scripts/dropuser.c
@@ -134,7 +134,7 @@ main(int argc, char *argv[])
(if_exists ? "IF EXISTS " : ""), fmtId(dropuser));
conn = connectDatabase("postgres", host, port, username, prompt_password,
- progname, false, false);
+ progname, echo, false, false);
if (echo)
printf("%s\n", sql.data);
diff --git a/src/bin/scripts/reindexdb.c b/src/bin/scripts/reindexdb.c
index 64e9a2f3ce7..be1c06ebbdb 100644
--- a/src/bin/scripts/reindexdb.c
+++ b/src/bin/scripts/reindexdb.c
@@ -282,23 +282,24 @@ reindex_one_database(const char *name, const char *dbname, const char *type,
PGconn *conn;
conn = connectDatabase(dbname, host, port, username, prompt_password,
- progname, false, false);
+ progname, echo, false, false);
initPQExpBuffer(&sql);
- appendPQExpBufferStr(&sql, "REINDEX");
+ appendPQExpBufferStr(&sql, "REINDEX ");
if (verbose)
- appendPQExpBufferStr(&sql, " (VERBOSE)");
+ appendPQExpBufferStr(&sql, "(VERBOSE) ");
- if (strcmp(type, "TABLE") == 0)
- appendPQExpBuffer(&sql, " TABLE %s", name);
- else if (strcmp(type, "INDEX") == 0)
- appendPQExpBuffer(&sql, " INDEX %s", name);
+ appendPQExpBufferStr(&sql, type);
+ appendPQExpBufferChar(&sql, ' ');
+ if (strcmp(type, "TABLE") == 0 ||
+ strcmp(type, "INDEX") == 0)
+ appendQualifiedRelation(&sql, name, conn, progname, echo);
else if (strcmp(type, "SCHEMA") == 0)
- appendPQExpBuffer(&sql, " SCHEMA %s", name);
+ appendPQExpBufferStr(&sql, name);
else if (strcmp(type, "DATABASE") == 0)
- appendPQExpBuffer(&sql, " DATABASE %s", fmtId(PQdb(conn)));
+ appendPQExpBufferStr(&sql, fmtId(PQdb(conn)));
appendPQExpBufferChar(&sql, ';');
if (!executeMaintenanceCommand(conn, sql.data, echo))
@@ -335,7 +336,7 @@ reindex_all_databases(const char *maintenance_db,
int i;
conn = connectMaintenanceDatabase(maintenance_db, host, port, username,
- prompt_password, progname);
+ prompt_password, progname, echo);
result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo);
PQfinish(conn);
@@ -372,7 +373,7 @@ reindex_system_catalogs(const char *dbname, const char *host, const char *port,
PQExpBufferData sql;
conn = connectDatabase(dbname, host, port, username, prompt_password,
- progname, false, false);
+ progname, echo, false, false);
initPQExpBuffer(&sql);
diff --git a/src/bin/scripts/t/010_clusterdb.pl b/src/bin/scripts/t/010_clusterdb.pl
index e2cff0fcab3..a767338f920 100644
--- a/src/bin/scripts/t/010_clusterdb.pl
+++ b/src/bin/scripts/t/010_clusterdb.pl
@@ -26,7 +26,7 @@ $node->safe_psql('postgres',
);
$node->issues_sql_like(
[ 'clusterdb', '-t', 'test1' ],
- qr/statement: CLUSTER test1;/,
+ qr/statement: CLUSTER public\.test1;/,
'cluster specific table');
$node->command_ok([qw(clusterdb --echo --verbose dbname=template1)],
diff --git a/src/bin/scripts/t/090_reindexdb.pl b/src/bin/scripts/t/090_reindexdb.pl
index 3aa3a953506..e57a5e2bad5 100644
--- a/src/bin/scripts/t/090_reindexdb.pl
+++ b/src/bin/scripts/t/090_reindexdb.pl
@@ -24,11 +24,11 @@ $node->safe_psql('postgres',
'CREATE TABLE test1 (a int); CREATE INDEX test1x ON test1 (a);');
$node->issues_sql_like(
[ 'reindexdb', '-t', 'test1', 'postgres' ],
- qr/statement: REINDEX TABLE test1;/,
+ qr/statement: REINDEX TABLE public\.test1;/,
'reindex specific table');
$node->issues_sql_like(
[ 'reindexdb', '-i', 'test1x', 'postgres' ],
- qr/statement: REINDEX INDEX test1x;/,
+ qr/statement: REINDEX INDEX public\.test1x;/,
'reindex specific index');
$node->issues_sql_like(
[ 'reindexdb', '-S', 'pg_catalog', 'postgres' ],
@@ -40,7 +40,7 @@ $node->issues_sql_like(
'reindex system tables');
$node->issues_sql_like(
[ 'reindexdb', '-v', '-t', 'test1', 'postgres' ],
- qr/statement: REINDEX \(VERBOSE\) TABLE test1;/,
+ qr/statement: REINDEX \(VERBOSE\) TABLE public\.test1;/,
'reindex with verbose output');
$node->command_ok([qw(reindexdb --echo --table=pg_am dbname=template1)],
diff --git a/src/bin/scripts/t/100_vacuumdb.pl b/src/bin/scripts/t/100_vacuumdb.pl
index dd98df8c08a..382210e3b6e 100644
--- a/src/bin/scripts/t/100_vacuumdb.pl
+++ b/src/bin/scripts/t/100_vacuumdb.pl
@@ -3,7 +3,7 @@ use warnings;
use PostgresNode;
use TestLib;
-use Test::More tests => 19;
+use Test::More tests => 23;
program_help_ok('vacuumdb');
program_version_ok('vacuumdb');
@@ -26,12 +26,32 @@ $node->issues_sql_like(
qr/statement: VACUUM \(FREEZE\);/,
'vacuumdb -F');
$node->issues_sql_like(
- [ 'vacuumdb', '-z', 'postgres' ],
- qr/statement: VACUUM \(ANALYZE\);/,
- 'vacuumdb -z');
+ [ 'vacuumdb', '-zj2', 'postgres' ],
+ qr/statement: VACUUM \(ANALYZE\) pg_catalog\./,
+ 'vacuumdb -zj2');
$node->issues_sql_like(
[ 'vacuumdb', '-Z', 'postgres' ],
qr/statement: ANALYZE;/,
'vacuumdb -Z');
$node->command_ok([qw(vacuumdb -Z --table=pg_am dbname=template1)],
'vacuumdb with connection string');
+
+$node->command_fails([qw(vacuumdb -Zt pg_am;ABORT postgres)],
+ 'trailing command in "-t", without COLUMNS');
+# Unwanted; better if it failed.
+$node->command_ok([qw(vacuumdb -Zt pg_am(amname);ABORT postgres)],
+ 'trailing command in "-t", with COLUMNS');
+
+$node->safe_psql('postgres', q|
+ CREATE TABLE "need""q(uot" (")x" text);
+
+ CREATE FUNCTION f0(int) RETURNS int LANGUAGE SQL AS 'SELECT $1 * $1';
+ CREATE FUNCTION f1(int) RETURNS int LANGUAGE SQL AS 'SELECT f0($1)';
+ CREATE TABLE funcidx (x int);
+ INSERT INTO funcidx VALUES (0),(1),(2),(3);
+ CREATE INDEX i0 ON funcidx ((f1(x)));
+|);
+$node->command_ok([qw|vacuumdb -Z --table="need""q(uot"(")x") postgres|],
+ 'column list');
+$node->command_fails([qw|vacuumdb -Zt funcidx postgres|],
+ 'unqualifed name via functional index');
diff --git a/src/bin/scripts/vacuumdb.c b/src/bin/scripts/vacuumdb.c
index 663083828ec..887fa48fbdf 100644
--- a/src/bin/scripts/vacuumdb.c
+++ b/src/bin/scripts/vacuumdb.c
@@ -61,7 +61,9 @@ static void vacuum_all_databases(vacuumingOptions *vacopts,
const char *progname, bool echo, bool quiet);
static void prepare_vacuum_command(PQExpBuffer sql, PGconn *conn,
- vacuumingOptions *vacopts, const char *table);
+ vacuumingOptions *vacopts, const char *table,
+ bool table_pre_qualified,
+ const char *progname, bool echo);
static void run_vacuum_command(PGconn *conn, const char *sql, bool echo,
const char *table, const char *progname, bool async);
@@ -361,7 +363,7 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
(stage >= 0 && stage < ANALYZE_NUM_STAGES));
conn = connectDatabase(dbname, host, port, username, prompt_password,
- progname, false, true);
+ progname, echo, false, true);
if (!quiet)
{
@@ -437,7 +439,7 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
for (i = 1; i < concurrentCons; i++)
{
conn = connectDatabase(dbname, host, port, username, prompt_password,
- progname, false, true);
+ progname, echo, false, true);
init_slot(slots + i, conn, progname);
}
}
@@ -463,7 +465,8 @@ vacuum_one_database(const char *dbname, vacuumingOptions *vacopts,
ParallelSlot *free_slot;
const char *tabname = cell ? cell->val : NULL;
- prepare_vacuum_command(&sql, conn, vacopts, tabname);
+ prepare_vacuum_command(&sql, conn, vacopts, tabname,
+ tables == &dbtables, progname, echo);
if (CancelRequested)
{
@@ -554,8 +557,8 @@ vacuum_all_databases(vacuumingOptions *vacopts,
int stage;
int i;
- conn = connectMaintenanceDatabase(maintenance_db, host, port,
- username, prompt_password, progname);
+ conn = connectMaintenanceDatabase(maintenance_db, host, port, username,
+ prompt_password, progname, echo);
result = executeQuery(conn,
"SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;",
progname, echo);
@@ -618,8 +621,10 @@ vacuum_all_databases(vacuumingOptions *vacopts,
* quoted. The command is semicolon-terminated.
*/
static void
-prepare_vacuum_command(PQExpBuffer sql, PGconn *conn, vacuumingOptions *vacopts,
- const char *table)
+prepare_vacuum_command(PQExpBuffer sql, PGconn *conn,
+ vacuumingOptions *vacopts, const char *table,
+ bool table_pre_qualified,
+ const char *progname, bool echo)
{
resetPQExpBuffer(sql);
@@ -675,7 +680,13 @@ prepare_vacuum_command(PQExpBuffer sql, PGconn *conn, vacuumingOptions *vacopts,
}
if (table)
- appendPQExpBuffer(sql, " %s", table);
+ {
+ appendPQExpBufferChar(sql, ' ');
+ if (table_pre_qualified)
+ appendPQExpBufferStr(sql, table);
+ else
+ appendQualifiedRelation(sql, table, conn, progname, echo);
+ }
appendPQExpBufferChar(sql, ';');
}