1 /*
2 * psql - the PostgreSQL interactive terminal
3 *
4 * Copyright (c) 2000-2018, PostgreSQL Global Development Group
5 *
6 * src/bin/psql/large_obj.c
7 */
8 #include "postgres_fe.h"
9 #include "large_obj.h"
10
11
12 #include "settings.h"
13 #include "common.h"
14
15 static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
16
17 static void
print_lo_result(const char * fmt,...)18 print_lo_result(const char *fmt,...)
19 {
20 va_list ap;
21
22 if (!pset.quiet)
23 {
24 if (pset.popt.topt.format == PRINT_HTML)
25 fputs("<p>", pset.queryFout);
26
27 va_start(ap, fmt);
28 vfprintf(pset.queryFout, fmt, ap);
29 va_end(ap);
30
31 if (pset.popt.topt.format == PRINT_HTML)
32 fputs("</p>\n", pset.queryFout);
33 else
34 fputs("\n", pset.queryFout);
35 }
36
37 if (pset.logfile)
38 {
39 va_start(ap, fmt);
40 vfprintf(pset.logfile, fmt, ap);
41 va_end(ap);
42 fputs("\n", pset.logfile);
43 }
44 }
45
46
47 /*
48 * Prepare to do a large-object operation. We *must* be inside a transaction
49 * block for all these operations, so start one if needed.
50 *
51 * Returns true if okay, false if failed. *own_transaction is set to indicate
52 * if we started our own transaction or not.
53 */
54 static bool
start_lo_xact(const char * operation,bool * own_transaction)55 start_lo_xact(const char *operation, bool *own_transaction)
56 {
57 PGTransactionStatusType tstatus;
58 PGresult *res;
59
60 *own_transaction = false;
61
62 if (!pset.db)
63 {
64 psql_error("%s: not connected to a database\n", operation);
65 return false;
66 }
67
68 tstatus = PQtransactionStatus(pset.db);
69
70 switch (tstatus)
71 {
72 case PQTRANS_IDLE:
73 /* need to start our own xact */
74 if (!(res = PSQLexec("BEGIN")))
75 return false;
76 PQclear(res);
77 *own_transaction = true;
78 break;
79 case PQTRANS_INTRANS:
80 /* use the existing xact */
81 break;
82 case PQTRANS_INERROR:
83 psql_error("%s: current transaction is aborted\n", operation);
84 return false;
85 default:
86 psql_error("%s: unknown transaction status\n", operation);
87 return false;
88 }
89
90 return true;
91 }
92
93 /*
94 * Clean up after a successful LO operation
95 */
96 static bool
finish_lo_xact(const char * operation,bool own_transaction)97 finish_lo_xact(const char *operation, bool own_transaction)
98 {
99 PGresult *res;
100
101 if (own_transaction && pset.autocommit)
102 {
103 /* close out our own xact */
104 if (!(res = PSQLexec("COMMIT")))
105 {
106 res = PSQLexec("ROLLBACK");
107 PQclear(res);
108 return false;
109 }
110 PQclear(res);
111 }
112
113 return true;
114 }
115
116 /*
117 * Clean up after a failed LO operation
118 */
119 static bool
fail_lo_xact(const char * operation,bool own_transaction)120 fail_lo_xact(const char *operation, bool own_transaction)
121 {
122 PGresult *res;
123
124 if (own_transaction && pset.autocommit)
125 {
126 /* close out our own xact */
127 res = PSQLexec("ROLLBACK");
128 PQclear(res);
129 }
130
131 return false; /* always */
132 }
133
134
135 /*
136 * do_lo_export()
137 *
138 * Write a large object to a file
139 */
140 bool
do_lo_export(const char * loid_arg,const char * filename_arg)141 do_lo_export(const char *loid_arg, const char *filename_arg)
142 {
143 int status;
144 bool own_transaction;
145
146 if (!start_lo_xact("\\lo_export", &own_transaction))
147 return false;
148
149 SetCancelConn();
150 status = lo_export(pset.db, atooid(loid_arg), filename_arg);
151 ResetCancelConn();
152
153 /* of course this status is documented nowhere :( */
154 if (status != 1)
155 {
156 psql_error("%s", PQerrorMessage(pset.db));
157 return fail_lo_xact("\\lo_export", own_transaction);
158 }
159
160 if (!finish_lo_xact("\\lo_export", own_transaction))
161 return false;
162
163 print_lo_result("lo_export");
164
165 return true;
166 }
167
168
169 /*
170 * do_lo_import()
171 *
172 * Copy large object from file to database
173 */
174 bool
do_lo_import(const char * filename_arg,const char * comment_arg)175 do_lo_import(const char *filename_arg, const char *comment_arg)
176 {
177 PGresult *res;
178 Oid loid;
179 char oidbuf[32];
180 bool own_transaction;
181
182 if (!start_lo_xact("\\lo_import", &own_transaction))
183 return false;
184
185 SetCancelConn();
186 loid = lo_import(pset.db, filename_arg);
187 ResetCancelConn();
188
189 if (loid == InvalidOid)
190 {
191 psql_error("%s", PQerrorMessage(pset.db));
192 return fail_lo_xact("\\lo_import", own_transaction);
193 }
194
195 /* insert description if given */
196 if (comment_arg)
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 return fail_lo_xact("\\lo_import", own_transaction);
205 sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
206 bufptr = cmdbuf + strlen(cmdbuf);
207 bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
208 strcpy(bufptr, "'");
209
210 if (!(res = PSQLexec(cmdbuf)))
211 {
212 free(cmdbuf);
213 return fail_lo_xact("\\lo_import", own_transaction);
214 }
215
216 PQclear(res);
217 free(cmdbuf);
218 }
219
220 if (!finish_lo_xact("\\lo_import", own_transaction))
221 return false;
222
223 print_lo_result("lo_import %u", loid);
224
225 sprintf(oidbuf, "%u", loid);
226 SetVariable(pset.vars, "LASTOID", oidbuf);
227
228 return true;
229 }
230
231
232 /*
233 * do_lo_unlink()
234 *
235 * removes a large object out of the database
236 */
237 bool
do_lo_unlink(const char * loid_arg)238 do_lo_unlink(const char *loid_arg)
239 {
240 int status;
241 Oid loid = atooid(loid_arg);
242 bool own_transaction;
243
244 if (!start_lo_xact("\\lo_unlink", &own_transaction))
245 return false;
246
247 SetCancelConn();
248 status = lo_unlink(pset.db, loid);
249 ResetCancelConn();
250
251 if (status == -1)
252 {
253 psql_error("%s", PQerrorMessage(pset.db));
254 return fail_lo_xact("\\lo_unlink", own_transaction);
255 }
256
257 if (!finish_lo_xact("\\lo_unlink", own_transaction))
258 return false;
259
260 print_lo_result("lo_unlink %u", loid);
261
262 return true;
263 }
264
265
266
267 /*
268 * do_lo_list()
269 *
270 * Show all large objects in database with comments
271 */
272 bool
do_lo_list(void)273 do_lo_list(void)
274 {
275 PGresult *res;
276 char buf[1024];
277 printQueryOpt myopt = pset.popt;
278
279 if (pset.sversion >= 90000)
280 {
281 snprintf(buf, sizeof(buf),
282 "SELECT oid as \"%s\",\n"
283 " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
284 " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
285 " FROM pg_catalog.pg_largeobject_metadata "
286 " ORDER BY oid",
287 gettext_noop("ID"),
288 gettext_noop("Owner"),
289 gettext_noop("Description"));
290 }
291 else
292 {
293 snprintf(buf, sizeof(buf),
294 "SELECT loid as \"%s\",\n"
295 " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
296 "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
297 "ORDER BY 1",
298 gettext_noop("ID"),
299 gettext_noop("Description"));
300 }
301
302 res = PSQLexec(buf);
303 if (!res)
304 return false;
305
306 myopt.topt.tuples_only = false;
307 myopt.nullPrint = NULL;
308 myopt.title = _("Large objects");
309 myopt.translate_header = true;
310
311 printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
312
313 PQclear(res);
314 return true;
315 }
316