Line data Source code
1 : /*
2 : * psql - the PostgreSQL interactive terminal
3 : *
4 : * Copyright (c) 2000-2017, 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
18 6 : print_lo_result(const char *fmt,...)
19 : {
20 : va_list ap;
21 :
22 6 : if (!pset.quiet)
23 : {
24 0 : if (pset.popt.topt.format == PRINT_HTML)
25 0 : fputs("<p>", pset.queryFout);
26 :
27 0 : va_start(ap, fmt);
28 0 : vfprintf(pset.queryFout, fmt, ap);
29 0 : va_end(ap);
30 :
31 0 : if (pset.popt.topt.format == PRINT_HTML)
32 0 : fputs("</p>\n", pset.queryFout);
33 : else
34 0 : fputs("\n", pset.queryFout);
35 : }
36 :
37 6 : if (pset.logfile)
38 : {
39 0 : va_start(ap, fmt);
40 0 : vfprintf(pset.logfile, fmt, ap);
41 0 : va_end(ap);
42 0 : fputs("\n", pset.logfile);
43 : }
44 6 : }
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
55 6 : start_lo_xact(const char *operation, bool *own_transaction)
56 : {
57 : PGTransactionStatusType tstatus;
58 : PGresult *res;
59 :
60 6 : *own_transaction = false;
61 :
62 6 : if (!pset.db)
63 : {
64 0 : psql_error("%s: not connected to a database\n", operation);
65 0 : return false;
66 : }
67 :
68 6 : tstatus = PQtransactionStatus(pset.db);
69 :
70 6 : switch (tstatus)
71 : {
72 : case PQTRANS_IDLE:
73 : /* need to start our own xact */
74 6 : if (!(res = PSQLexec("BEGIN")))
75 0 : return false;
76 6 : PQclear(res);
77 6 : *own_transaction = true;
78 6 : break;
79 : case PQTRANS_INTRANS:
80 : /* use the existing xact */
81 0 : break;
82 : case PQTRANS_INERROR:
83 0 : psql_error("%s: current transaction is aborted\n", operation);
84 0 : return false;
85 : default:
86 0 : psql_error("%s: unknown transaction status\n", operation);
87 0 : return false;
88 : }
89 :
90 6 : return true;
91 : }
92 :
93 : /*
94 : * Clean up after a successful LO operation
95 : */
96 : static bool
97 6 : finish_lo_xact(const char *operation, bool own_transaction)
98 : {
99 : PGresult *res;
100 :
101 6 : if (own_transaction && pset.autocommit)
102 : {
103 : /* close out our own xact */
104 6 : if (!(res = PSQLexec("COMMIT")))
105 : {
106 0 : res = PSQLexec("ROLLBACK");
107 0 : PQclear(res);
108 0 : return false;
109 : }
110 6 : PQclear(res);
111 : }
112 :
113 6 : return true;
114 : }
115 :
116 : /*
117 : * Clean up after a failed LO operation
118 : */
119 : static bool
120 0 : fail_lo_xact(const char *operation, bool own_transaction)
121 : {
122 : PGresult *res;
123 :
124 0 : if (own_transaction && pset.autocommit)
125 : {
126 : /* close out our own xact */
127 0 : res = PSQLexec("ROLLBACK");
128 0 : PQclear(res);
129 : }
130 :
131 0 : return false; /* always */
132 : }
133 :
134 :
135 : /*
136 : * do_lo_export()
137 : *
138 : * Write a large object to a file
139 : */
140 : bool
141 1 : do_lo_export(const char *loid_arg, const char *filename_arg)
142 : {
143 : int status;
144 : bool own_transaction;
145 :
146 1 : if (!start_lo_xact("\\lo_export", &own_transaction))
147 0 : return false;
148 :
149 1 : SetCancelConn();
150 1 : status = lo_export(pset.db, atooid(loid_arg), filename_arg);
151 1 : ResetCancelConn();
152 :
153 : /* of course this status is documented nowhere :( */
154 1 : if (status != 1)
155 : {
156 0 : psql_error("%s", PQerrorMessage(pset.db));
157 0 : return fail_lo_xact("\\lo_export", own_transaction);
158 : }
159 :
160 1 : if (!finish_lo_xact("\\lo_export", own_transaction))
161 0 : return false;
162 :
163 1 : print_lo_result("lo_export");
164 :
165 1 : return true;
166 : }
167 :
168 :
169 : /*
170 : * do_lo_import()
171 : *
172 : * Copy large object from file to database
173 : */
174 : bool
175 2 : 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 2 : if (!start_lo_xact("\\lo_import", &own_transaction))
183 0 : return false;
184 :
185 2 : SetCancelConn();
186 2 : loid = lo_import(pset.db, filename_arg);
187 2 : ResetCancelConn();
188 :
189 2 : if (loid == InvalidOid)
190 : {
191 0 : psql_error("%s", PQerrorMessage(pset.db));
192 0 : return fail_lo_xact("\\lo_import", own_transaction);
193 : }
194 :
195 : /* insert description if given */
196 2 : if (comment_arg)
197 : {
198 : char *cmdbuf;
199 : char *bufptr;
200 0 : size_t slen = strlen(comment_arg);
201 :
202 0 : cmdbuf = malloc(slen * 2 + 256);
203 0 : if (!cmdbuf)
204 0 : return fail_lo_xact("\\lo_import", own_transaction);
205 0 : sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
206 0 : bufptr = cmdbuf + strlen(cmdbuf);
207 0 : bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
208 0 : strcpy(bufptr, "'");
209 :
210 0 : if (!(res = PSQLexec(cmdbuf)))
211 : {
212 0 : free(cmdbuf);
213 0 : return fail_lo_xact("\\lo_import", own_transaction);
214 : }
215 :
216 0 : PQclear(res);
217 0 : free(cmdbuf);
218 : }
219 :
220 2 : if (!finish_lo_xact("\\lo_import", own_transaction))
221 0 : return false;
222 :
223 2 : print_lo_result("lo_import %u", loid);
224 :
225 2 : sprintf(oidbuf, "%u", loid);
226 2 : SetVariable(pset.vars, "LASTOID", oidbuf);
227 :
228 2 : return true;
229 : }
230 :
231 :
232 : /*
233 : * do_lo_unlink()
234 : *
235 : * removes a large object out of the database
236 : */
237 : bool
238 3 : do_lo_unlink(const char *loid_arg)
239 : {
240 : int status;
241 3 : Oid loid = atooid(loid_arg);
242 : bool own_transaction;
243 :
244 3 : if (!start_lo_xact("\\lo_unlink", &own_transaction))
245 0 : return false;
246 :
247 3 : SetCancelConn();
248 3 : status = lo_unlink(pset.db, loid);
249 3 : ResetCancelConn();
250 :
251 3 : if (status == -1)
252 : {
253 0 : psql_error("%s", PQerrorMessage(pset.db));
254 0 : return fail_lo_xact("\\lo_unlink", own_transaction);
255 : }
256 :
257 3 : if (!finish_lo_xact("\\lo_unlink", own_transaction))
258 0 : return false;
259 :
260 3 : print_lo_result("lo_unlink %u", loid);
261 :
262 3 : return true;
263 : }
264 :
265 :
266 :
267 : /*
268 : * do_lo_list()
269 : *
270 : * Show all large objects in database with comments
271 : */
272 : bool
273 0 : do_lo_list(void)
274 : {
275 : PGresult *res;
276 : char buf[1024];
277 0 : printQueryOpt myopt = pset.popt;
278 :
279 0 : if (pset.sversion >= 90000)
280 : {
281 0 : 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 0 : 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 0 : res = PSQLexec(buf);
303 0 : if (!res)
304 0 : return false;
305 :
306 0 : myopt.topt.tuples_only = false;
307 0 : myopt.nullPrint = NULL;
308 0 : myopt.title = _("Large objects");
309 0 : myopt.translate_header = true;
310 :
311 0 : printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
312 :
313 0 : PQclear(res);
314 0 : return true;
315 : }
|