/Users/deen/code/yugabyte-db/src/postgres/src/bin/psql/large_obj.c
Line | Count | Source (jump to first uncovered line) |
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 |
18 | | print_lo_result(const char *fmt,...) |
19 | 0 | { |
20 | 0 | va_list ap; |
21 | |
|
22 | 0 | if (!pset.quiet) |
23 | 0 | { |
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 | 0 | else |
34 | 0 | fputs("\n", pset.queryFout); |
35 | 0 | } |
36 | |
|
37 | 0 | if (pset.logfile) |
38 | 0 | { |
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 | 0 | } |
44 | 0 | } |
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 | | start_lo_xact(const char *operation, bool *own_transaction) |
56 | 0 | { |
57 | 0 | PGTransactionStatusType tstatus; |
58 | 0 | PGresult *res; |
59 | |
|
60 | 0 | *own_transaction = false; |
61 | |
|
62 | 0 | if (!pset.db) |
63 | 0 | { |
64 | 0 | psql_error("%s: not connected to a database\n", operation); |
65 | 0 | return false; |
66 | 0 | } |
67 | | |
68 | 0 | tstatus = PQtransactionStatus(pset.db); |
69 | |
|
70 | 0 | switch (tstatus) |
71 | 0 | { |
72 | 0 | case PQTRANS_IDLE: |
73 | | /* need to start our own xact */ |
74 | 0 | if (!(res = PSQLexec("BEGIN"))) |
75 | 0 | return false; |
76 | 0 | PQclear(res); |
77 | 0 | *own_transaction = true; |
78 | 0 | break; |
79 | 0 | case PQTRANS_INTRANS: |
80 | | /* use the existing xact */ |
81 | 0 | break; |
82 | 0 | case PQTRANS_INERROR: |
83 | 0 | psql_error("%s: current transaction is aborted\n", operation); |
84 | 0 | return false; |
85 | 0 | default: |
86 | 0 | psql_error("%s: unknown transaction status\n", operation); |
87 | 0 | return false; |
88 | 0 | } |
89 | | |
90 | 0 | return true; |
91 | 0 | } |
92 | | |
93 | | /* |
94 | | * Clean up after a successful LO operation |
95 | | */ |
96 | | static bool |
97 | | finish_lo_xact(const char *operation, bool own_transaction) |
98 | 0 | { |
99 | 0 | PGresult *res; |
100 | |
|
101 | 0 | if (own_transaction && pset.autocommit) |
102 | 0 | { |
103 | | /* close out our own xact */ |
104 | 0 | if (!(res = PSQLexec("COMMIT"))) |
105 | 0 | { |
106 | 0 | res = PSQLexec("ROLLBACK"); |
107 | 0 | PQclear(res); |
108 | 0 | return false; |
109 | 0 | } |
110 | 0 | PQclear(res); |
111 | 0 | } |
112 | |
|
113 | 0 | return true; |
114 | 0 | } |
115 | | |
116 | | /* |
117 | | * Clean up after a failed LO operation |
118 | | */ |
119 | | static bool |
120 | | fail_lo_xact(const char *operation, bool own_transaction) |
121 | 0 | { |
122 | 0 | PGresult *res; |
123 | |
|
124 | 0 | if (own_transaction && pset.autocommit) |
125 | 0 | { |
126 | | /* close out our own xact */ |
127 | 0 | res = PSQLexec("ROLLBACK"); |
128 | 0 | PQclear(res); |
129 | 0 | } |
130 | |
|
131 | 0 | return false; /* always */ |
132 | 0 | } |
133 | | |
134 | | |
135 | | /* |
136 | | * do_lo_export() |
137 | | * |
138 | | * Write a large object to a file |
139 | | */ |
140 | | bool |
141 | | do_lo_export(const char *loid_arg, const char *filename_arg) |
142 | 0 | { |
143 | 0 | int status; |
144 | 0 | bool own_transaction; |
145 | |
|
146 | 0 | if (!start_lo_xact("\\lo_export", &own_transaction)) |
147 | 0 | return false; |
148 | | |
149 | 0 | SetCancelConn(); |
150 | 0 | status = lo_export(pset.db, atooid(loid_arg), filename_arg); |
151 | 0 | ResetCancelConn(); |
152 | | |
153 | | /* of course this status is documented nowhere :( */ |
154 | 0 | if (status != 1) |
155 | 0 | { |
156 | 0 | psql_error("%s", PQerrorMessage(pset.db)); |
157 | 0 | return fail_lo_xact("\\lo_export", own_transaction); |
158 | 0 | } |
159 | | |
160 | 0 | if (!finish_lo_xact("\\lo_export", own_transaction)) |
161 | 0 | return false; |
162 | | |
163 | 0 | print_lo_result("lo_export"); |
164 | |
|
165 | 0 | return true; |
166 | 0 | } |
167 | | |
168 | | |
169 | | /* |
170 | | * do_lo_import() |
171 | | * |
172 | | * Copy large object from file to database |
173 | | */ |
174 | | bool |
175 | | do_lo_import(const char *filename_arg, const char *comment_arg) |
176 | 0 | { |
177 | 0 | PGresult *res; |
178 | 0 | Oid loid; |
179 | 0 | char oidbuf[32]; |
180 | 0 | bool own_transaction; |
181 | |
|
182 | 0 | if (!start_lo_xact("\\lo_import", &own_transaction)) |
183 | 0 | return false; |
184 | | |
185 | 0 | SetCancelConn(); |
186 | 0 | loid = lo_import(pset.db, filename_arg); |
187 | 0 | ResetCancelConn(); |
188 | |
|
189 | 0 | if (loid == InvalidOid) |
190 | 0 | { |
191 | 0 | psql_error("%s", PQerrorMessage(pset.db)); |
192 | 0 | return fail_lo_xact("\\lo_import", own_transaction); |
193 | 0 | } |
194 | | |
195 | | /* insert description if given */ |
196 | 0 | if (comment_arg) |
197 | 0 | { |
198 | 0 | char *cmdbuf; |
199 | 0 | 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 | 0 | { |
212 | 0 | free(cmdbuf); |
213 | 0 | return fail_lo_xact("\\lo_import", own_transaction); |
214 | 0 | } |
215 | | |
216 | 0 | PQclear(res); |
217 | 0 | free(cmdbuf); |
218 | 0 | } |
219 | |
|
220 | 0 | if (!finish_lo_xact("\\lo_import", own_transaction)) |
221 | 0 | return false; |
222 | | |
223 | 0 | print_lo_result("lo_import %u", loid); |
224 | |
|
225 | 0 | sprintf(oidbuf, "%u", loid); |
226 | 0 | SetVariable(pset.vars, "LASTOID", oidbuf); |
227 | |
|
228 | 0 | return true; |
229 | 0 | } |
230 | | |
231 | | |
232 | | /* |
233 | | * do_lo_unlink() |
234 | | * |
235 | | * removes a large object out of the database |
236 | | */ |
237 | | bool |
238 | | do_lo_unlink(const char *loid_arg) |
239 | 0 | { |
240 | 0 | int status; |
241 | 0 | Oid loid = atooid(loid_arg); |
242 | 0 | bool own_transaction; |
243 | |
|
244 | 0 | if (!start_lo_xact("\\lo_unlink", &own_transaction)) |
245 | 0 | return false; |
246 | | |
247 | 0 | SetCancelConn(); |
248 | 0 | status = lo_unlink(pset.db, loid); |
249 | 0 | ResetCancelConn(); |
250 | |
|
251 | 0 | if (status == -1) |
252 | 0 | { |
253 | 0 | psql_error("%s", PQerrorMessage(pset.db)); |
254 | 0 | return fail_lo_xact("\\lo_unlink", own_transaction); |
255 | 0 | } |
256 | | |
257 | 0 | if (!finish_lo_xact("\\lo_unlink", own_transaction)) |
258 | 0 | return false; |
259 | | |
260 | 0 | print_lo_result("lo_unlink %u", loid); |
261 | |
|
262 | 0 | return true; |
263 | 0 | } |
264 | | |
265 | | |
266 | | |
267 | | /* |
268 | | * do_lo_list() |
269 | | * |
270 | | * Show all large objects in database with comments |
271 | | */ |
272 | | bool |
273 | | do_lo_list(void) |
274 | 0 | { |
275 | 0 | PGresult *res; |
276 | 0 | char buf[1024]; |
277 | 0 | printQueryOpt myopt = pset.popt; |
278 | |
|
279 | 0 | if (pset.sversion >= 90000) |
280 | 0 | { |
281 | 0 | snprintf(buf, sizeof(buf), |
282 | 0 | "SELECT oid as \"%s\",\n" |
283 | 0 | " pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n" |
284 | 0 | " pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n" |
285 | 0 | " FROM pg_catalog.pg_largeobject_metadata " |
286 | 0 | " ORDER BY oid", |
287 | 0 | gettext_noop("ID"), |
288 | 0 | gettext_noop("Owner"), |
289 | 0 | gettext_noop("Description")); |
290 | 0 | } |
291 | 0 | else |
292 | 0 | { |
293 | 0 | snprintf(buf, sizeof(buf), |
294 | 0 | "SELECT loid as \"%s\",\n" |
295 | 0 | " pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n" |
296 | 0 | "FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n" |
297 | 0 | "ORDER BY 1", |
298 | 0 | gettext_noop("ID"), |
299 | 0 | gettext_noop("Description")); |
300 | 0 | } |
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 | 0 | } |