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