Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7f45980

Browse files
committed
I've improved the contributed vacuumlo command, now it behaves like all other
postgres command line utilites e.g. supports -U, -p, -h, -?, -v, password prompt and has a "test mode". In test mode, no large objects are removed, just reported. Mario Weilguni
1 parent 5b0fb00 commit 7f45980

File tree

1 file changed

+246
-33
lines changed

1 file changed

+246
-33
lines changed

contrib/vacuumlo/vacuumlo.c

Lines changed: 246 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,20 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.10 2001/09/17 02:30:54 inoue Exp $
11+
* $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.11 2002/04/24 02:45:51 momjian Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
15+
16+
#include <pg_config.h>
1517
#include <stdio.h>
1618
#include <stdlib.h>
1719
#include <string.h>
1820

21+
#ifdef HAVE_TERMIOS_H
22+
#include <termios.h>
23+
#endif
24+
1925
#include <sys/types.h>
2026
#include <sys/stat.h>
2127
#include <fcntl.h>
@@ -28,24 +34,163 @@
2834

2935
#define BUFSIZE 1024
3036

31-
int vacuumlo(char *, int);
37+
extern char *optarg;
38+
extern int optind, opterr, optopt;
39+
40+
struct _param {
41+
char *pg_user;
42+
int pg_prompt;
43+
char *pg_port;
44+
char *pg_host;
45+
int verbose;
46+
int dry_run;
47+
};
48+
49+
int vacuumlo(char *, struct _param *);
50+
char *simple_prompt(const char *prompt, int , int);
51+
void usage(void);
52+
53+
54+
/*
55+
* simple_prompt
56+
*
57+
* Generalized function especially intended for reading in usernames and
58+
* password interactively. Reads from /dev/tty or stdin/stderr.
59+
*
60+
* prompt: The prompt to print
61+
* maxlen: How many characters to accept
62+
* echo: Set to 0 if you want to hide what is entered (for passwords)
63+
*
64+
* Returns a malloc()'ed string with the input (w/o trailing newline).
65+
*/
66+
static int prompt_state = 0;
67+
68+
char *
69+
simple_prompt(const char *prompt, int maxlen, int echo)
70+
{
71+
int length;
72+
char *destination;
73+
FILE *termin,
74+
*termout;
75+
76+
#ifdef HAVE_TERMIOS_H
77+
struct termios t_orig,
78+
t;
79+
#endif
80+
81+
destination = (char *) malloc(maxlen + 2);
82+
if (!destination)
83+
return NULL;
84+
85+
prompt_state = 1; /* disable SIGINT */
86+
87+
/*
88+
* Do not try to collapse these into one "w+" mode file. Doesn't work
89+
* on some platforms (eg, HPUX 10.20).
90+
*/
91+
termin = fopen("/dev/tty", "r");
92+
termout = fopen("/dev/tty", "w");
93+
if (!termin || !termout)
94+
{
95+
if (termin)
96+
fclose(termin);
97+
if (termout)
98+
fclose(termout);
99+
termin = stdin;
100+
termout = stderr;
101+
}
102+
103+
#ifdef HAVE_TERMIOS_H
104+
if (!echo)
105+
{
106+
tcgetattr(fileno(termin), &t);
107+
t_orig = t;
108+
t.c_lflag &= ~ECHO;
109+
tcsetattr(fileno(termin), TCSAFLUSH, &t);
110+
}
111+
#endif
112+
113+
if (prompt)
114+
{
115+
fputs(prompt, termout);
116+
fflush(termout);
117+
}
118+
119+
if (fgets(destination, maxlen, termin) == NULL)
120+
destination[0] = '\0';
121+
122+
length = strlen(destination);
123+
if (length > 0 && destination[length - 1] != '\n')
124+
{
125+
/* eat rest of the line */
126+
char buf[128];
127+
int buflen;
128+
129+
do
130+
{
131+
if (fgets(buf, sizeof(buf), termin) == NULL)
132+
break;
133+
buflen = strlen(buf);
134+
} while (buflen > 0 && buf[buflen - 1] != '\n');
135+
}
136+
137+
if (length > 0 && destination[length - 1] == '\n')
138+
/* remove trailing newline */
139+
destination[length - 1] = '\0';
140+
141+
#ifdef HAVE_TERMIOS_H
142+
if (!echo)
143+
{
144+
tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
145+
fputs("\n", termout);
146+
fflush(termout);
147+
}
148+
#endif
149+
150+
if (termin != stdin)
151+
{
152+
fclose(termin);
153+
fclose(termout);
154+
}
155+
156+
prompt_state = 0; /* SIGINT okay again */
157+
158+
return destination;
159+
}
160+
32161

33162

34163
/*
35164
* This vacuums LOs of one database. It returns 0 on success, -1 on failure.
36165
*/
37166
int
38-
vacuumlo(char *database, int verbose)
167+
vacuumlo(char *database, struct _param *param)
39168
{
40169
PGconn *conn;
41170
PGresult *res,
42-
*res2;
171+
*res2;
43172
char buf[BUFSIZE];
44-
int matched;
45-
int deleted;
46-
int i;
173+
int matched;
174+
int deleted;
175+
int i;
176+
char *password = NULL;
47177

48-
conn = PQsetdb(NULL, NULL, NULL, NULL, database);
178+
if(param->pg_prompt) {
179+
password = simple_prompt("Password: ", 32, 0);
180+
if(!password) {
181+
fprintf(stderr, "failed to get password\n");
182+
exit(1);
183+
}
184+
}
185+
186+
conn = PQsetdbLogin( param->pg_host,
187+
param->pg_port,
188+
NULL,
189+
NULL,
190+
database,
191+
param->pg_user,
192+
password
193+
);
49194

50195
/* check to see that the backend connection was successfully made */
51196
if (PQstatus(conn) == CONNECTION_BAD)
@@ -56,8 +201,11 @@ vacuumlo(char *database, int verbose)
56201
return -1;
57202
}
58203

