| 1 | /*------------------------------------------------------------------------- | 
|---|
| 2 | * | 
|---|
| 3 | * clusterdb | 
|---|
| 4 | * | 
|---|
| 5 | * Portions Copyright (c) 2002-2019, PostgreSQL Global Development Group | 
|---|
| 6 | * | 
|---|
| 7 | * src/bin/scripts/clusterdb.c | 
|---|
| 8 | * | 
|---|
| 9 | *------------------------------------------------------------------------- | 
|---|
| 10 | */ | 
|---|
| 11 |  | 
|---|
| 12 | #include "postgres_fe.h" | 
|---|
| 13 | #include "common.h" | 
|---|
| 14 | #include "common/logging.h" | 
|---|
| 15 | #include "fe_utils/simple_list.h" | 
|---|
| 16 | #include "fe_utils/string_utils.h" | 
|---|
| 17 |  | 
|---|
| 18 |  | 
|---|
| 19 | static void cluster_one_database(const char *dbname, bool verbose, const char *table, | 
|---|
| 20 | const char *host, const char *port, | 
|---|
| 21 | const char *username, enum trivalue prompt_password, | 
|---|
| 22 | const char *progname, bool echo); | 
|---|
| 23 | static void cluster_all_databases(bool verbose, const char *maintenance_db, | 
|---|
| 24 | const char *host, const char *port, | 
|---|
| 25 | const char *username, enum trivalue prompt_password, | 
|---|
| 26 | const char *progname, bool echo, bool quiet); | 
|---|
| 27 |  | 
|---|
| 28 | static void help(const char *progname); | 
|---|
| 29 |  | 
|---|
| 30 |  | 
|---|
| 31 | int | 
|---|
| 32 | main(int argc, char *argv[]) | 
|---|
| 33 | { | 
|---|
| 34 | static struct option long_options[] = { | 
|---|
| 35 | { "host", required_argument, NULL, 'h'}, | 
|---|
| 36 | { "port", required_argument, NULL, 'p'}, | 
|---|
| 37 | { "username", required_argument, NULL, 'U'}, | 
|---|
| 38 | { "no-password", no_argument, NULL, 'w'}, | 
|---|
| 39 | { "password", no_argument, NULL, 'W'}, | 
|---|
| 40 | { "echo", no_argument, NULL, 'e'}, | 
|---|
| 41 | { "quiet", no_argument, NULL, 'q'}, | 
|---|
| 42 | { "dbname", required_argument, NULL, 'd'}, | 
|---|
| 43 | { "all", no_argument, NULL, 'a'}, | 
|---|
| 44 | { "table", required_argument, NULL, 't'}, | 
|---|
| 45 | { "verbose", no_argument, NULL, 'v'}, | 
|---|
| 46 | { "maintenance-db", required_argument, NULL, 2}, | 
|---|
| 47 | {NULL, 0, NULL, 0} | 
|---|
| 48 | }; | 
|---|
| 49 |  | 
|---|
| 50 | const char *progname; | 
|---|
| 51 | int			optindex; | 
|---|
| 52 | int			c; | 
|---|
| 53 |  | 
|---|
| 54 | const char *dbname = NULL; | 
|---|
| 55 | const char *maintenance_db = NULL; | 
|---|
| 56 | char	   *host = NULL; | 
|---|
| 57 | char	   *port = NULL; | 
|---|
| 58 | char	   *username = NULL; | 
|---|
| 59 | enum trivalue prompt_password = TRI_DEFAULT; | 
|---|
| 60 | bool		echo = false; | 
|---|
| 61 | bool		quiet = false; | 
|---|
| 62 | bool		alldb = false; | 
|---|
| 63 | bool		verbose = false; | 
|---|
| 64 | SimpleStringList tables = {NULL, NULL}; | 
|---|
| 65 |  | 
|---|
| 66 | pg_logging_init(argv[0]); | 
|---|
| 67 | progname = get_progname(argv[0]); | 
|---|
| 68 | set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN( "pgscripts")); | 
|---|
| 69 |  | 
|---|
| 70 | handle_help_version_opts(argc, argv, "clusterdb", help); | 
|---|
| 71 |  | 
|---|
| 72 | while ((c = getopt_long(argc, argv, "h:p:U:wWeqd:at:v", long_options, &optindex)) != -1) | 
|---|
| 73 | { | 
|---|
| 74 | switch (c) | 
|---|
| 75 | { | 
|---|
| 76 | case 'h': | 
|---|
| 77 | host = pg_strdup(optarg); | 
|---|
| 78 | break; | 
|---|
| 79 | case 'p': | 
|---|
| 80 | port = pg_strdup(optarg); | 
|---|
| 81 | break; | 
|---|
| 82 | case 'U': | 
|---|
| 83 | username = pg_strdup(optarg); | 
|---|
| 84 | break; | 
|---|
| 85 | case 'w': | 
|---|
| 86 | prompt_password = TRI_NO; | 
|---|
| 87 | break; | 
|---|
| 88 | case 'W': | 
|---|
| 89 | prompt_password = TRI_YES; | 
|---|
| 90 | break; | 
|---|
| 91 | case 'e': | 
|---|
| 92 | echo = true; | 
|---|
| 93 | break; | 
|---|
| 94 | case 'q': | 
|---|
| 95 | quiet = true; | 
|---|
| 96 | break; | 
|---|
| 97 | case 'd': | 
|---|
| 98 | dbname = pg_strdup(optarg); | 
|---|
| 99 | break; | 
|---|
| 100 | case 'a': | 
|---|
| 101 | alldb = true; | 
|---|
| 102 | break; | 
|---|
| 103 | case 't': | 
|---|
| 104 | simple_string_list_append(&tables, optarg); | 
|---|
| 105 | break; | 
|---|
| 106 | case 'v': | 
|---|
| 107 | verbose = true; | 
|---|
| 108 | break; | 
|---|
| 109 | case 2: | 
|---|
| 110 | maintenance_db = pg_strdup(optarg); | 
|---|
| 111 | break; | 
|---|
| 112 | default: | 
|---|
| 113 | fprintf(stderr, _( "Try \"%s --help\" for more information.\n"), progname); | 
|---|
| 114 | exit(1); | 
|---|
| 115 | } | 
|---|
| 116 | } | 
|---|
| 117 |  | 
|---|
| 118 | /* | 
|---|
| 119 | * Non-option argument specifies database name as long as it wasn't | 
|---|
| 120 | * already specified with -d / --dbname | 
|---|
| 121 | */ | 
|---|
| 122 | if (optind < argc && dbname == NULL) | 
|---|
| 123 | { | 
|---|
| 124 | dbname = argv[optind]; | 
|---|
| 125 | optind++; | 
|---|
| 126 | } | 
|---|
| 127 |  | 
|---|
| 128 | if (optind < argc) | 
|---|
| 129 | { | 
|---|
| 130 | pg_log_error( "too many command-line arguments (first is \"%s\")", | 
|---|
| 131 | argv[optind]); | 
|---|
| 132 | fprintf(stderr, _( "Try \"%s --help\" for more information.\n"), progname); | 
|---|
| 133 | exit(1); | 
|---|
| 134 | } | 
|---|
| 135 |  | 
|---|
| 136 | setup_cancel_handler(); | 
|---|
| 137 |  | 
|---|
| 138 | if (alldb) | 
|---|
| 139 | { | 
|---|
| 140 | if (dbname) | 
|---|
| 141 | { | 
|---|
| 142 | pg_log_error( "cannot cluster all databases and a specific one at the same time"); | 
|---|
| 143 | exit(1); | 
|---|
| 144 | } | 
|---|
| 145 |  | 
|---|
| 146 | if (tables.head != NULL) | 
|---|
| 147 | { | 
|---|
| 148 | pg_log_error( "cannot cluster specific table(s) in all databases"); | 
|---|
| 149 | exit(1); | 
|---|
| 150 | } | 
|---|
| 151 |  | 
|---|
| 152 | cluster_all_databases(verbose, maintenance_db, host, port, username, prompt_password, | 
|---|
| 153 | progname, echo, quiet); | 
|---|
| 154 | } | 
|---|
| 155 | else | 
|---|
| 156 | { | 
|---|
| 157 | if (dbname == NULL) | 
|---|
| 158 | { | 
|---|
| 159 | if (getenv( "PGDATABASE")) | 
|---|
| 160 | dbname = getenv( "PGDATABASE"); | 
|---|
| 161 | else if (getenv( "PGUSER")) | 
|---|
| 162 | dbname = getenv( "PGUSER"); | 
|---|
| 163 | else | 
|---|
| 164 | dbname = get_user_name_or_exit(progname); | 
|---|
| 165 | } | 
|---|
| 166 |  | 
|---|
| 167 | if (tables.head != NULL) | 
|---|
| 168 | { | 
|---|
| 169 | SimpleStringListCell *cell; | 
|---|
| 170 |  | 
|---|
| 171 | for (cell = tables.head; cell; cell = cell->next) | 
|---|
| 172 | { | 
|---|
| 173 | cluster_one_database(dbname, verbose, cell->val, | 
|---|
| 174 | host, port, username, prompt_password, | 
|---|
| 175 | progname, echo); | 
|---|
| 176 | } | 
|---|
| 177 | } | 
|---|
| 178 | else | 
|---|
| 179 | cluster_one_database(dbname, verbose, NULL, | 
|---|
| 180 | host, port, username, prompt_password, | 
|---|
| 181 | progname, echo); | 
|---|
| 182 | } | 
|---|
| 183 |  | 
|---|
| 184 | exit(0); | 
|---|
| 185 | } | 
|---|
| 186 |  | 
|---|
| 187 |  | 
|---|
| 188 | static void | 
|---|
| 189 | cluster_one_database(const char *dbname, bool verbose, const char *table, | 
|---|
| 190 | const char *host, const char *port, | 
|---|
| 191 | const char *username, enum trivalue prompt_password, | 
|---|
| 192 | const char *progname, bool echo) | 
|---|
| 193 | { | 
|---|
| 194 | PQExpBufferData sql; | 
|---|
| 195 |  | 
|---|
| 196 | PGconn	   *conn; | 
|---|
| 197 |  | 
|---|
| 198 | conn = connectDatabase(dbname, host, port, username, prompt_password, | 
|---|
| 199 | progname, echo, false, false); | 
|---|
| 200 |  | 
|---|
| 201 | initPQExpBuffer(&sql); | 
|---|
| 202 |  | 
|---|
| 203 | appendPQExpBufferStr(&sql, "CLUSTER"); | 
|---|
| 204 | if (verbose) | 
|---|
| 205 | appendPQExpBufferStr(&sql, " VERBOSE"); | 
|---|
| 206 | if (table) | 
|---|
| 207 | { | 
|---|
| 208 | appendPQExpBufferChar(&sql, ' '); | 
|---|
| 209 | appendQualifiedRelation(&sql, table, conn, progname, echo); | 
|---|
| 210 | } | 
|---|
| 211 | appendPQExpBufferChar(&sql, ';'); | 
|---|
| 212 |  | 
|---|
| 213 | if (!executeMaintenanceCommand(conn, sql.data, echo)) | 
|---|
| 214 | { | 
|---|
| 215 | if (table) | 
|---|
| 216 | pg_log_error( "clustering of table \"%s\" in database \"%s\" failed: %s", | 
|---|
| 217 | table, PQdb(conn), PQerrorMessage(conn)); | 
|---|
| 218 | else | 
|---|
| 219 | pg_log_error( "clustering of database \"%s\" failed: %s", | 
|---|
| 220 | PQdb(conn), PQerrorMessage(conn)); | 
|---|
| 221 | PQfinish(conn); | 
|---|
| 222 | exit(1); | 
|---|
| 223 | } | 
|---|
| 224 | PQfinish(conn); | 
|---|
| 225 | termPQExpBuffer(&sql); | 
|---|
| 226 | } | 
|---|
| 227 |  | 
|---|
| 228 |  | 
|---|
| 229 | static void | 
|---|
| 230 | cluster_all_databases(bool verbose, const char *maintenance_db, | 
|---|
| 231 | const char *host, const char *port, | 
|---|
| 232 | const char *username, enum trivalue prompt_password, | 
|---|
| 233 | const char *progname, bool echo, bool quiet) | 
|---|
| 234 | { | 
|---|
| 235 | PGconn	   *conn; | 
|---|
| 236 | PGresult   *result; | 
|---|
| 237 | PQExpBufferData connstr; | 
|---|
| 238 | int			i; | 
|---|
| 239 |  | 
|---|
| 240 | conn = connectMaintenanceDatabase(maintenance_db, host, port, username, | 
|---|
| 241 | prompt_password, progname, echo); | 
|---|
| 242 | result = executeQuery(conn, "SELECT datname FROM pg_database WHERE datallowconn ORDER BY 1;", progname, echo); | 
|---|
| 243 | PQfinish(conn); | 
|---|
| 244 |  | 
|---|
| 245 | initPQExpBuffer(&connstr); | 
|---|
| 246 | for (i = 0; i < PQntuples(result); i++) | 
|---|
| 247 | { | 
|---|
| 248 | char	   *dbname = PQgetvalue(result, i, 0); | 
|---|
| 249 |  | 
|---|
| 250 | if (!quiet) | 
|---|
| 251 | { | 
|---|
| 252 | printf(_( "%s: clustering database \"%s\"\n"), progname, dbname); | 
|---|
| 253 | fflush(stdout); | 
|---|
| 254 | } | 
|---|
| 255 |  | 
|---|
| 256 | resetPQExpBuffer(&connstr); | 
|---|
| 257 | appendPQExpBuffer(&connstr, "dbname="); | 
|---|
| 258 | appendConnStrVal(&connstr, dbname); | 
|---|
| 259 |  | 
|---|
| 260 | cluster_one_database(connstr.data, verbose, NULL, | 
|---|
| 261 | host, port, username, prompt_password, | 
|---|
| 262 | progname, echo); | 
|---|
| 263 | } | 
|---|
| 264 | termPQExpBuffer(&connstr); | 
|---|
| 265 |  | 
|---|
| 266 | PQclear(result); | 
|---|
| 267 | } | 
|---|
| 268 |  | 
|---|
| 269 |  | 
|---|
| 270 | static void | 
|---|
| 271 | help(const char *progname) | 
|---|
| 272 | { | 
|---|
| 273 | printf(_( "%s clusters all previously clustered tables in a database.\n\n"), progname); | 
|---|
| 274 | printf(_( "Usage:\n")); | 
|---|
| 275 | printf(_( "  %s [OPTION]... [DBNAME]\n"), progname); | 
|---|
| 276 | printf(_( "\nOptions:\n")); | 
|---|
| 277 | printf(_( "  -a, --all                 cluster all databases\n")); | 
|---|
| 278 | printf(_( "  -d, --dbname=DBNAME       database to cluster\n")); | 
|---|
| 279 | printf(_( "  -e, --echo                show the commands being sent to the server\n")); | 
|---|
| 280 | printf(_( "  -q, --quiet               don't write any messages\n")); | 
|---|
| 281 | printf(_( "  -t, --table=TABLE         cluster specific table(s) only\n")); | 
|---|
| 282 | printf(_( "  -v, --verbose             write a lot of output\n")); | 
|---|
| 283 | printf(_( "  -V, --version             output version information, then exit\n")); | 
|---|
| 284 | printf(_( "  -?, --help                show this help, then exit\n")); | 
|---|
| 285 | printf(_( "\nConnection options:\n")); | 
|---|
| 286 | printf(_( "  -h, --host=HOSTNAME       database server host or socket directory\n")); | 
|---|
| 287 | printf(_( "  -p, --port=PORT           database server port\n")); | 
|---|
| 288 | printf(_( "  -U, --username=USERNAME   user name to connect as\n")); | 
|---|
| 289 | printf(_( "  -w, --no-password         never prompt for password\n")); | 
|---|
| 290 | printf(_( "  -W, --password            force password prompt\n")); | 
|---|
| 291 | printf(_( "  --maintenance-db=DBNAME   alternate maintenance database\n")); | 
|---|
| 292 | printf(_( "\nRead the description of the SQL command CLUSTER for details.\n")); | 
|---|
| 293 | printf(_( "\nReport bugs to <pgsql-bugs@lists.postgresql.org>.\n")); | 
|---|
| 294 | } | 
|---|
| 295 |  | 
|---|