2929
3030extern char * optarg ;
3131extern int optind ,
32- opterr ,
33- optopt ;
32+ opterr ;
3433
3534enum trivalue
3635{
@@ -50,16 +49,16 @@ struct _param
5049long transaction_limit ;
5150};
5251
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 );
5554
5655
5756
5857/*
5958 * This vacuums LOs of one database. It returns 0 on success, -1 on failure.
6059 */
61- int
62- vacuumlo (char * database ,struct _param * param )
60+ static int
61+ vacuumlo (const char * database ,const struct _param * param )
6362{
6463PGconn * conn ;
6564PGresult * res ,
@@ -72,6 +71,7 @@ vacuumlo(char *database, struct _param * param)
7271bool new_pass ;
7372bool success = true;
7473
74+ /* Note: password can be carried over from a previous call */
7575if (param -> pg_prompt == TRI_YES && password == NULL )
7676password = simple_prompt ("Password: " ,100 , false);
7777
@@ -119,7 +119,7 @@ vacuumlo(char *database, struct _param * param)
119119
120120if (param -> verbose )
121121{
122- fprintf (stdout ,"Connected to%s \n" ,database );
122+ fprintf (stdout ,"Connected todatabase \"%s\" \n" ,database );
123123if (param -> dry_run )
124124fprintf (stdout ,"Test run: no large objects will be removed!\n" );
125125}
@@ -220,9 +220,21 @@ vacuumlo(char *database, struct _param * param)
220220if (param -> verbose )
221221fprintf (stdout ,"Checking %s in %s.%s\n" ,field ,schema ,table );
222222
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+
223235snprintf (buf ,BUFSIZE ,
224236"DELETE FROM vacuum_l "
225- "WHERE lo IN (SELECT\"%s\" FROM\"%s\".\"%s\" )" ,
237+ "WHERE lo IN (SELECT%s FROM%s.%s )" ,
226238field ,schema ,table );
227239res2 = PQexec (conn ,buf );
228240if (PQresultStatus (res2 )!= PGRES_COMMAND_OK )
@@ -236,23 +248,35 @@ vacuumlo(char *database, struct _param * param)
236248return -1 ;
237249}
238250PQclear (res2 );
251+
252+ PQfreemem (schema );
253+ PQfreemem (table );
254+ PQfreemem (field );
239255}
240256PQclear (res );
241257
242258/*
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.
246266 */
247267res = 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+ }
248276PQclear (res );
249277
250- /*
251- * Finally, those entries remaining in vacuum_l are orphans.
252- */
253278buf [0 ]= '\0' ;
254- strcat (buf ,"SELECT lo " );
255- strcat (buf ,"FROM vacuum_l" );
279+ strcat (buf ,"SELECT lo FROM vacuum_l" );
256280res = PQexec (conn ,buf );
257281if (PQresultStatus (res )!= PGRES_TUPLES_OK )
258282{
@@ -292,44 +316,76 @@ vacuumlo(char *database, struct _param * param)
292316}
293317else
294318deleted ++ ;
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+ }
297345}
298346PQclear (res );
299347
300348/*
301349 * That's all folks!
302350 */
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+ }
304360PQclear (res );
305361
306362PQfinish (conn );
307363
308364if (param -> verbose )
309365{
310366if (param -> dry_run )
311- fprintf (stdout ,"\rWould remove %ld large objects from%s .\n" ,
367+ fprintf (stdout ,"\rWould remove %ld large objects fromdatabase \"%s\" .\n" ,
312368deleted ,database );
313369else if (success )
314370fprintf (stdout ,
315- "\rSuccessfully removed %ld large objects from%s .\n" ,
371+ "\rSuccessfully removed %ld large objects fromdatabase \"%s\" .\n" ,
316372deleted ,database );
317373else
318- fprintf (stdout ,"\rRemoval from%s failed at object %ld of %ld.\n" ,
374+ fprintf (stdout ,"\rRemoval fromdatabase \"%s\" failed at object %ld of %ld.\n" ,
319375database ,deleted ,matched );
320376}
321377
322378return ((param -> dry_run || success ) ?0 :-1 );
323379}
324380
325- void
381+ static void
326382usage (const char * progname )
327383{
328384printf ("%s removes unreferenced large objects from databases.\n\n" ,progname );
329385printf ("Usage:\n %s [OPTION]... DBNAME...\n\n" ,progname );
330386printf ("Options:\n" );
331387printf (" -h HOSTNAME database server host or socket directory\n" );
332- printf (" -l LIMITstop after removing LIMIT large objects\n" );
388+ printf (" -l LIMITcommit after removing each LIMIT large objects\n" );
333389printf (" -n don't remove large objects, just show what would be done\n" );
334390printf (" -p PORT database server port\n" );
335391printf (" -U USERNAME user name to connect as\n" );
@@ -354,15 +410,16 @@ main(int argc, char **argv)
354410
355411progname = get_progname (argv [0 ]);
356412
357- /*Parameter handling */
413+ /*Set default parameter values */
358414param .pg_user = NULL ;
359415param .pg_prompt = TRI_DEFAULT ;
360416param .pg_host = NULL ;
361417param .pg_port = NULL ;
362418param .verbose = 0 ;
363419param .dry_run = 0 ;
364- param .transaction_limit = 0 ;
420+ param .transaction_limit = 1000 ;
365421
422+ /* Process command-line arguments */
366423if (argc > 1 )
367424{
368425if (strcmp (argv [1 ],"--help" )== 0 || strcmp (argv [1 ],"-?" )== 0 )
@@ -397,6 +454,16 @@ main(int argc, char **argv)
397454param .dry_run = 1 ;
398455param .verbose = 1 ;
399456break ;
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 ;
400467case 'U' :
401468param .pg_user = strdup (optarg );
402469break ;
@@ -415,16 +482,6 @@ main(int argc, char **argv)
415482}
416483param .pg_port = strdup (optarg );
417484break ;
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 ;
428485case 'h' :
429486param .pg_host = strdup (optarg );
430487break ;
@@ -435,7 +492,7 @@ main(int argc, char **argv)
435492if (optind >=argc )
436493{
437494fprintf (stderr ,"vacuumlo: missing required argument: database name\n" );
438- fprintf (stderr ,"Try'vacuumlo -?' forhelp .\n" );
495+ fprintf (stderr ,_ ( "Try\"%s --help\" formore information .\n" ), progname );
439496exit (1 );
440497}
441498