Branch data Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * fe-lobj.c
4 : : * Front-end large object interface
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : *
10 : : * IDENTIFICATION
11 : : * src/interfaces/libpq/fe-lobj.c
12 : : *
13 : : *-------------------------------------------------------------------------
14 : : */
15 : :
16 : : #ifdef WIN32
17 : : /*
18 : : * As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h
19 : : * must be included first on MS C. Might as well do it for all WIN32's
20 : : * here.
21 : : */
22 : : #include <io.h>
23 : : #endif
24 : :
25 : : #include "postgres_fe.h"
26 : :
27 : : #ifdef WIN32
28 : : #include "win32.h"
29 : : #else
30 : : #include <unistd.h>
31 : : #endif
32 : :
33 : : #include <fcntl.h>
34 : : #include <limits.h>
35 : : #include <sys/stat.h>
36 : :
37 : : #include "libpq-fe.h"
38 : : #include "libpq-int.h"
39 : : #include "libpq/libpq-fs.h" /* must come after sys/stat.h */
40 : : #include "port/pg_bswap.h"
41 : :
42 : : #define LO_BUFSIZE 8192
43 : :
44 : : static int lo_initialize(PGconn *conn);
45 : : static Oid lo_import_internal(PGconn *conn, const char *filename, Oid oid);
46 : :
47 : : /*
48 : : * lo_open
49 : : * opens an existing large object
50 : : *
51 : : * returns the file descriptor for use in later lo_* calls
52 : : * return -1 upon failure.
53 : : */
54 : : int
55 : 98 : lo_open(PGconn *conn, Oid lobjId, int mode)
56 : : {
57 : : int fd;
58 : : int result_len;
59 : : PQArgBlock argv[2];
60 : : PGresult *res;
61 : :
62 [ - + ]: 98 : if (lo_initialize(conn) < 0)
63 : 0 : return -1;
64 : :
65 : 98 : argv[0].isint = 1;
66 : 98 : argv[0].len = 4;
67 : 98 : argv[0].u.integer = lobjId;
68 : :
69 : 98 : argv[1].isint = 1;
70 : 98 : argv[1].len = 4;
71 : 98 : argv[1].u.integer = mode;
72 : :
73 : 98 : res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
74 [ + - ]: 98 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
75 : : {
76 : 98 : PQclear(res);
77 : 98 : return fd;
78 : : }
79 : : else
80 : : {
81 : 0 : PQclear(res);
82 : 0 : return -1;
83 : : }
84 : : }
85 : :
86 : : /*
87 : : * lo_close
88 : : * closes an existing large object
89 : : *
90 : : * returns 0 upon success
91 : : * returns -1 upon failure.
92 : : */
93 : : int
94 : 98 : lo_close(PGconn *conn, int fd)
95 : : {
96 : : PQArgBlock argv[1];
97 : : PGresult *res;
98 : : int retval;
99 : : int result_len;
100 : :
101 [ - + ]: 98 : if (lo_initialize(conn) < 0)
102 : 0 : return -1;
103 : :
104 : 98 : argv[0].isint = 1;
105 : 98 : argv[0].len = 4;
106 : 98 : argv[0].u.integer = fd;
107 : 98 : res = PQfn(conn, conn->lobjfuncs->fn_lo_close,
108 : : &retval, &result_len, 1, argv, 1);
109 [ + - ]: 98 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
110 : : {
111 : 98 : PQclear(res);
112 : 98 : return retval;
113 : : }
114 : : else
115 : : {
116 : 0 : PQclear(res);
117 : 0 : return -1;
118 : : }
119 : : }
120 : :
121 : : /*
122 : : * lo_truncate
123 : : * truncates an existing large object to the given size
124 : : *
125 : : * returns 0 upon success
126 : : * returns -1 upon failure
127 : : */
128 : : int
129 : 0 : lo_truncate(PGconn *conn, int fd, size_t len)
130 : : {
131 : : PQArgBlock argv[2];
132 : : PGresult *res;
133 : : int retval;
134 : : int result_len;
135 : :
136 [ # # ]: 0 : if (lo_initialize(conn) < 0)
137 : 0 : return -1;
138 : :
139 : : /* Must check this on-the-fly because it's not there pre-8.3 */
140 [ # # ]: 0 : if (conn->lobjfuncs->fn_lo_truncate == 0)
141 : : {
142 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
143 : : "lo_truncate");
144 : 0 : return -1;
145 : : }
146 : :
147 : : /*
148 : : * Long ago, somebody thought it'd be a good idea to declare this function
149 : : * as taking size_t ... but the underlying backend function only accepts a
150 : : * signed int32 length. So throw error if the given value overflows
151 : : * int32. (A possible alternative is to automatically redirect the call
152 : : * to lo_truncate64; but if the caller wanted to rely on that backend
153 : : * function being available, he could have called lo_truncate64 for
154 : : * himself.)
155 : : */
156 [ # # ]: 0 : if (len > (size_t) INT_MAX)
157 : : {
158 : 0 : libpq_append_conn_error(conn, "argument of lo_truncate exceeds integer range");
159 : 0 : return -1;
160 : : }
161 : :
162 : 0 : argv[0].isint = 1;
163 : 0 : argv[0].len = 4;
164 : 0 : argv[0].u.integer = fd;
165 : :
166 : 0 : argv[1].isint = 1;
167 : 0 : argv[1].len = 4;
168 : 0 : argv[1].u.integer = (int) len;
169 : :
170 : 0 : res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate,
171 : : &retval, &result_len, 1, argv, 2);
172 : :
173 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
174 : : {
175 : 0 : PQclear(res);
176 : 0 : return retval;
177 : : }
178 : : else
179 : : {
180 : 0 : PQclear(res);
181 : 0 : return -1;
182 : : }
183 : : }
184 : :
185 : : /*
186 : : * lo_truncate64
187 : : * truncates an existing large object to the given size
188 : : *
189 : : * returns 0 upon success
190 : : * returns -1 upon failure
191 : : */
192 : : int
193 : 0 : lo_truncate64(PGconn *conn, int fd, int64_t len)
194 : : {
195 : : PQArgBlock argv[2];
196 : : PGresult *res;
197 : : int retval;
198 : : int result_len;
199 : :
200 [ # # ]: 0 : if (lo_initialize(conn) < 0)
201 : 0 : return -1;
202 : :
203 [ # # ]: 0 : if (conn->lobjfuncs->fn_lo_truncate64 == 0)
204 : : {
205 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
206 : : "lo_truncate64");
207 : 0 : return -1;
208 : : }
209 : :
210 : 0 : argv[0].isint = 1;
211 : 0 : argv[0].len = 4;
212 : 0 : argv[0].u.integer = fd;
213 : :
214 : 0 : len = pg_hton64(len);
215 : 0 : argv[1].isint = 0;
216 : 0 : argv[1].len = 8;
217 : 0 : argv[1].u.ptr = (int *) &len;
218 : :
219 : 0 : res = PQfn(conn, conn->lobjfuncs->fn_lo_truncate64,
220 : : &retval, &result_len, 1, argv, 2);
221 : :
222 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
223 : : {
224 : 0 : PQclear(res);
225 : 0 : return retval;
226 : : }
227 : : else
228 : : {
229 : 0 : PQclear(res);
230 : 0 : return -1;
231 : : }
232 : : }
233 : :
234 : : /*
235 : : * lo_read
236 : : * read len bytes of the large object into buf
237 : : *
238 : : * returns the number of bytes read, or -1 on failure.
239 : : * the CALLER must have allocated enough space to hold the result returned
240 : : */
241 : :
242 : : int
243 : 465 : lo_read(PGconn *conn, int fd, char *buf, size_t len)
244 : : {
245 : : PQArgBlock argv[2];
246 : : PGresult *res;
247 : : int result_len;
248 : :
249 [ - + ]: 465 : if (lo_initialize(conn) < 0)
250 : 0 : return -1;
251 : :
252 : : /*
253 : : * Long ago, somebody thought it'd be a good idea to declare this function
254 : : * as taking size_t ... but the underlying backend function only accepts a
255 : : * signed int32 length. So throw error if the given value overflows
256 : : * int32.
257 : : */
258 [ - + ]: 465 : if (len > (size_t) INT_MAX)
259 : : {
260 : 0 : libpq_append_conn_error(conn, "argument of lo_read exceeds integer range");
261 : 0 : return -1;
262 : : }
263 : :
264 : 465 : argv[0].isint = 1;
265 : 465 : argv[0].len = 4;
266 : 465 : argv[0].u.integer = fd;
267 : :
268 : 465 : argv[1].isint = 1;
269 : 465 : argv[1].len = 4;
270 : 465 : argv[1].u.integer = (int) len;
271 : :
272 : 465 : res = PQnfn(conn, conn->lobjfuncs->fn_lo_read,
273 : : (void *) buf, len, &result_len, 0, argv, 2);
274 [ + - ]: 465 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
275 : : {
276 : 465 : PQclear(res);
277 : 465 : return result_len;
278 : : }
279 : : else
280 : : {
281 : 0 : PQclear(res);
282 : 0 : return -1;
283 : : }
284 : : }
285 : :
286 : : /*
287 : : * lo_write
288 : : * write len bytes of buf into the large object fd
289 : : *
290 : : * returns the number of bytes written, or -1 on failure.
291 : : */
292 : : int
293 : 657 : lo_write(PGconn *conn, int fd, const char *buf, size_t len)
294 : : {
295 : : PQArgBlock argv[2];
296 : : PGresult *res;
297 : : int result_len;
298 : : int retval;
299 : :
300 [ - + ]: 657 : if (lo_initialize(conn) < 0)
301 : 0 : return -1;
302 : :
303 : : /*
304 : : * Long ago, somebody thought it'd be a good idea to declare this function
305 : : * as taking size_t ... but the underlying backend function only accepts a
306 : : * signed int32 length. So throw error if the given value overflows
307 : : * int32.
308 : : */
309 [ - + ]: 657 : if (len > (size_t) INT_MAX)
310 : : {
311 : 0 : libpq_append_conn_error(conn, "argument of lo_write exceeds integer range");
312 : 0 : return -1;
313 : : }
314 : :
315 : 657 : argv[0].isint = 1;
316 : 657 : argv[0].len = 4;
317 : 657 : argv[0].u.integer = fd;
318 : :
319 : 657 : argv[1].isint = 0;
320 : 657 : argv[1].len = (int) len;
321 : 657 : argv[1].u.ptr = (int *) unconstify(char *, buf);
322 : :
323 : 657 : res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
324 : : &retval, &result_len, 1, argv, 2);
325 [ + - ]: 657 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
326 : : {
327 : 657 : PQclear(res);
328 : 657 : return retval;
329 : : }
330 : : else
331 : : {
332 : 0 : PQclear(res);
333 : 0 : return -1;
334 : : }
335 : : }
336 : :
337 : : /*
338 : : * lo_lseek
339 : : * change the current read or write location on a large object
340 : : */
341 : : int
342 : 0 : lo_lseek(PGconn *conn, int fd, int offset, int whence)
343 : : {
344 : : PQArgBlock argv[3];
345 : : PGresult *res;
346 : : int retval;
347 : : int result_len;
348 : :
349 [ # # ]: 0 : if (lo_initialize(conn) < 0)
350 : 0 : return -1;
351 : :
352 : 0 : argv[0].isint = 1;
353 : 0 : argv[0].len = 4;
354 : 0 : argv[0].u.integer = fd;
355 : :
356 : 0 : argv[1].isint = 1;
357 : 0 : argv[1].len = 4;
358 : 0 : argv[1].u.integer = offset;
359 : :
360 : 0 : argv[2].isint = 1;
361 : 0 : argv[2].len = 4;
362 : 0 : argv[2].u.integer = whence;
363 : :
364 : 0 : res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
365 : : &retval, &result_len, 1, argv, 3);
366 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
367 : : {
368 : 0 : PQclear(res);
369 : 0 : return retval;
370 : : }
371 : : else
372 : : {
373 : 0 : PQclear(res);
374 : 0 : return -1;
375 : : }
376 : : }
377 : :
378 : : /*
379 : : * lo_lseek64
380 : : * change the current read or write location on a large object
381 : : */
382 : : int64_t
383 : 0 : lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
384 : : {
385 : : PQArgBlock argv[3];
386 : : PGresult *res;
387 : : int64 retval;
388 : : int result_len;
389 : :
390 [ # # ]: 0 : if (lo_initialize(conn) < 0)
391 : 0 : return -1;
392 : :
393 [ # # ]: 0 : if (conn->lobjfuncs->fn_lo_lseek64 == 0)
394 : : {
395 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
396 : : "lo_lseek64");
397 : 0 : return -1;
398 : : }
399 : :
400 : 0 : argv[0].isint = 1;
401 : 0 : argv[0].len = 4;
402 : 0 : argv[0].u.integer = fd;
403 : :
404 : 0 : offset = pg_hton64(offset);
405 : 0 : argv[1].isint = 0;
406 : 0 : argv[1].len = 8;
407 : 0 : argv[1].u.ptr = (int *) &offset;
408 : :
409 : 0 : argv[2].isint = 1;
410 : 0 : argv[2].len = 4;
411 : 0 : argv[2].u.integer = whence;
412 : :
413 : 0 : res = PQnfn(conn, conn->lobjfuncs->fn_lo_lseek64,
414 : : (void *) &retval, sizeof(retval), &result_len, 0, argv, 3);
415 [ # # # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
416 : : {
417 : 0 : PQclear(res);
418 : 0 : return pg_ntoh64(retval);
419 : : }
420 : : else
421 : : {
422 : 0 : PQclear(res);
423 : 0 : return -1;
424 : : }
425 : : }
426 : :
427 : : /*
428 : : * lo_creat
429 : : * create a new large object
430 : : * the mode is ignored (once upon a time it had a use)
431 : : *
432 : : * returns the oid of the large object created or
433 : : * InvalidOid upon failure
434 : : */
435 : : Oid
436 : 9 : lo_creat(PGconn *conn, int mode)
437 : : {
438 : : PQArgBlock argv[1];
439 : : PGresult *res;
440 : : int retval;
441 : : int result_len;
442 : :
443 [ - + ]: 9 : if (lo_initialize(conn) < 0)
444 : 0 : return InvalidOid;
445 : :
446 : 9 : argv[0].isint = 1;
447 : 9 : argv[0].len = 4;
448 : 9 : argv[0].u.integer = mode;
449 : 9 : res = PQfn(conn, conn->lobjfuncs->fn_lo_creat,
450 : : &retval, &result_len, 1, argv, 1);
451 [ + - ]: 9 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
452 : : {
453 : 9 : PQclear(res);
454 : 9 : return (Oid) retval;
455 : : }
456 : : else
457 : : {
458 : 0 : PQclear(res);
459 : 0 : return InvalidOid;
460 : : }
461 : : }
462 : :
463 : : /*
464 : : * lo_create
465 : : * create a new large object
466 : : * if lobjId isn't InvalidOid, it specifies the OID to (attempt to) create
467 : : *
468 : : * returns the oid of the large object created or
469 : : * InvalidOid upon failure
470 : : */
471 : : Oid
472 : 0 : lo_create(PGconn *conn, Oid lobjId)
473 : : {
474 : : PQArgBlock argv[1];
475 : : PGresult *res;
476 : : int retval;
477 : : int result_len;
478 : :
479 [ # # ]: 0 : if (lo_initialize(conn) < 0)
480 : 0 : return InvalidOid;
481 : :
482 : : /* Must check this on-the-fly because it's not there pre-8.1 */
483 [ # # ]: 0 : if (conn->lobjfuncs->fn_lo_create == 0)
484 : : {
485 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
486 : : "lo_create");
487 : 0 : return InvalidOid;
488 : : }
489 : :
490 : 0 : argv[0].isint = 1;
491 : 0 : argv[0].len = 4;
492 : 0 : argv[0].u.integer = lobjId;
493 : 0 : res = PQfn(conn, conn->lobjfuncs->fn_lo_create,
494 : : &retval, &result_len, 1, argv, 1);
495 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
496 : : {
497 : 0 : PQclear(res);
498 : 0 : return (Oid) retval;
499 : : }
500 : : else
501 : : {
502 : 0 : PQclear(res);
503 : 0 : return InvalidOid;
504 : : }
505 : : }
506 : :
507 : :
508 : : /*
509 : : * lo_tell
510 : : * returns the current seek location of the large object
511 : : */
512 : : int
513 : 0 : lo_tell(PGconn *conn, int fd)
514 : : {
515 : : int retval;
516 : : PQArgBlock argv[1];
517 : : PGresult *res;
518 : : int result_len;
519 : :
520 [ # # ]: 0 : if (lo_initialize(conn) < 0)
521 : 0 : return -1;
522 : :
523 : 0 : argv[0].isint = 1;
524 : 0 : argv[0].len = 4;
525 : 0 : argv[0].u.integer = fd;
526 : :
527 : 0 : res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
528 : : &retval, &result_len, 1, argv, 1);
529 [ # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
530 : : {
531 : 0 : PQclear(res);
532 : 0 : return retval;
533 : : }
534 : : else
535 : : {
536 : 0 : PQclear(res);
537 : 0 : return -1;
538 : : }
539 : : }
540 : :
541 : : /*
542 : : * lo_tell64
543 : : * returns the current seek location of the large object
544 : : */
545 : : int64_t
546 : 0 : lo_tell64(PGconn *conn, int fd)
547 : : {
548 : : int64 retval;
549 : : PQArgBlock argv[1];
550 : : PGresult *res;
551 : : int result_len;
552 : :
553 [ # # ]: 0 : if (lo_initialize(conn) < 0)
554 : 0 : return -1;
555 : :
556 [ # # ]: 0 : if (conn->lobjfuncs->fn_lo_tell64 == 0)
557 : : {
558 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
559 : : "lo_tell64");
560 : 0 : return -1;
561 : : }
562 : :
563 : 0 : argv[0].isint = 1;
564 : 0 : argv[0].len = 4;
565 : 0 : argv[0].u.integer = fd;
566 : :
567 : 0 : res = PQnfn(conn, conn->lobjfuncs->fn_lo_tell64,
568 : : (void *) &retval, sizeof(retval), &result_len, 0, argv, 1);
569 [ # # # # ]: 0 : if (PQresultStatus(res) == PGRES_COMMAND_OK && result_len == 8)
570 : : {
571 : 0 : PQclear(res);
572 : 0 : return pg_ntoh64(retval);
573 : : }
574 : : else
575 : : {
576 : 0 : PQclear(res);
577 : 0 : return -1;
578 : : }
579 : : }
580 : :
581 : : /*
582 : : * lo_unlink
583 : : * delete a file
584 : : */
585 : :
586 : : int
587 : 16 : lo_unlink(PGconn *conn, Oid lobjId)
588 : : {
589 : : PQArgBlock argv[1];
590 : : PGresult *res;
591 : : int result_len;
592 : : int retval;
593 : :
594 [ - + ]: 16 : if (lo_initialize(conn) < 0)
595 : 0 : return -1;
596 : :
597 : 16 : argv[0].isint = 1;
598 : 16 : argv[0].len = 4;
599 : 16 : argv[0].u.integer = lobjId;
600 : :
601 : 16 : res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
602 : : &retval, &result_len, 1, argv, 1);
603 [ + - ]: 16 : if (PQresultStatus(res) == PGRES_COMMAND_OK)
604 : : {
605 : 16 : PQclear(res);
606 : 16 : return retval;
607 : : }
608 : : else
609 : : {
610 : 0 : PQclear(res);
611 : 0 : return -1;
612 : : }
613 : : }
614 : :
615 : : /*
616 : : * lo_import -
617 : : * imports a file as an (inversion) large object.
618 : : *
619 : : * returns the oid of that object upon success,
620 : : * returns InvalidOid upon failure
621 : : */
622 : :
623 : : Oid
624 : 9 : lo_import(PGconn *conn, const char *filename)
625 : : {
626 : 9 : return lo_import_internal(conn, filename, InvalidOid);
627 : : }
628 : :
629 : : /*
630 : : * lo_import_with_oid -
631 : : * imports a file as an (inversion) large object.
632 : : * large object id can be specified.
633 : : *
634 : : * returns the oid of that object upon success,
635 : : * returns InvalidOid upon failure
636 : : */
637 : :
638 : : Oid
639 : 0 : lo_import_with_oid(PGconn *conn, const char *filename, Oid lobjId)
640 : : {
641 : 0 : return lo_import_internal(conn, filename, lobjId);
642 : : }
643 : :
644 : : static Oid
645 : 9 : lo_import_internal(PGconn *conn, const char *filename, Oid oid)
646 : : {
647 : : int fd;
648 : : ssize_t nbytes;
649 : : int tmp;
650 : : char buf[LO_BUFSIZE];
651 : : Oid lobjOid;
652 : : int lobj;
653 : : char sebuf[PG_STRERROR_R_BUFLEN];
654 : :
655 [ - + ]: 9 : if (conn == NULL)
656 : 0 : return InvalidOid;
657 : :
658 : : /* Since this is the beginning of a query cycle, reset the error state */
659 : 9 : pqClearConnErrorState(conn);
660 : :
661 : : /*
662 : : * open the file to be read in
663 : : */
664 : 9 : fd = open(filename, O_RDONLY | PG_BINARY, 0666);
665 [ - + ]: 9 : if (fd < 0)
666 : : { /* error */
667 : 0 : libpq_append_conn_error(conn, "could not open file \"%s\": %s",
668 : 0 : filename, strerror_r(errno, sebuf, sizeof(sebuf)));
669 : 0 : return InvalidOid;
670 : : }
671 : :
672 : : /*
673 : : * create an inversion object
674 : : */
675 [ + - ]: 9 : if (oid == InvalidOid)
676 : 9 : lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
677 : : else
678 : 0 : lobjOid = lo_create(conn, oid);
679 : :
680 [ - + ]: 9 : if (lobjOid == InvalidOid)
681 : : {
682 : : /* we assume lo_create() already set a suitable error message */
683 : 0 : (void) close(fd);
684 : 0 : return InvalidOid;
685 : : }
686 : :
687 : 9 : lobj = lo_open(conn, lobjOid, INV_WRITE);
688 [ - + ]: 9 : if (lobj == -1)
689 : : {
690 : : /* we assume lo_open() already set a suitable error message */
691 : 0 : (void) close(fd);
692 : 0 : return InvalidOid;
693 : : }
694 : :
695 : : /*
696 : : * read in from the file and write to the large object
697 : : */
698 [ + + ]: 666 : while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0)
699 : : {
700 : 657 : tmp = lo_write(conn, lobj, buf, nbytes);
701 [ - + ]: 657 : if (tmp != nbytes)
702 : : {
703 : : /*
704 : : * If lo_write() failed, we are now in an aborted transaction so
705 : : * there's no need for lo_close(); furthermore, if we tried it
706 : : * we'd overwrite the useful error result with a useless one. So
707 : : * just nail the doors shut and get out of town.
708 : : */
709 : 0 : (void) close(fd);
710 : 0 : return InvalidOid;
711 : : }
712 : : }
713 : :
714 [ - + ]: 9 : if (nbytes < 0)
715 : : {
716 : : /* We must do lo_close before setting the errorMessage */
717 : 0 : int save_errno = errno;
718 : :
719 : 0 : (void) lo_close(conn, lobj);
720 : 0 : (void) close(fd);
721 : : /* deliberately overwrite any error from lo_close */
722 : 0 : pqClearConnErrorState(conn);
723 : 0 : libpq_append_conn_error(conn, "could not read from file \"%s\": %s",
724 : : filename,
725 : : strerror_r(save_errno, sebuf, sizeof(sebuf)));
726 : 0 : return InvalidOid;
727 : : }
728 : :
729 : 9 : (void) close(fd);
730 : :
731 [ - + ]: 9 : if (lo_close(conn, lobj) != 0)
732 : : {
733 : : /* we assume lo_close() already set a suitable error message */
734 : 0 : return InvalidOid;
735 : : }
736 : :
737 : 9 : return lobjOid;
738 : : }
739 : :
740 : : /*
741 : : * lo_export -
742 : : * exports an (inversion) large object.
743 : : * returns -1 upon failure, 1 if OK
744 : : */
745 : : int
746 : 4 : lo_export(PGconn *conn, Oid lobjId, const char *filename)
747 : : {
748 : 4 : int result = 1;
749 : : int fd;
750 : : int nbytes;
751 : : char buf[LO_BUFSIZE];
752 : : int lobj;
753 : : char sebuf[PG_STRERROR_R_BUFLEN];
754 : :
755 : : /*
756 : : * open the large object.
757 : : */
758 : 4 : lobj = lo_open(conn, lobjId, INV_READ);
759 [ - + ]: 4 : if (lobj == -1)
760 : : {
761 : : /* we assume lo_open() already set a suitable error message */
762 : 0 : return -1;
763 : : }
764 : :
765 : : /*
766 : : * create the file to be written to
767 : : */
768 : 4 : fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
769 [ - + ]: 4 : if (fd < 0)
770 : : {
771 : : /* We must do lo_close before setting the errorMessage */
772 : 0 : int save_errno = errno;
773 : :
774 : 0 : (void) lo_close(conn, lobj);
775 : : /* deliberately overwrite any error from lo_close */
776 : 0 : pqClearConnErrorState(conn);
777 : 0 : libpq_append_conn_error(conn, "could not open file \"%s\": %s",
778 : : filename,
779 : : strerror_r(save_errno, sebuf, sizeof(sebuf)));
780 : 0 : return -1;
781 : : }
782 : :
783 : : /*
784 : : * read in from the large object and write to the file
785 : : */
786 [ + + ]: 332 : while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0)
787 : : {
788 : : ssize_t tmp;
789 : :
790 : 328 : tmp = write(fd, buf, nbytes);
791 [ - + ]: 328 : if (tmp != nbytes)
792 : : {
793 : : /* We must do lo_close before setting the errorMessage */
794 : 0 : int save_errno = errno;
795 : :
796 : 0 : (void) lo_close(conn, lobj);
797 : 0 : (void) close(fd);
798 : : /* deliberately overwrite any error from lo_close */
799 : 0 : pqClearConnErrorState(conn);
800 : 0 : libpq_append_conn_error(conn, "could not write to file \"%s\": %s",
801 : : filename,
802 : : strerror_r(save_errno, sebuf, sizeof(sebuf)));
803 : 0 : return -1;
804 : : }
805 : : }
806 : :
807 : : /*
808 : : * If lo_read() failed, we are now in an aborted transaction so there's no
809 : : * need for lo_close(); furthermore, if we tried it we'd overwrite the
810 : : * useful error result with a useless one. So skip lo_close() if we got a
811 : : * failure result.
812 : : */
813 [ + - - + ]: 8 : if (nbytes < 0 ||
814 : 4 : lo_close(conn, lobj) != 0)
815 : : {
816 : : /* assume lo_read() or lo_close() left a suitable error message */
817 : 0 : result = -1;
818 : : }
819 : :
820 : : /* if we already failed, don't overwrite that msg with a close error */
821 [ - + - - ]: 4 : if (close(fd) != 0 && result >= 0)
822 : : {
823 : 0 : libpq_append_conn_error(conn, "could not write to file \"%s\": %s",
824 : 0 : filename, strerror_r(errno, sebuf, sizeof(sebuf)));
825 : 0 : result = -1;
826 : : }
827 : :
828 : 4 : return result;
829 : : }
830 : :
831 : :
832 : : /*
833 : : * lo_initialize
834 : : *
835 : : * Initialize for a new large-object operation on an existing connection.
836 : : * Return 0 if OK, -1 on failure.
837 : : *
838 : : * If we haven't previously done so, we collect the function OIDs from
839 : : * pg_proc for all functions that are required for large object operations.
840 : : */
841 : : static int
842 : 1343 : lo_initialize(PGconn *conn)
843 : : {
844 : : PGresult *res;
845 : : PGlobjfuncs *lobjfuncs;
846 : : int n;
847 : : const char *query;
848 : : const char *fname;
849 : : Oid foid;
850 : :
851 : : /* Nothing we can do with no connection */
852 [ - + ]: 1343 : if (conn == NULL)
853 : 0 : return -1;
854 : :
855 : : /* Since this is the beginning of a query cycle, reset the error state */
856 : 1343 : pqClearConnErrorState(conn);
857 : :
858 : : /* Nothing else to do if we already collected info */
859 [ + + ]: 1343 : if (conn->lobjfuncs != NULL)
860 : 1293 : return 0;
861 : :
862 : : /*
863 : : * Allocate the structure to hold the function OIDs. We don't store it
864 : : * into the PGconn until it's successfully filled.
865 : : */
866 : 50 : lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
867 [ - + ]: 50 : if (lobjfuncs == NULL)
868 : : {
869 : 0 : libpq_append_conn_error(conn, "out of memory");
870 : 0 : return -1;
871 : : }
872 [ + - - + : 50 : MemSet(lobjfuncs, 0, sizeof(PGlobjfuncs));
- - - - -
- ]
873 : :
874 : : /*
875 : : * Execute the query to get all the functions at once. (Not all of them
876 : : * may exist in older server versions.)
877 : : */
878 : 50 : query = "select proname, oid from pg_catalog.pg_proc "
879 : : "where proname in ("
880 : : "'lo_open', "
881 : : "'lo_close', "
882 : : "'lo_creat', "
883 : : "'lo_create', "
884 : : "'lo_unlink', "
885 : : "'lo_lseek', "
886 : : "'lo_lseek64', "
887 : : "'lo_tell', "
888 : : "'lo_tell64', "
889 : : "'lo_truncate', "
890 : : "'lo_truncate64', "
891 : : "'loread', "
892 : : "'lowrite') "
893 : : "and pronamespace = (select oid from pg_catalog.pg_namespace "
894 : : "where nspname = 'pg_catalog')";
895 : :
896 : 50 : res = PQexec(conn, query);
897 [ - + ]: 50 : if (res == NULL)
898 : : {
899 : 0 : free(lobjfuncs);
900 : 0 : return -1;
901 : : }
902 : :
903 [ - + ]: 50 : if (res->resultStatus != PGRES_TUPLES_OK)
904 : : {
905 : 0 : free(lobjfuncs);
906 : 0 : PQclear(res);
907 : 0 : libpq_append_conn_error(conn, "query to initialize large object functions did not return data");
908 : 0 : return -1;
909 : : }
910 : :
911 : : /*
912 : : * Examine the result and put the OID's into the struct
913 : : */
914 [ + + ]: 700 : for (n = 0; n < PQntuples(res); n++)
915 : : {
916 : 650 : fname = PQgetvalue(res, n, 0);
917 : 650 : foid = (Oid) atoi(PQgetvalue(res, n, 1));
918 [ + + ]: 650 : if (strcmp(fname, "lo_open") == 0)
919 : 50 : lobjfuncs->fn_lo_open = foid;
920 [ + + ]: 600 : else if (strcmp(fname, "lo_close") == 0)
921 : 50 : lobjfuncs->fn_lo_close = foid;
922 [ + + ]: 550 : else if (strcmp(fname, "lo_creat") == 0)
923 : 50 : lobjfuncs->fn_lo_creat = foid;
924 [ + + ]: 500 : else if (strcmp(fname, "lo_create") == 0)
925 : 50 : lobjfuncs->fn_lo_create = foid;
926 [ + + ]: 450 : else if (strcmp(fname, "lo_unlink") == 0)
927 : 50 : lobjfuncs->fn_lo_unlink = foid;
928 [ + + ]: 400 : else if (strcmp(fname, "lo_lseek") == 0)
929 : 50 : lobjfuncs->fn_lo_lseek = foid;
930 [ + + ]: 350 : else if (strcmp(fname, "lo_lseek64") == 0)
931 : 50 : lobjfuncs->fn_lo_lseek64 = foid;
932 [ + + ]: 300 : else if (strcmp(fname, "lo_tell") == 0)
933 : 50 : lobjfuncs->fn_lo_tell = foid;
934 [ + + ]: 250 : else if (strcmp(fname, "lo_tell64") == 0)
935 : 50 : lobjfuncs->fn_lo_tell64 = foid;
936 [ + + ]: 200 : else if (strcmp(fname, "lo_truncate") == 0)
937 : 50 : lobjfuncs->fn_lo_truncate = foid;
938 [ + + ]: 150 : else if (strcmp(fname, "lo_truncate64") == 0)
939 : 50 : lobjfuncs->fn_lo_truncate64 = foid;
940 [ + + ]: 100 : else if (strcmp(fname, "loread") == 0)
941 : 50 : lobjfuncs->fn_lo_read = foid;
942 [ + - ]: 50 : else if (strcmp(fname, "lowrite") == 0)
943 : 50 : lobjfuncs->fn_lo_write = foid;
944 : : }
945 : :
946 : 50 : PQclear(res);
947 : :
948 : : /*
949 : : * Finally check that we got all required large object interface functions
950 : : * (ones that have been added later than the stone age are instead checked
951 : : * only if used)
952 : : */
953 [ - + ]: 50 : if (lobjfuncs->fn_lo_open == 0)
954 : : {
955 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
956 : : "lo_open");
957 : 0 : free(lobjfuncs);
958 : 0 : return -1;
959 : : }
960 [ - + ]: 50 : if (lobjfuncs->fn_lo_close == 0)
961 : : {
962 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
963 : : "lo_close");
964 : 0 : free(lobjfuncs);
965 : 0 : return -1;
966 : : }
967 [ - + ]: 50 : if (lobjfuncs->fn_lo_creat == 0)
968 : : {
969 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
970 : : "lo_creat");
971 : 0 : free(lobjfuncs);
972 : 0 : return -1;
973 : : }
974 [ - + ]: 50 : if (lobjfuncs->fn_lo_unlink == 0)
975 : : {
976 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
977 : : "lo_unlink");
978 : 0 : free(lobjfuncs);
979 : 0 : return -1;
980 : : }
981 [ - + ]: 50 : if (lobjfuncs->fn_lo_lseek == 0)
982 : : {
983 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
984 : : "lo_lseek");
985 : 0 : free(lobjfuncs);
986 : 0 : return -1;
987 : : }
988 [ - + ]: 50 : if (lobjfuncs->fn_lo_tell == 0)
989 : : {
990 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
991 : : "lo_tell");
992 : 0 : free(lobjfuncs);
993 : 0 : return -1;
994 : : }
995 [ - + ]: 50 : if (lobjfuncs->fn_lo_read == 0)
996 : : {
997 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
998 : : "loread");
999 : 0 : free(lobjfuncs);
1000 : 0 : return -1;
1001 : : }
1002 [ - + ]: 50 : if (lobjfuncs->fn_lo_write == 0)
1003 : : {
1004 : 0 : libpq_append_conn_error(conn, "cannot determine OID of function %s",
1005 : : "lowrite");
1006 : 0 : free(lobjfuncs);
1007 : 0 : return -1;
1008 : : }
1009 : :
1010 : : /*
1011 : : * Put the structure into the connection control
1012 : : */
1013 : 50 : conn->lobjfuncs = lobjfuncs;
1014 : 50 : return 0;
1015 : : }
|