8
8
*
9
9
*
10
10
* 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 $
12
12
*
13
13
*-------------------------------------------------------------------------
14
14
*/
24
24
#include "libpq-fe.h"
25
25
#include "libpq/libpq-fs.h"
26
26
27
+ #define atooid (x ) ((Oid) strtoul((x), NULL, 10))
28
+
27
29
#define BUFSIZE 1024
28
30
29
31
int vacuumlo (char * , int );
30
32
31
33
32
34
/*
33
- * This vacuums a database. It returns 1 on success, -1 on failure.
35
+ * This vacuums LOs of one database. It returns 0 on success, -1 on failure.
34
36
*/
35
37
int
36
38
vacuumlo (char * database , int verbose )
@@ -39,40 +41,68 @@ vacuumlo(char *database, int verbose)
39
41
PGresult * res ,
40
42
* res2 ;
41
43
char buf [BUFSIZE ];
42
- int matched = 0 ; /* Number matched per scan */
44
+ int matched ;
45
+ int deleted ;
43
46
int i ;
44
47
45
48
conn = PQsetdb (NULL , NULL , NULL , NULL , database );
46
49
47
50
/* check to see that the backend connection was successfully made */
48
51
if (PQstatus (conn ) == CONNECTION_BAD )
49
52
{
50
- fprintf (stderr , "Connection to database '%s' failed. \n" , database );
53
+ fprintf (stderr , "Connection to database '%s' failed: \n" , database );
51
54
fprintf (stderr , "%s" , PQerrorMessage (conn ));
55
+ PQfinish (conn );
52
56
return -1 ;
53
57
}
54
58
55
59
if (verbose )
56
60
fprintf (stdout , "Connected to %s\n" , database );
57
61
58
62
/*
59
- * First we create and populate the lo temp table
63
+ * First we create and populate the LO temp table
60
64
*/
61
65
buf [0 ] = '\0' ;
62
66
strcat (buf , "SELECT DISTINCT loid AS lo " );
63
67
strcat (buf , "INTO TEMP TABLE vacuum_l " );
64
68
strcat (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 )
66
87
{
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 );
68
91
PQfinish (conn );
69
92
return -1 ;
70
93
}
71
94
PQclear (res );
72
95
73
96
/*
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.
76
106
*/
77
107
buf [0 ] = '\0' ;
78
108
strcat (buf , "SELECT c.relname, a.attname " );
@@ -81,13 +111,18 @@ vacuumlo(char *database, int verbose)
81
111
strcat (buf , " AND a.attrelid = c.oid " );
82
112
strcat (buf , " AND a.atttypid = t.oid " );
83
113
strcat (buf , " AND t.typname = 'oid' " );
114
+ strcat (buf , " AND c.relkind = 'r'" );
84
115
strcat (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 )
86
118
{
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 );
88
122
PQfinish (conn );
89
123
return -1 ;
90
124
}
125
+
91
126
for (i = 0 ; i < PQntuples (res ); i ++ )
92
127
{
93
128
char * table ,
@@ -97,50 +132,36 @@ vacuumlo(char *database, int verbose)
97
132
field = PQgetvalue (res , i , 1 );
98
133
99
134
if (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 );
123
145
if (PQresultStatus (res2 ) != PGRES_COMMAND_OK )
124
146
{
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 ));
130
150
PQclear (res2 );
131
151
PQclear (res );
132
152
PQfinish (conn );
133
153
return -1 ;
134
154
}
135
155
PQclear (res2 );
136
-
137
- res2 = PQexec (conn , "end" );
138
- PQclear (res2 );
139
-
140
156
}
141
157
PQclear (res );
142
158
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
+ */
144
165
res = PQexec (conn , "begin" );
145
166
PQclear (res );
146
167
@@ -150,25 +171,35 @@ vacuumlo(char *database, int verbose)
150
171
buf [0 ] = '\0' ;
151
172
strcat (buf , "SELECT lo " );
152
173
strcat (buf , "FROM vacuum_l" );
153
- if (!(res = PQexec (conn , buf )))
174
+ res = PQexec (conn , buf );
175
+ if (PQresultStatus (res ) != PGRES_TUPLES_OK )
154
176
{
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 );
156
180
PQfinish (conn );
157
181
return -1 ;
158
182
}
183
+
159
184
matched = PQntuples (res );
185
+ deleted = 0 ;
160
186
for (i = 0 ; i < matched ; i ++ )
161
187
{
162
- Oid lo = ( Oid ) atoi (PQgetvalue (res , i , 0 ));
188
+ Oid lo = atooid (PQgetvalue (res , i , 0 ));
163
189
164
190
if (verbose )
165
191
{
166
- fprintf (stdout , "\rRemoving lo %6d \n " , lo );
192
+ fprintf (stdout , "\rRemoving lo %6u " , lo );
167
193
fflush (stdout );
168
194
}
169
195
170
196
if (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 ++ ;
172
203
}
173
204
PQclear (res );
174
205
@@ -177,10 +208,12 @@ vacuumlo(char *database, int verbose)
177
208
*/
178
209
res = PQexec (conn , "end" );
179
210
PQclear (res );
211
+
180
212
PQfinish (conn );
181
213
182
214
if (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 );
184
217
185
218
return 0 ;
186
219
}
@@ -204,7 +237,7 @@ main(int argc, char **argv)
204
237
if (strcmp ("-v" , argv [arg ]) == 0 )
205
238
verbose = !verbose ;
206
239
else
207
- rc += vacuumlo (argv [arg ], verbose );
240
+ rc += ( vacuumlo (argv [arg ], verbose ) != 0 );
208
241
}
209
242
210
243
return rc ;
0 commit comments