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