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