OSDN Git Service

Major overhaul of large-object implementation, by Denis Perchine with
[pg-rex/syncrep.git] / contrib / vacuumlo / vacuumlo.c
1 /*-------------------------------------------------------------------------
2  *
3  * vacuumlo.c
4  *        This removes orphaned large objects from a database.
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/contrib/vacuumlo/vacuumlo.c,v 1.6 2000/10/24 01:38:20 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23
24 #include "libpq-fe.h"
25 #include "libpq/libpq-fs.h"
26
27 #define BUFSIZE                 1024
28
29 int                     vacuumlo(char *, int);
30
31
32 /*
33  * This vacuums a database. It returns 1 on success, -1 on failure.
34  */
35 int
36 vacuumlo(char *database, int verbose)
37 {
38         PGconn     *conn;
39         PGresult   *res,
40                            *res2;
41         char            buf[BUFSIZE];
42         int                     matched = 0;    /* Number matched per scan */
43         int                     i;
44
45         conn = PQsetdb(NULL, NULL, NULL, NULL, database);
46
47         /* check to see that the backend connection was successfully made */
48         if (PQstatus(conn) == CONNECTION_BAD)
49         {
50                 fprintf(stderr, "Connection to database '%s' failed.\n", database);
51                 fprintf(stderr, "%s", PQerrorMessage(conn));
52                 return -1;
53         }
54
55         if (verbose)
56                 fprintf(stdout, "Connected to %s\n", database);
57
58         /*
59          * First we create and populate the lo temp table
60          */
61         buf[0] = '\0';
62         strcat(buf, "SELECT DISTINCT loid AS lo ");
63         strcat(buf, "INTO TEMP TABLE vacuum_l ");
64         strcat(buf, "FROM pg_largeobject ");
65         if (!(res = PQexec(conn, buf)))
66         {
67                 fprintf(stderr, "Failed to create temp table.\n");
68                 PQfinish(conn);
69                 return -1;
70         }
71         PQclear(res);
72
73         /*
74          * Now find any candidate tables who have columns of type oid (the
75          * column oid is ignored, as it has attnum < 1)
76          */
77         buf[0] = '\0';
78         strcat(buf, "SELECT c.relname, a.attname ");
79         strcat(buf, "FROM pg_class c, pg_attribute a, pg_type t ");
80         strcat(buf, "WHERE a.attnum > 0 ");
81         strcat(buf, "      AND a.attrelid = c.oid ");
82         strcat(buf, "      AND a.atttypid = t.oid ");
83         strcat(buf, "      AND t.typname = 'oid' ");
84         strcat(buf, "      AND c.relname NOT LIKE 'pg_%'");
85         if (!(res = PQexec(conn, buf)))
86         {
87                 fprintf(stderr, "Failed to create temp table.\n");
88                 PQfinish(conn);
89                 return -1;
90         }
91         for (i = 0; i < PQntuples(res); i++)
92         {
93                 char       *table,
94                                    *field;
95
96                 table = PQgetvalue(res, i, 0);
97                 field = PQgetvalue(res, i, 1);
98
99                 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                 }
123                 if (PQresultStatus(res2) != PGRES_COMMAND_OK)
124                 {
125                         fprintf(stderr,
126                                         "Failed to check %s in table %s\n%s\n",
127                                         field, table,
128                                         PQerrorMessage(conn)
129                                 );
130                         PQclear(res2);
131                         PQclear(res);
132                         PQfinish(conn);
133                         return -1;
134                 }
135                 PQclear(res2);
136
137                 res2 = PQexec(conn, "end");
138                 PQclear(res2);
139
140         }
141         PQclear(res);
142
143         /* Start the transaction */
144         res = PQexec(conn, "begin");
145         PQclear(res);
146
147         /*
148          * Finally, those entries remaining in vacuum_l are orphans.
149          */
150         buf[0] = '\0';
151         strcat(buf, "SELECT lo ");
152         strcat(buf, "FROM vacuum_l");
153         if (!(res = PQexec(conn, buf)))
154         {
155                 fprintf(stderr, "Failed to read temp table.\n");
156                 PQfinish(conn);
157                 return -1;
158         }
159         matched = PQntuples(res);
160         for (i = 0; i < matched; i++)
161         {
162                 Oid                     lo = (Oid) atoi(PQgetvalue(res, i, 0));
163
164                 if (verbose)
165                 {
166                         fprintf(stdout, "\rRemoving lo %6d \n", lo);
167                         fflush(stdout);
168                 }
169
170                 if (lo_unlink(conn, lo) < 0)
171                         fprintf(stderr, "Failed to remove lo %d\n", lo);
172         }
173         PQclear(res);
174
175         /*
176          * That's all folks!
177          */
178         res = PQexec(conn, "end");
179         PQclear(res);
180         PQfinish(conn);
181
182         if (verbose)
183                 fprintf(stdout, "\rRemoved %d large objects from %s.\n", matched, database);
184
185         return 0;
186 }
187
188 int
189 main(int argc, char **argv)
190 {
191         int                     verbose = 0;
192         int                     arg;
193         int                     rc = 0;
194
195         if (argc < 2)
196         {
197                 fprintf(stderr, "Usage: %s [-v] database_name [db2 ... dbn]\n",
198                                 argv[0]);
199                 exit(1);
200         }
201
202         for (arg = 1; arg < argc; arg++)
203         {
204                 if (strcmp("-v", argv[arg]) == 0)
205                         verbose = !verbose;
206                 else
207                         rc += vacuumlo(argv[arg], verbose);
208         }
209
210         return rc;
211 }