59-
if (verbose)
204+
if (param->verbose) {
60205
fprintf(stdout, "Connected to %s\n", database);
206+
if(param->dry_run)
207+
fprintf(stdout, "Test run: no large objects will be removed!\n");
208+
}
61209

62210
/*
63211
* First we create and populate the LO temp table
@@ -132,7 +280,7 @@ vacuumlo(char *database, int verbose)
132280
table = PQgetvalue(res, i, 0);
133281
field = PQgetvalue(res, i, 1);
134282

135-
if (verbose)
283+
if (param->verbose)
136284
fprintf(stdout, "Checking %s in %s\n", field, table);
137285

138286
/*
@@ -188,19 +336,22 @@ vacuumlo(char *database, int verbose)
188336
{
189337
Oid lo = atooid(PQgetvalue(res, i, 0));
190338

191-
if (verbose)
339+
if (param->verbose)
192340
{
193341
fprintf(stdout, "\rRemoving lo %6u ", lo);
194342
fflush(stdout);
195343
}
196344

197-
if (lo_unlink(conn, lo) < 0)
198-
{
199-
fprintf(stderr, "\nFailed to remove lo %u: ", lo);
200-
fprintf(stderr, "%s", PQerrorMessage(conn));
201-
}
202-
else
203-
deleted++;
345+
if(param->dry_run == 0) {
346+
if (lo_unlink(conn, lo) < 0)
347+
{
348+
fprintf(stderr, "\nFailed to remove lo %u: ", lo);
349+
fprintf(stderr, "%s", PQerrorMessage(conn));
350+
}
351+
else
352+
deleted++;
353+
} else
354+
deleted++;
204355
}
205356
PQclear(res);
206357

@@ -212,33 +363,95 @@ vacuumlo(char *database, int verbose)
212363

213364
PQfinish(conn);
214365

215-
if (verbose)
216-
fprintf(stdout, "\rRemoved %d large objects from %s.\n",
217-
deleted, database);
366+
if (param->verbose)
367+
fprintf(stdout, "\r%s %d large objects from %s.\n",
368+
(param->dry_run?"Would remove":"Removed"), deleted, database);
218369

219370
return 0;
220371
}
221372

373+
void
374+
usage(void) {
375+
fprintf(stdout, "vacuumlo removes unreferenced large objects from databases\n\n");
376+
fprintf(stdout, "Usage:\n vacuumlo [options] dbname [dbnames...]\n\n");
377+
fprintf(stdout, "Options:\n");
378+
fprintf(stdout, " -v\t\tWrite a lot of output\n");
379+
fprintf(stdout, " -n\t\tDon't remove any large object, just show what would be done\n");
380+
fprintf(stdout, " -U username\tUsername to connect as\n");
381+
fprintf(stdout, " -W\t\tPrompt for password\n");
382+
fprintf(stdout, " -h hostname\tDatabase server host\n");
383+
fprintf(stdout, " -p port\tDatabase server port\n");
384+
fprintf(stdout, " -p port\tDatabase server port\n\n");
385+
}
386+
387+
222388
int
223389
main(int argc, char **argv)
224390
{
225-
int verbose = 0;
226-
int arg;
227391
int rc = 0;
392+
struct _param param;
393+
int c;
394+
int port;
228395

229-
if (argc < 2)
230-
{
231-
fprintf(stderr, "Usage: %s [-v] database_name [db2 ... dbn]\n",
232-
argv[0]);
396+
/* Parameter handling */
397+
param.pg_user = NULL;
398+
param.pg_prompt = 0;
399+
param.pg_host = NULL;
400+
param.pg_port = 0;
401+
param.verbose = 0;
402+
param.dry_run = 0;
403+
404+
while( 1 ) {
405+
c = getopt(argc, argv, "?h:U:p:vnW");
406+
if(c == -1)
407+
break;
408+
409+
switch(c) {
410+
case '?':
411+
if(optopt == '?') {
412+
usage();
413+
exit(0);
414+
}
415+
exit(1);
416+
case ':':
417+
exit(1);
418+
case 'v':
419+
param.verbose = 1;
420+
break;
421+
case 'n':
422+
param.dry_run = 1;
423+
param.verbose = 1;
424+
break;
425+
case 'U':
426+
param.pg_user = strdup(optarg);
427+
break;
428+
case 'W':
429+
param.pg_prompt = 1;
430+
break;
431+
case 'p':
432+
port = strtol(optarg, NULL, 10);
433+
if( (port < 1) || (port > 65535)) {
434+
fprintf(stderr, "[%s]: invalid port number '%s'\n", argv[0], optarg);
233435
exit(1);
436+
}
437+
param.pg_port = strdup(optarg);
438+
break;
439+
case 'h':
440+
param.pg_host = strdup(optarg);
441+
break;
442+
}
443+
}
444+
445+
/* No database given? Show usage */
446+
if(optind >= argc-1) {
447+
fprintf(stderr, "vacuumlo: missing required argument: database name\n");
448+
fprintf(stderr, "Try 'vacuumlo -?' for help.\n");
449+
exit(1);
234450
}
235451

236-
for (arg = 1; arg < argc; arg++)
237-
{
238-
if (strcmp("-v", argv[arg]) == 0)
239-
verbose = !verbose;
240-
else
241-
rc += (vacuumlo(argv[arg], verbose) != 0);
452+
for(c = optind; c < argc; c++) {
453+
/* Work on selected database */
454+
rc += (vacuumlo(argv[c], &param) != 0);
242455
}
243456

244457
return rc;

0 commit comments

Comments
 (0)