]> granicus.if.org Git - postgresql/blob - src/bin/psql/large_obj.c
I'm continuing to work on cleaning up code in psql. As things appear
[postgresql] / src / bin / psql / large_obj.c
1 /*
2  * psql - the PostgreSQL interactive terminal
3  *
4  * Copyright 2000-2002 by PostgreSQL Global Development Group
5  *
6  * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.24 2003/03/20 06:43:35 momjian Exp $
7  */
8 #include "postgres_fe.h"
9 #include "large_obj.h"
10
11 #include "libpq-fe.h"
12
13 #include "settings.h"
14 #include "variables.h"
15 #include "common.h"
16 #include "print.h"
17
18
19 #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
20
21
22 /*
23  * Since all large object ops must be in a transaction, we must do some magic
24  * here. You can set the variable lo_transaction to one of commit|rollback|
25  * nothing to get your favourite behaviour regarding any transaction in
26  * progress. Rollback is default.
27  */
28
29 static char notice[80];
30
31 static void
32 _my_notice_handler(void *arg, const char *message)
33 {
34         (void) arg;
35         strncpy(notice, message, 79);
36         notice[79] = '\0';
37 }
38
39
40 static bool
41 handle_transaction(void)
42 {
43         PGresult   *res;
44         bool            commit = false;
45         PQnoticeProcessor old_notice_hook;
46
47         switch (SwitchVariable(pset.vars, "LO_TRANSACTION", 
48                                                                         "nothing", 
49                                                                         "commit", 
50                                                                         NULL))
51         {
52           case 1:
53                 return true;
54           case 2:
55                  commit = true;
56         }
57
58         notice[0] = '\0';
59         old_notice_hook = PQsetNoticeProcessor(pset.db, _my_notice_handler, NULL);
60
61         res = PSQLexec(commit ? "COMMIT" : "ROLLBACK", false);
62         if (!res)
63                 return false;
64
65         if (notice[0])
66         {
67                 if ((!commit && strcmp(notice, "WARNING:  ROLLBACK: no transaction in progress\n") != 0) ||
68                         (commit && strcmp(notice, "WARNING:  COMMIT: no transaction in progress\n") != 0))
69                         fputs(notice, stderr);
70         }
71         else if (!QUIET())
72         {
73                 if (commit)
74                         puts(gettext("Warning: Your transaction in progress has been committed."));
75                 else
76                         puts(gettext("Warning: Your transaction in progress has been rolled back."));
77         }
78
79         PQsetNoticeProcessor(pset.db, old_notice_hook, NULL);
80         return true;
81 }
82
83
84
85 /*
86  * do_lo_export()
87  *
88  * Write a large object to a file
89  */
90 bool
91 do_lo_export(const char *loid_arg, const char *filename_arg)
92 {
93         PGresult   *res;
94         int                     status;
95         bool            own_transaction;
96
97         own_transaction = VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
98
99         if (!pset.db)
100         {
101                 psql_error("\\lo_export: not connected to a database\n");
102                 return false;
103         }
104
105         if (own_transaction)
106         {
107                 if (!handle_transaction())
108                         return false;
109
110                 if (!(res = PSQLexec("BEGIN", false)))
111                         return false;
112
113                 PQclear(res);
114         }
115
116         status = lo_export(pset.db, atooid(loid_arg), filename_arg);
117         if (status != 1)
118         {                                                       /* of course this status is documented
119                                                                  * nowhere :( */
120                 fputs(PQerrorMessage(pset.db), stderr);
121                 if (own_transaction)
122                 {
123                         res = PQexec(pset.db, "ROLLBACK");
124                         PQclear(res);
125                 }
126                 return false;
127         }
128
129         if (own_transaction)
130         {
131                 if (!(res = PSQLexec("COMMIT", false)))
132                 {
133                         res = PQexec(pset.db, "ROLLBACK");
134                         PQclear(res);
135                         return false;
136                 }
137
138                 PQclear(res);
139         }
140
141         fprintf(pset.queryFout, "lo_export\n");
142
143         return true;
144 }
145
146
147
148 /*
149  * do_lo_import()
150  *
151  * Copy large object from file to database
152  */
153 bool
154 do_lo_import(const char *filename_arg, const char *comment_arg)
155 {
156         PGresult   *res;
157         Oid                     loid;
158         char            oidbuf[32];
159         unsigned int i;
160         bool            own_transaction;
161
162         own_transaction = VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
163
164         if (!pset.db)
165         {
166                 psql_error("\\lo_import: not connected to a database\n");
167                 return false;
168         }
169
170         if (own_transaction)
171         {
172                 if (!handle_transaction())
173                         return false;
174
175                 if (!(res = PSQLexec("BEGIN", false)))
176                         return false;
177
178                 PQclear(res);
179         }
180
181         loid = lo_import(pset.db, filename_arg);
182         if (loid == InvalidOid)
183         {
184                 fputs(PQerrorMessage(pset.db), stderr);
185                 if (own_transaction)
186                 {
187                         res = PQexec(pset.db, "ROLLBACK");
188                         PQclear(res);
189                 }
190                 return false;
191         }
192
193         /* insert description if given */
194         /* XXX don't try to hack pg_description if not superuser */
195         /* XXX ought to replace this with some kind of COMMENT command */
196         if (comment_arg && pset.issuper)
197         {
198                 char       *cmdbuf;
199                 char       *bufptr;
200                 size_t          slen = strlen(comment_arg);
201
202                 cmdbuf = malloc(slen * 2 + 256);
203                 if (!cmdbuf)
204                 {
205                         if (own_transaction)
206                         {
207                                 res = PQexec(pset.db, "ROLLBACK");
208                                 PQclear(res);
209                         }
210                         return false;
211                 }
212                 sprintf(cmdbuf,
213                                 "INSERT INTO pg_catalog.pg_description VALUES ('%u', "
214                                 "'pg_catalog.pg_largeobject'::regclass, "
215                                 "0, '",
216                                 loid);
217                 bufptr = cmdbuf + strlen(cmdbuf);
218                 for (i = 0; i < slen; i++)
219                 {
220                         if (comment_arg[i] == '\'' || comment_arg[i] == '\\')
221                                 *bufptr++ = '\\';
222                         *bufptr++ = comment_arg[i];
223                 }
224                 strcpy(bufptr, "')");
225
226                 if (!(res = PSQLexec(cmdbuf, false)))
227                 {
228                         if (own_transaction)
229                         {
230                                 res = PQexec(pset.db, "ROLLBACK");
231                                 PQclear(res);
232                         }
233                         free(cmdbuf);
234                         return false;
235                 }
236
237                 PQclear(res);
238                 free(cmdbuf);
239         }
240
241         if (own_transaction)
242         {
243                 if (!(res = PSQLexec("COMMIT", false)))
244                 {
245                         res = PQexec(pset.db, "ROLLBACK");
246                         PQclear(res);
247                         return false;
248                 }
249
250                 PQclear(res);
251         }
252
253
254         fprintf(pset.queryFout, "lo_import %u\n", loid);
255         sprintf(oidbuf, "%u", loid);
256         SetVariable(pset.vars, "LASTOID", oidbuf);
257
258         return true;
259 }
260
261
262
263 /*
264  * do_lo_unlink()
265  *
266  * removes a large object out of the database
267  */
268 bool
269 do_lo_unlink(const char *loid_arg)
270 {
271         PGresult   *res;
272         int                     status;
273         Oid                     loid = atooid(loid_arg);
274         char            buf[256];
275
276         bool            own_transaction;
277
278         own_transaction = VariableEquals(pset.vars, "LO_TRANSACTION", "nothing");
279
280         if (!pset.db)
281         {
282                 psql_error("\\lo_unlink: not connected to a database\n");
283                 return false;
284         }
285
286         if (own_transaction)
287         {
288                 if (!handle_transaction())
289                         return false;
290
291                 if (!(res = PSQLexec("BEGIN", false)))
292                         return false;
293
294                 PQclear(res);
295         }
296
297         status = lo_unlink(pset.db, loid);
298         if (status == -1)
299         {
300                 fputs(PQerrorMessage(pset.db), stderr);
301                 if (own_transaction)
302                 {
303                         res = PQexec(pset.db, "ROLLBACK");
304                         PQclear(res);
305                 }
306                 return false;
307         }
308
309         /* remove the comment as well */
310         /* XXX don't try to hack pg_description if not superuser */
311         /* XXX ought to replace this with some kind of COMMENT command */
312         if (pset.issuper)
313         {
314                 sprintf(buf, "DELETE FROM pg_catalog.pg_description WHERE objoid = '%u' "
315                                 "AND classoid = 'pg_catalog.pg_largeobject'::regclass",
316                                 loid);
317                 if (!(res = PSQLexec(buf, false)))
318                 {
319                         if (own_transaction)
320                         {
321                                 res = PQexec(pset.db, "ROLLBACK");
322                                 PQclear(res);
323                         }
324                         return false;
325                 }
326         }
327
328         if (own_transaction)
329         {
330                 if (!(res = PSQLexec("COMMIT", false)))
331                 {
332                         res = PQexec(pset.db, "ROLLBACK");
333                         PQclear(res);
334                         return false;
335                 }
336                 PQclear(res);
337         }
338
339
340         fprintf(pset.queryFout, "lo_unlink %u\n", loid);
341
342         return true;
343 }
344
345
346
347 /*
348  * do_lo_list()
349  *
350  * Show all large objects in database with comments
351  */
352 bool
353 do_lo_list(void)
354 {
355         PGresult   *res;
356         char            buf[1024];
357         printQueryOpt myopt = pset.popt;
358
359         snprintf(buf, sizeof(buf),
360                          "SELECT loid as \"ID\", pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
361                  "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
362                          "ORDER BY \"ID\"",
363                          gettext("Description"));
364
365         res = PSQLexec(buf, false);
366         if (!res)
367                 return false;
368
369         myopt.topt.tuples_only = false;
370         myopt.nullPrint = NULL;
371         myopt.title = gettext("Large objects");
372
373         printQuery(res, &myopt, pset.queryFout);
374
375         PQclear(res);
376         return true;
377 }