diff options
Diffstat (limited to 'src/bin/scripts')
-rw-r--r-- | src/bin/scripts/clusterdb.c | 12 | ||||
-rw-r--r-- | src/bin/scripts/common.c | 137 | ||||
-rw-r--r-- | src/bin/scripts/common.h | 10 | ||||
-rw-r--r-- | src/bin/scripts/createdb.c | 2 | ||||
-rw-r--r-- | src/bin/scripts/createuser.c | 2 | ||||
-rw-r--r-- | src/bin/scripts/dropdb.c | 3 | ||||
-rw-r--r-- | src/bin/scripts/dropuser.c | 2 | ||||
-rw-r--r-- | src/bin/scripts/reindexdb.c | 23 | ||||
-rw-r--r-- | src/bin/scripts/t/010_clusterdb.pl | 2 | ||||
-rw-r--r-- | src/bin/scripts/t/090_reindexdb.pl | 6 | ||||
-rw-r--r-- | src/bin/scripts/t/100_vacuumdb.pl | 28 | ||||
-rw-r--r-- | src/bin/scripts/vacuumdb.c | 29 |
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, ';'); } |