]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/fe-lobj.c
30c77e98a539265d354cda0c806176d31eeb2205
[postgresql] / src / interfaces / libpq / fe-lobj.c
1 /*-------------------------------------------------------------------------
2  *
3  * fe-lobj.c
4  *        Front-end large object interface
5  *
6  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/interfaces/libpq/fe-lobj.c,v 1.52 2004/12/31 22:03:50 pgsql Exp $
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 <sys/stat.h>
35 #include <errno.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
41 #define LO_BUFSIZE                8192
42
43 static int      lo_initialize(PGconn *conn);
44
45
46 /*
47  * lo_open
48  *        opens an existing large object
49  *
50  * returns the file descriptor for use in later lo_* calls
51  * return -1 upon failure.
52  */
53 int
54 lo_open(PGconn *conn, Oid lobjId, int mode)
55 {
56         int                     fd;
57         int                     result_len;
58         PQArgBlock      argv[2];
59         PGresult   *res;
60
61         argv[0].isint = 1;
62         argv[0].len = 4;
63         argv[0].u.integer = lobjId;
64
65         argv[1].isint = 1;
66         argv[1].len = 4;
67         argv[1].u.integer = mode;
68
69         if (conn->lobjfuncs == NULL)
70         {
71                 if (lo_initialize(conn) < 0)
72                         return -1;
73         }
74
75         res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
76         if (PQresultStatus(res) == PGRES_COMMAND_OK)
77         {
78                 PQclear(res);
79
80                 /* have to do this to reset offset in shared fd cache */
81                 /* but only if fd is valid */
82                 if (fd >= 0 && lo_lseek(conn, fd, 0L, SEEK_SET) < 0)
83                         return -1;
84                 return fd;
85         }
86         else
87         {
88                 PQclear(res);
89                 return -1;
90         }
91 }
92
93 /*
94  * lo_close
95  *        closes an existing large object
96  *
97  * returns 0 upon success
98  * returns -1 upon failure.
99  */
100 int
101 lo_close(PGconn *conn, int fd)
102 {
103         PQArgBlock      argv[1];
104         PGresult   *res;
105         int                     retval;
106         int                     result_len;
107
108         if (conn->lobjfuncs == NULL)
109         {
110                 if (lo_initialize(conn) < 0)
111                         return -1;
112         }
113
114         argv[0].isint = 1;
115         argv[0].len = 4;
116         argv[0].u.integer = fd;
117         res = PQfn(conn, conn->lobjfuncs->fn_lo_close,
118                            &retval, &result_len, 1, argv, 1);
119         if (PQresultStatus(res) == PGRES_COMMAND_OK)
120         {
121                 PQclear(res);
122                 return retval;
123         }
124         else
125         {
126                 PQclear(res);
127                 return -1;
128         }
129 }
130
131 /*
132  * lo_read
133  *        read len bytes of the large object into buf
134  *
135  * returns the number of bytes read, or -1 on failure.
136  * the CALLER must have allocated enough space to hold the result returned
137  */
138
139 int
140 lo_read(PGconn *conn, int fd, char *buf, size_t len)
141 {
142         PQArgBlock      argv[2];
143         PGresult   *res;
144         int                     result_len;
145
146         if (conn->lobjfuncs == NULL)
147         {
148                 if (lo_initialize(conn) < 0)
149                         return -1;
150         }
151
152         argv[0].isint = 1;
153         argv[0].len = 4;
154         argv[0].u.integer = fd;
155
156         argv[1].isint = 1;
157         argv[1].len = 4;
158         argv[1].u.integer = len;
159
160         res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
161                            (int *) buf, &result_len, 0, argv, 2);
162         if (PQresultStatus(res) == PGRES_COMMAND_OK)
163         {
164                 PQclear(res);
165                 return result_len;
166         }
167         else
168         {
169                 PQclear(res);
170                 return -1;
171         }
172 }
173
174 /*
175  * lo_write
176  *        write len bytes of buf into the large object fd
177  *
178  * returns the number of bytes written, or -1 on failure.
179  */
180 int
181 lo_write(PGconn *conn, int fd, char *buf, size_t len)
182 {
183         PQArgBlock      argv[2];
184         PGresult   *res;
185         int                     result_len;
186         int                     retval;
187
188         if (conn->lobjfuncs == NULL)
189         {
190                 if (lo_initialize(conn) < 0)
191                         return -1;
192         }
193
194         if (len <= 0)
195                 return 0;
196
197         argv[0].isint = 1;
198         argv[0].len = 4;
199         argv[0].u.integer = fd;
200
201         argv[1].isint = 0;
202         argv[1].len = len;
203         argv[1].u.ptr = (int *) buf;
204
205         res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
206                            &retval, &result_len, 1, argv, 2);
207         if (PQresultStatus(res) == PGRES_COMMAND_OK)
208         {
209                 PQclear(res);
210                 return retval;
211         }
212         else
213         {
214                 PQclear(res);
215                 return -1;
216         }
217 }
218
219 /*
220  * lo_lseek
221  *        change the current read or write location on a large object
222  * currently, only L_SET is a legal value for whence
223  *
224  */
225
226 int
227 lo_lseek(PGconn *conn, int fd, int offset, int whence)
228 {
229         PQArgBlock      argv[3];
230         PGresult   *res;
231         int                     retval;
232         int                     result_len;
233
234         if (conn->lobjfuncs == NULL)
235         {
236                 if (lo_initialize(conn) < 0)
237                         return -1;
238         }
239
240         argv[0].isint = 1;
241         argv[0].len = 4;
242         argv[0].u.integer = fd;
243
244         argv[1].isint = 1;
245         argv[1].len = 4;
246         argv[1].u.integer = offset;
247
248         argv[2].isint = 1;
249         argv[2].len = 4;
250         argv[2].u.integer = whence;
251
252         res = PQfn(conn, conn->lobjfuncs->fn_lo_lseek,
253                            &retval, &result_len, 1, argv, 3);
254         if (PQresultStatus(res) == PGRES_COMMAND_OK)
255         {
256                 PQclear(res);
257                 return retval;
258         }
259         else
260         {
261                 PQclear(res);
262                 return -1;
263         }
264 }
265
266 /*
267  * lo_creat
268  *        create a new large object
269  * the mode is a bitmask describing different attributes of the new object
270  *
271  * returns the oid of the large object created or
272  * InvalidOid upon failure
273  */
274
275 Oid
276 lo_creat(PGconn *conn, int mode)
277 {
278         PQArgBlock      argv[1];
279         PGresult   *res;
280         int                     retval;
281         int                     result_len;
282
283         if (conn->lobjfuncs == NULL)
284         {
285                 if (lo_initialize(conn) < 0)
286                         return InvalidOid;
287         }
288
289         argv[0].isint = 1;
290         argv[0].len = 4;
291         argv[0].u.integer = mode;
292         res = PQfn(conn, conn->lobjfuncs->fn_lo_creat,
293                            &retval, &result_len, 1, argv, 1);
294         if (PQresultStatus(res) == PGRES_COMMAND_OK)
295         {
296                 PQclear(res);
297                 return (Oid) retval;
298         }
299         else
300         {
301                 PQclear(res);
302                 return InvalidOid;
303         }
304 }
305
306
307 /*
308  * lo_tell
309  *        returns the current seek location of the large object
310  *
311  */
312
313 int
314 lo_tell(PGconn *conn, int fd)
315 {
316         int                     retval;
317         PQArgBlock      argv[1];
318         PGresult   *res;
319         int                     result_len;
320
321         if (conn->lobjfuncs == NULL)
322         {
323                 if (lo_initialize(conn) < 0)
324                         return -1;
325         }
326
327         argv[0].isint = 1;
328         argv[0].len = 4;
329         argv[0].u.integer = fd;
330
331         res = PQfn(conn, conn->lobjfuncs->fn_lo_tell,
332                            &retval, &result_len, 1, argv, 1);
333         if (PQresultStatus(res) == PGRES_COMMAND_OK)
334         {
335                 PQclear(res);
336                 return retval;
337         }
338         else
339         {
340                 PQclear(res);
341                 return -1;
342         }
343 }
344
345 /*
346  * lo_unlink
347  *        delete a file
348  *
349  */
350
351 int
352 lo_unlink(PGconn *conn, Oid lobjId)
353 {
354         PQArgBlock      argv[1];
355         PGresult   *res;
356         int                     result_len;
357         int                     retval;
358
359         if (conn->lobjfuncs == NULL)
360         {
361                 if (lo_initialize(conn) < 0)
362                         return -1;
363         }
364
365         argv[0].isint = 1;
366         argv[0].len = 4;
367         argv[0].u.integer = lobjId;
368
369         res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
370                            &retval, &result_len, 1, argv, 1);
371         if (PQresultStatus(res) == PGRES_COMMAND_OK)
372         {
373                 PQclear(res);
374                 return retval;
375         }
376         else
377         {
378                 PQclear(res);
379                 return -1;
380         }
381 }
382
383 /*
384  * lo_import -
385  *        imports a file as an (inversion) large object.
386  *
387  * returns the oid of that object upon success,
388  * returns InvalidOid upon failure
389  */
390
391 Oid
392 lo_import(PGconn *conn, const char *filename)
393 {
394         int                     fd;
395         int                     nbytes,
396                                 tmp;
397         char            buf[LO_BUFSIZE];
398         Oid                     lobjOid;
399         int                     lobj;
400
401         /*
402          * open the file to be read in
403          */
404         fd = open(filename, O_RDONLY | PG_BINARY, 0666);
405         if (fd < 0)
406         {                                                       /* error */
407                 char            sebuf[256];
408
409                 printfPQExpBuffer(&conn->errorMessage,
410                                            libpq_gettext("could not open file \"%s\": %s\n"),
411                                           filename, pqStrerror(errno, sebuf, sizeof(sebuf)));
412                 return InvalidOid;
413         }
414
415         /*
416          * create an inversion "object"
417          */
418         lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
419         if (lobjOid == InvalidOid)
420         {
421                 printfPQExpBuffer(&conn->errorMessage,
422                 libpq_gettext("could not create large object for file \"%s\"\n"),
423                                                   filename);
424                 (void) close(fd);
425                 return InvalidOid;
426         }
427
428         lobj = lo_open(conn, lobjOid, INV_WRITE);
429         if (lobj == -1)
430         {
431                 printfPQExpBuffer(&conn->errorMessage,
432                                            libpq_gettext("could not open large object %u\n"),
433                                                   lobjOid);
434                 (void) close(fd);
435                 return InvalidOid;
436         }
437
438         /*
439          * read in from the Unix file and write to the inversion file
440          */
441         while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0)
442         {
443                 tmp = lo_write(conn, lobj, buf, nbytes);
444                 if (tmp < nbytes)
445                 {
446                         printfPQExpBuffer(&conn->errorMessage,
447                                           libpq_gettext("error while reading file \"%s\"\n"),
448                                                           filename);
449                         (void) close(fd);
450                         (void) lo_close(conn, lobj);
451                         return InvalidOid;
452                 }
453         }
454
455         (void) close(fd);
456         (void) lo_close(conn, lobj);
457
458         return lobjOid;
459 }
460
461 /*
462  * lo_export -
463  *        exports an (inversion) large object.
464  * returns -1 upon failure, 1 otherwise
465  */
466 int
467 lo_export(PGconn *conn, Oid lobjId, const char *filename)
468 {
469         int                     fd;
470         int                     nbytes,
471                                 tmp;
472         char            buf[LO_BUFSIZE];
473         int                     lobj;
474
475         /*
476          * open the large object.
477          */
478         lobj = lo_open(conn, lobjId, INV_READ);
479         if (lobj == -1)
480         {
481                 printfPQExpBuffer(&conn->errorMessage,
482                           libpq_gettext("could not open large object %u\n"), lobjId);
483                 return -1;
484         }
485
486         /*
487          * create the file to be written to
488          */
489         fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
490         if (fd < 0)
491         {                                                       /* error */
492                 char            sebuf[256];
493
494                 printfPQExpBuffer(&conn->errorMessage,
495                                            libpq_gettext("could not open file \"%s\": %s\n"),
496                                           filename, pqStrerror(errno, sebuf, sizeof(sebuf)));
497                 (void) lo_close(conn, lobj);
498                 return -1;
499         }
500
501         /*
502          * read in from the inversion file and write to the Unix file
503          */
504         while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0)
505         {
506                 tmp = write(fd, buf, nbytes);
507                 if (tmp < nbytes)
508                 {
509                         printfPQExpBuffer(&conn->errorMessage,
510                                    libpq_gettext("error while writing to file \"%s\"\n"),
511                                                           filename);
512                         (void) lo_close(conn, lobj);
513                         (void) close(fd);
514                         return -1;
515                 }
516         }
517
518         (void) lo_close(conn, lobj);
519
520         if (close(fd))
521         {
522                 printfPQExpBuffer(&conn->errorMessage,
523                                    libpq_gettext("error while writing to file \"%s\"\n"),
524                                                   filename);
525                 return -1;
526         }
527
528         return 1;
529 }
530
531
532 /*
533  * lo_initialize
534  *
535  * Initialize the large object interface for an existing connection.
536  * We ask the backend about the functions OID's in pg_proc for all
537  * functions that are required for large object operations.
538  */
539 static int
540 lo_initialize(PGconn *conn)
541 {
542         PGresult   *res;
543         PGlobjfuncs *lobjfuncs;
544         int                     n;
545         const char *query;
546         const char *fname;
547         Oid                     foid;
548
549         /*
550          * Allocate the structure to hold the functions OID's
551          */
552         lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
553         if (lobjfuncs == NULL)
554         {
555                 printfPQExpBuffer(&conn->errorMessage,
556                                                   libpq_gettext("out of memory\n"));
557                 return -1;
558         }
559         MemSet((char *) lobjfuncs, 0, sizeof(PGlobjfuncs));
560
561         /*
562          * Execute the query to get all the functions at once.  In 7.3 and
563          * later we need to be schema-safe.
564          */
565         if (conn->sversion >= 70300)
566                 query = "select proname, oid from pg_catalog.pg_proc "
567                         "where proname in ("
568                         "'lo_open', "
569                         "'lo_close', "
570                         "'lo_creat', "
571                         "'lo_unlink', "
572                         "'lo_lseek', "
573                         "'lo_tell', "
574                         "'loread', "
575                         "'lowrite') "
576                         "and pronamespace = (select oid from pg_catalog.pg_namespace "
577                         "where nspname = 'pg_catalog')";
578         else
579                 query = "select proname, oid from pg_proc "
580                         "where proname = 'lo_open' "
581                         "or proname = 'lo_close' "
582                         "or proname = 'lo_creat' "
583                         "or proname = 'lo_unlink' "
584                         "or proname = 'lo_lseek' "
585                         "or proname = 'lo_tell' "
586                         "or proname = 'loread' "
587                         "or proname = 'lowrite'";
588
589         res = PQexec(conn, query);
590         if (res == NULL)
591         {
592                 free(lobjfuncs);
593                 return -1;
594         }
595
596         if (res->resultStatus != PGRES_TUPLES_OK)
597         {
598                 free(lobjfuncs);
599                 PQclear(res);
600                 printfPQExpBuffer(&conn->errorMessage,
601                                                   libpq_gettext("query to initialize large object functions did not return data\n"));
602                 return -1;
603         }
604
605         /*
606          * Examine the result and put the OID's into the struct
607          */
608         for (n = 0; n < PQntuples(res); n++)
609         {
610                 fname = PQgetvalue(res, n, 0);
611                 foid = (Oid) atoi(PQgetvalue(res, n, 1));
612                 if (!strcmp(fname, "lo_open"))
613                         lobjfuncs->fn_lo_open = foid;
614                 else if (!strcmp(fname, "lo_close"))
615                         lobjfuncs->fn_lo_close = foid;
616                 else if (!strcmp(fname, "lo_creat"))
617                         lobjfuncs->fn_lo_creat = foid;
618                 else if (!strcmp(fname, "lo_unlink"))
619                         lobjfuncs->fn_lo_unlink = foid;
620                 else if (!strcmp(fname, "lo_lseek"))
621                         lobjfuncs->fn_lo_lseek = foid;
622                 else if (!strcmp(fname, "lo_tell"))
623                         lobjfuncs->fn_lo_tell = foid;
624                 else if (!strcmp(fname, "loread"))
625                         lobjfuncs->fn_lo_read = foid;
626                 else if (!strcmp(fname, "lowrite"))
627                         lobjfuncs->fn_lo_write = foid;
628         }
629
630         PQclear(res);
631
632         /*
633          * Finally check that we really got all large object interface
634          * functions.
635          */
636         if (lobjfuncs->fn_lo_open == 0)
637         {
638                 printfPQExpBuffer(&conn->errorMessage,
639                         libpq_gettext("cannot determine OID of function lo_open\n"));
640                 free(lobjfuncs);
641                 return -1;
642         }
643         if (lobjfuncs->fn_lo_close == 0)
644         {
645                 printfPQExpBuffer(&conn->errorMessage,
646                    libpq_gettext("cannot determine OID of function lo_close\n"));
647                 free(lobjfuncs);
648                 return -1;
649         }
650         if (lobjfuncs->fn_lo_creat == 0)
651         {
652                 printfPQExpBuffer(&conn->errorMessage,
653                    libpq_gettext("cannot determine OID of function lo_creat\n"));
654                 free(lobjfuncs);
655                 return -1;
656         }
657         if (lobjfuncs->fn_lo_unlink == 0)
658         {
659                 printfPQExpBuffer(&conn->errorMessage,
660                   libpq_gettext("cannot determine OID of function lo_unlink\n"));
661                 free(lobjfuncs);
662                 return -1;
663         }
664         if (lobjfuncs->fn_lo_lseek == 0)
665         {
666                 printfPQExpBuffer(&conn->errorMessage,
667                    libpq_gettext("cannot determine OID of function lo_lseek\n"));
668                 free(lobjfuncs);
669                 return -1;
670         }
671         if (lobjfuncs->fn_lo_tell == 0)
672         {
673                 printfPQExpBuffer(&conn->errorMessage,
674                         libpq_gettext("cannot determine OID of function lo_tell\n"));
675                 free(lobjfuncs);
676                 return -1;
677         }
678         if (lobjfuncs->fn_lo_read == 0)
679         {
680                 printfPQExpBuffer(&conn->errorMessage,
681                          libpq_gettext("cannot determine OID of function loread\n"));
682                 free(lobjfuncs);
683                 return -1;
684         }
685         if (lobjfuncs->fn_lo_write == 0)
686         {
687                 printfPQExpBuffer(&conn->errorMessage,
688                         libpq_gettext("cannot determine OID of function lowrite\n"));
689                 free(lobjfuncs);
690                 return -1;
691         }
692
693         /*
694          * Put the structure into the connection control
695          */
696         conn->lobjfuncs = lobjfuncs;
697         return 0;
698 }