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
19static 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);
23static 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
28static void help(const char *progname);
29
30
31int
32main(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
188static void
189cluster_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
229static void
230cluster_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
270static void
271help(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