29
29
30
30
extern char * optarg ;
31
31
extern int optind ,
32
- opterr ,
33
- optopt ;
32
+ opterr ;
34
33
35
34
enum trivalue
36
35
{
@@ -50,16 +49,16 @@ struct _param
50
49
long transaction_limit ;
51
50
};
52
51
53
- int vacuumlo (char * , struct _param * );
54
- void usage (const char * progname );
52
+ static int vacuumlo (const char * database , const struct _param * param );
53
+ static void usage (const char * progname );
55
54
56
55
57
56
58
57
/*
59
58
* This vacuums LOs of one database. It returns 0 on success, -1 on failure.
60
59
*/
61
- int
62
- vacuumlo (char * database , struct _param * param )
60
+ static int
61
+ vacuumlo (const char * database , const struct _param * param )
63
62
{
64
63
PGconn * conn ;
65
64
PGresult * res ,
@@ -72,6 +71,7 @@ vacuumlo(char *database, struct _param * param)
72
71
bool new_pass ;
73
72
bool success = true;
74
73
74
+ /* Note: password can be carried over from a previous call */
75
75
if (param -> pg_prompt == TRI_YES && password == NULL )
76
76
password = simple_prompt ("Password: " , 100 , false);
77
77
@@ -119,7 +119,7 @@ vacuumlo(char *database, struct _param * param)
119
119
120
120
if (param -> verbose )
121
121
{
122
- fprintf (stdout , "Connected to %s \n" , database );
122
+ fprintf (stdout , "Connected to database \"%s\" \n" , database );
123
123
if (param -> dry_run )
124
124
fprintf (stdout , "Test run: no large objects will be removed!\n" );
125
125
}
@@ -220,9 +220,21 @@ vacuumlo(char *database, struct _param * param)
220
220
if (param -> verbose )
221
221
fprintf (stdout , "Checking %s in %s.%s\n" , field , schema , table );
222
222
223
+ schema = PQescapeIdentifier (conn , schema , strlen (schema ));
224
+ table = PQescapeIdentifier (conn , table , strlen (table ));
225
+ field = PQescapeIdentifier (conn , field , strlen (field ));
226
+
227
+ if (!schema || !table || !field )
228
+ {
229
+ fprintf (stderr , "Out of memory\n" );
230
+ PQclear (res );
231
+ PQfinish (conn );
232
+ return -1 ;
233
+ }
234
+
223
235
snprintf (buf , BUFSIZE ,
224
236
"DELETE FROM vacuum_l "
225
- "WHERE lo IN (SELECT \"%s\" FROM \"%s\".\"%s\" )" ,
237
+ "WHERE lo IN (SELECT %s FROM %s.%s )" ,
226
238
field , schema , table );
227
239
res2 = PQexec (conn , buf );
228
240
if (PQresultStatus (res2 ) != PGRES_COMMAND_OK )
@@ -236,23 +248,35 @@ vacuumlo(char *database, struct _param * param)
236
248
return -1 ;
237
249
}
238
250
PQclear (res2 );
251
+
252
+ PQfreemem (schema );
253
+ PQfreemem (table );
254
+ PQfreemem (field );
239
255
}
240
256
PQclear (res );
241
257
242
258
/*
243
- * Run the actual deletes in a single transaction. Note that this would
244
- * be a bad idea in pre-7.1 Postgres releases (since rolling back a table
245
- * delete used to cause problems), but it should be safe now.
259
+ * Now, those entries remaining in vacuum_l are orphans. Delete 'em.
260
+ *
261
+ * We don't want to run each delete as an individual transaction, because
262
+ * the commit overhead would be high. However, since 9.0 the backend will
263
+ * acquire a lock per deleted LO, so deleting too many LOs per transaction
264
+ * risks running out of room in the shared-memory lock table.
265
+ * Accordingly, we delete up to transaction_limit LOs per transaction.
246
266
*/
247
267
res = PQexec (conn , "begin" );
268
+ if (PQresultStatus (res ) != PGRES_COMMAND_OK )
269
+ {
270
+ fprintf (stderr , "Failed to start transaction:\n" );
271
+ fprintf (stderr , "%s" , PQerrorMessage (conn ));
272
+ PQclear (res );
273
+ PQfinish (conn );
274
+ return -1 ;
275
+ }
248
276
PQclear (res );
249
277
250
- /*
251
- * Finally, those entries remaining in vacuum_l are orphans.
252
- */
253
278
buf [0 ] = '\0' ;
254
- strcat (buf , "SELECT lo " );
255
- strcat (buf , "FROM vacuum_l" );
279
+ strcat (buf , "SELECT lo FROM vacuum_l" );
256
280
res = PQexec (conn , buf );
257
281
if (PQresultStatus (res ) != PGRES_TUPLES_OK )
258
282
{
@@ -292,44 +316,76 @@ vacuumlo(char *database, struct _param * param)
292
316
}
293
317
else
294
318
deleted ++ ;
295
- if (param -> transaction_limit != 0 && deleted >= param -> transaction_limit )
296
- break ;
319
+ if (param -> transaction_limit > 0 &&
320
+ (deleted % param -> transaction_limit ) == 0 )
321
+ {
322
+ res2 = PQexec (conn , "commit" );
323
+ if (PQresultStatus (res2 ) != PGRES_COMMAND_OK )
324
+ {
325
+ fprintf (stderr , "Failed to commit transaction:\n" );
326
+ fprintf (stderr , "%s" , PQerrorMessage (conn ));
327
+ PQclear (res2 );
328
+ PQclear (res );
329
+ PQfinish (conn );
330
+ return -1 ;
331
+ }
332
+ PQclear (res2 );
333
+ res2 = PQexec (conn , "begin" );
334
+ if (PQresultStatus (res2 ) != PGRES_COMMAND_OK )
335
+ {
336
+ fprintf (stderr , "Failed to start transaction:\n" );
337
+ fprintf (stderr , "%s" , PQerrorMessage (conn ));
338
+ PQclear (res2 );
339
+ PQclear (res );
340
+ PQfinish (conn );
341
+ return -1 ;
342
+ }
343
+ PQclear (res2 );
344
+ }
297
345
}
298
346
PQclear (res );
299
347
300
348
/*
301
349
* That's all folks!
302
350
*/
303
- res = PQexec (conn , "end" );
351
+ res = PQexec (conn , "commit" );
352
+ if (PQresultStatus (res ) != PGRES_COMMAND_OK )
353
+ {
354
+ fprintf (stderr , "Failed to commit transaction:\n" );
355
+ fprintf (stderr , "%s" , PQerrorMessage (conn ));
356
+ PQclear (res );
357
+ PQfinish (conn );
358
+ return -1 ;
359
+ }
304
360
PQclear (res );
305
361
306
362
PQfinish (conn );
307
363
308
364
if (param -> verbose )
309
365
{
310
366
if (param -> dry_run )
311
- fprintf (stdout , "\rWould remove %ld large objects from %s .\n" ,
367
+ fprintf (stdout , "\rWould remove %ld large objects from database \"%s\" .\n" ,
312
368
deleted , database );
313
369
else if (success )
314
370
fprintf (stdout ,
315
- "\rSuccessfully removed %ld large objects from %s .\n" ,
371
+ "\rSuccessfully removed %ld large objects from database \"%s\" .\n" ,
316
372
deleted , database );
317
373
else
318
- fprintf (stdout , "\rRemoval from %s failed at object %ld of %ld.\n" ,
374
+ fprintf (stdout , "\rRemoval from database \"%s\" failed at object %ld of %ld.\n" ,
319
375
database , deleted , matched );
320
376
}
321
377
322
378
return ((param -> dry_run || success ) ? 0 : -1 );
323
379
}
324
380
325
- void
381
+ static void
326
382
usage (const char * progname )
327
383
{
328
384
printf ("%s removes unreferenced large objects from databases.\n\n" , progname );
329
385
printf ("Usage:\n %s [OPTION]... DBNAME...\n\n" , progname );
330
386
printf ("Options:\n" );
331
387
printf (" -h HOSTNAME database server host or socket directory\n" );
332
- printf (" -l LIMIT stop after removing LIMIT large objects\n" );
388
+ printf (" -l LIMIT commit after removing each LIMIT large objects\n" );
333
389
printf (" -n don't remove large objects, just show what would be done\n" );
334
390
printf (" -p PORT database server port\n" );
335
391
printf (" -U USERNAME user name to connect as\n" );
@@ -354,15 +410,16 @@ main(int argc, char **argv)
354
410
355
411
progname = get_progname (argv [0 ]);
356
412
357
- /* Parameter handling */
413
+ /* Set default parameter values */
358
414
param .pg_user = NULL ;
359
415
param .pg_prompt = TRI_DEFAULT ;
360
416
param .pg_host = NULL ;
361
417
param .pg_port = NULL ;
362
418
param .verbose = 0 ;
363
419
param .dry_run = 0 ;
364
- param .transaction_limit = 0 ;
420
+ param .transaction_limit = 1000 ;
365
421
422
+ /* Process command-line arguments */
366
423
if (argc > 1 )
367
424
{
368
425
if (strcmp (argv [1 ], "--help" ) == 0 || strcmp (argv [1 ], "-?" ) == 0 )
@@ -397,6 +454,16 @@ main(int argc, char **argv)
397
454
param .dry_run = 1 ;
398
455
param .verbose = 1 ;
399
456
break ;
457
+ case 'l' :
458
+ param .transaction_limit = strtol (optarg , NULL , 10 );
459
+ if (param .transaction_limit < 0 )
460
+ {
461
+ fprintf (stderr ,
462
+ "%s: transaction limit must not be negative (0 disables)\n" ,
463
+ progname );
464
+ exit (1 );
465
+ }
466
+ break ;
400
467
case 'U' :
401
468
param .pg_user = strdup (optarg );
402
469
break ;
@@ -415,16 +482,6 @@ main(int argc, char **argv)
415
482
}
416
483
param .pg_port = strdup (optarg );
417
484
break ;
418
- case 'l' :
419
- param .transaction_limit = strtol (optarg , NULL , 10 );
420
- if (param .transaction_limit < 0 )
421
- {
422
- fprintf (stderr ,
423
- "%s: transaction limit must not be negative (0 disables)\n" ,
424
- progname );
425
- exit (1 );
426
- }
427
- break ;
428
485
case 'h' :
429
486
param .pg_host = strdup (optarg );
430
487
break ;
@@ -435,7 +492,7 @@ main(int argc, char **argv)
435
492
if (optind >= argc )
436
493
{
437
494
fprintf (stderr , "vacuumlo: missing required argument: database name\n" );
438
- fprintf (stderr , "Try 'vacuumlo -?' for help .\n" );
495
+ fprintf (stderr , _ ( "Try \"%s --help\" for more information .\n" ), progname );
439
496
exit (1 );
440
497
}
441
498
0 commit comments