LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-lobj.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 149 374 39.8 %
Date: 2024-04-25 04:11:22 Functions: 10 20 50.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14