88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.6 2000/10/24 01:38:20 tgl Exp $
11+ * $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.7 2000/11/21 17:54:21 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
2424#include "libpq-fe.h"
2525#include "libpq/libpq-fs.h"
2626
27+ #define atooid (x ) ((Oid) strtoul((x), NULL, 10))
28+
2729#define BUFSIZE 1024
2830
2931int vacuumlo (char * ,int );
3032
3133
3234/*
33- * This vacuumsa database. It returns1 on success, -1 on failure.
35+ * This vacuumsLOs of one database. It returns0 on success, -1 on failure.
3436 */
3537int
3638vacuumlo (char * database ,int verbose )
@@ -39,40 +41,68 @@ vacuumlo(char *database, int verbose)
3941PGresult * res ,
4042* res2 ;
4143char buf [BUFSIZE ];
42- int matched = 0 ;/* Number matched per scan */
44+ int matched ;
45+ int deleted ;
4346int i ;
4447
4548conn = PQsetdb (NULL ,NULL ,NULL ,NULL ,database );
4649
4750/* check to see that the backend connection was successfully made */
4851if (PQstatus (conn )== CONNECTION_BAD )
4952{
50- fprintf (stderr ,"Connection to database '%s' failed. \n" ,database );
53+ fprintf (stderr ,"Connection to database '%s' failed: \n" ,database );
5154fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
55+ PQfinish (conn );
5256return -1 ;
5357}
5458
5559if (verbose )
5660fprintf (stdout ,"Connected to %s\n" ,database );
5761
5862/*
59- * First we create and populate thelo temp table
63+ * First we create and populate theLO temp table
6064 */
6165buf [0 ]= '\0' ;
6266strcat (buf ,"SELECT DISTINCT loid AS lo " );
6367strcat (buf ,"INTO TEMP TABLE vacuum_l " );
6468strcat (buf ,"FROM pg_largeobject " );
65- if (!(res = PQexec (conn ,buf )))
69+ res = PQexec (conn ,buf );
70+ if (PQresultStatus (res )!= PGRES_COMMAND_OK )
71+ {
72+ fprintf (stderr ,"Failed to create temp table:\n" );
73+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
74+ PQclear (res );
75+ PQfinish (conn );
76+ return -1 ;
77+ }
78+ PQclear (res );
79+ /*
80+ * Vacuum the temp table so that planner will generate decent plans
81+ * for the DELETEs below.
82+ */
83+ buf [0 ]= '\0' ;
84+ strcat (buf ,"VACUUM ANALYZE vacuum_l " );
85+ res = PQexec (conn ,buf );
86+ if (PQresultStatus (res )!= PGRES_COMMAND_OK )
6687{
67- fprintf (stderr ,"Failed to create temp table.\n" );
88+ fprintf (stderr ,"Failed to vacuum temp table:\n" );
89+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
90+ PQclear (res );
6891PQfinish (conn );
6992return -1 ;
7093}
7194PQclear (res );
7295
7396/*
74- * Now find any candidate tables who have columns of type oid (the
75- * column oid is ignored, as it has attnum < 1)
97+ * Now find any candidate tables who have columns of type oid.
98+ *
99+ * NOTE: the temp table formed above is ignored, because its real
100+ * table name will be pg_something. Also, pg_largeobject will be
101+ * ignored. If either of these were scanned, obviously we'd end up
102+ * with nothing to delete...
103+ *
104+ * NOTE: the system oid column is ignored, as it has attnum < 1.
105+ * This shouldn't matter for correctness, but it saves time.
76106 */
77107buf [0 ]= '\0' ;
78108strcat (buf ,"SELECT c.relname, a.attname " );
@@ -81,13 +111,18 @@ vacuumlo(char *database, int verbose)
81111strcat (buf ," AND a.attrelid = c.oid " );
82112strcat (buf ," AND a.atttypid = t.oid " );
83113strcat (buf ," AND t.typname = 'oid' " );
114+ strcat (buf ," AND c.relkind = 'r'" );
84115strcat (buf ," AND c.relname NOT LIKE 'pg_%'" );
85- if (!(res = PQexec (conn ,buf )))
116+ res = PQexec (conn ,buf );
117+ if (PQresultStatus (res )!= PGRES_TUPLES_OK )
86118{
87- fprintf (stderr ,"Failed to create temp table.\n" );
119+ fprintf (stderr ,"Failed to find OID columns:\n" );
120+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
121+ PQclear (res );
88122PQfinish (conn );
89123return -1 ;
90124}
125+
91126for (i = 0 ;i < PQntuples (res );i ++ )
92127{
93128char * table ,
@@ -97,50 +132,36 @@ vacuumlo(char *database, int verbose)
97132field = PQgetvalue (res ,i ,1 );
98133
99134if (verbose )
100- {
101- fprintf (stdout ,"Checking %s in %s: " ,field ,table );
102- fflush (stdout );
103- }
104-
105- res2 = PQexec (conn ,"begin" );
106- PQclear (res2 );
107-
108- buf [0 ]= '\0' ;
109- strcat (buf ,"DELETE FROM vacuum_l " );
110- strcat (buf ,"WHERE lo IN (" );
111- strcat (buf ,"SELECT " );
112- strcat (buf ,field );
113- strcat (buf ," FROM " );
114- strcat (buf ,table );
115- strcat (buf ,");" );
116- if (!(res2 = PQexec (conn ,buf )))
117- {
118- fprintf (stderr ,"Failed to check %s in table %s\n" ,field ,table );
119- PQclear (res );
120- PQfinish (conn );
121- return -1 ;
122- }
135+ fprintf (stdout ,"Checking %s in %s\n" ,field ,table );
136+
137+ /*
138+ * We use a DELETE with implicit join for efficiency. This
139+ * is a Postgres-ism and not portable to other DBMSs, but
140+ * then this whole program is a Postgres-ism.
141+ */
142+ sprintf (buf ,"DELETE FROM vacuum_l WHERE lo = \"%s\".\"%s\" " ,
143+ table ,field );
144+ res2 = PQexec (conn ,buf );
123145if (PQresultStatus (res2 )!= PGRES_COMMAND_OK )
124146{
125- fprintf (stderr ,
126- "Failed to check %s in table %s\n%s\n" ,
127- field ,table ,
128- PQerrorMessage (conn )
129- );
147+ fprintf (stderr ,"Failed to check %s in table %s:\n" ,
148+ field ,table );
149+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
130150PQclear (res2 );
131151PQclear (res );
132152PQfinish (conn );
133153return -1 ;
134154}
135155PQclear (res2 );
136-
137- res2 = PQexec (conn ,"end" );
138- PQclear (res2 );
139-
140156}
141157PQclear (res );
142158
143- /* Start the transaction */
159+ /*
160+ * Run the actual deletes in a single transaction. Note that this
161+ * would be a bad idea in pre-7.1 Postgres releases (since rolling
162+ * back a table delete used to cause problems), but it should
163+ * be safe now.
164+ */
144165res = PQexec (conn ,"begin" );
145166PQclear (res );
146167
@@ -150,25 +171,35 @@ vacuumlo(char *database, int verbose)
150171buf [0 ]= '\0' ;
151172strcat (buf ,"SELECT lo " );
152173strcat (buf ,"FROM vacuum_l" );
153- if (!(res = PQexec (conn ,buf )))
174+ res = PQexec (conn ,buf );
175+ if (PQresultStatus (res )!= PGRES_TUPLES_OK )
154176{
155- fprintf (stderr ,"Failed to read temp table.\n" );
177+ fprintf (stderr ,"Failed to read temp table:\n" );
178+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
179+ PQclear (res );
156180PQfinish (conn );
157181return -1 ;
158182}
183+
159184matched = PQntuples (res );
185+ deleted = 0 ;
160186for (i = 0 ;i < matched ;i ++ )
161187{
162- Oid lo = ( Oid ) atoi (PQgetvalue (res ,i ,0 ));
188+ Oid lo = atooid (PQgetvalue (res ,i ,0 ));
163189
164190if (verbose )
165191{
166- fprintf (stdout ,"\rRemoving lo %6d \n " ,lo );
192+ fprintf (stdout ,"\rRemoving lo %6u " ,lo );
167193fflush (stdout );
168194}
169195
170196if (lo_unlink (conn ,lo )< 0 )
171- fprintf (stderr ,"Failed to remove lo %d\n" ,lo );
197+ {
198+ fprintf (stderr ,"\nFailed to remove lo %u: " ,lo );
199+ fprintf (stderr ,"%s" ,PQerrorMessage (conn ));
200+ }
201+ else
202+ deleted ++ ;
172203}
173204PQclear (res );
174205
@@ -177,10 +208,12 @@ vacuumlo(char *database, int verbose)
177208 */
178209res = PQexec (conn ,"end" );
179210PQclear (res );
211+
180212PQfinish (conn );
181213
182214if (verbose )
183- fprintf (stdout ,"\rRemoved %d large objects from %s.\n" ,matched ,database );
215+ fprintf (stdout ,"\rRemoved %d large objects from %s.\n" ,
216+ deleted ,database );
184217
185218return 0 ;
186219}
@@ -204,7 +237,7 @@ main(int argc, char **argv)
204237if (strcmp ("-v" ,argv [arg ])== 0 )
205238verbose = !verbose ;
206239else
207- rc += vacuumlo (argv [arg ],verbose );
240+ rc += ( vacuumlo (argv [arg ],verbose ) != 0 );
208241}
209242
210243return rc ;