LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-lobj.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 39.8 % 374 149
Test Date: 2026-03-12 01:15:13 Functions: 50.0 % 20 10
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-2026, PostgreSQL Global Development Group
       7              :  * Portions Copyright (c) 1994, Regents of the University of California
       8              :  *
       9              :  *
      10              :  * IDENTIFICATION
      11              :  *    src/interfaces/libpq/fe-lobj.c
      12              :  *
      13              :  *-------------------------------------------------------------------------
      14              :  */
      15              : 
      16              : #ifdef WIN32
      17              : /*
      18              :  *  As unlink/rename are #define'd in port.h (via postgres_fe.h), io.h
      19              :  *  must be included first on MS C.  Might as well do it for all WIN32's
      20              :  *  here.
      21              :  */
      22              : #include <io.h>
      23              : #endif
      24              : 
      25              : #include "postgres_fe.h"
      26              : 
      27              : #ifdef WIN32
      28              : #include "win32.h"
      29              : #else
      30              : #include <unistd.h>
      31              : #endif
      32              : 
      33              : #include <fcntl.h>
      34              : #include <limits.h>
      35              : #include <sys/stat.h>
      36              : 
      37              : #include "libpq-fe.h"
      38              : #include "libpq-int.h"
      39              : #include "libpq/libpq-fs.h"       /* must come after sys/stat.h */
      40              : #include "port/pg_bswap.h"
      41              : 
      42              : #define LO_BUFSIZE        8192
      43              : 
      44              : static int  lo_initialize(PGconn *conn);
      45              : static Oid  lo_import_internal(PGconn *conn, const char *filename, Oid oid);
      46              : static int64_t lo_hton64(int64_t host64);
      47              : static int64_t lo_ntoh64(int64_t 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           91 : 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           91 :     if (lo_initialize(conn) < 0)
      65            0 :         return -1;
      66              : 
      67           91 :     argv[0].isint = 1;
      68           91 :     argv[0].len = 4;
      69           91 :     argv[0].u.integer = lobjId;
      70              : 
      71           91 :     argv[1].isint = 1;
      72           91 :     argv[1].len = 4;
      73           91 :     argv[1].u.integer = mode;
      74              : 
      75           91 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_open, &fd, &result_len, 1, argv, 2);
      76           91 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
      77              :     {
      78           91 :         PQclear(res);
      79           91 :         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           91 : lo_close(PGconn *conn, int fd)
      97              : {
      98              :     PQArgBlock  argv[1];
      99              :     PGresult   *res;
     100              :     int         retval;
     101              :     int         result_len;
     102              : 
     103           91 :     if (lo_initialize(conn) < 0)
     104            0 :         return -1;
     105              : 
     106           91 :     argv[0].isint = 1;
     107           91 :     argv[0].len = 4;
     108           91 :     argv[0].u.integer = fd;
     109           91 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_close,
     110              :                &retval, &result_len, 1, argv, 1);
     111           91 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
     112              :     {
     113           91 :         PQclear(res);
     114           91 :         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, int64_t 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          376 : 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          376 :     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          376 :     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          376 :     argv[0].isint = 1;
     267          376 :     argv[0].len = 4;
     268          376 :     argv[0].u.integer = fd;
     269              : 
     270          376 :     argv[1].isint = 1;
     271          376 :     argv[1].len = 4;
     272          376 :     argv[1].u.integer = (int) len;
     273              : 
     274          376 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_read,
     275              :                (void *) buf, &result_len, 0, argv, 2);
     276          376 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
     277              :     {
     278          376 :         PQclear(res);
     279          376 :         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          493 : 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          493 :     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          493 :     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          493 :     argv[0].isint = 1;
     318          493 :     argv[0].len = 4;
     319          493 :     argv[0].u.integer = fd;
     320              : 
     321          493 :     argv[1].isint = 0;
     322          493 :     argv[1].len = (int) len;
     323          493 :     argv[1].u.ptr = (int *) unconstify(char *, buf);
     324              : 
     325          493 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_write,
     326              :                &retval, &result_len, 1, argv, 2);
     327          493 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
     328              :     {
     329          493 :         PQclear(res);
     330          493 :         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              : int64_t
     385            0 : lo_lseek64(PGconn *conn, int fd, int64_t offset, int whence)
     386              : {
     387              :     PQArgBlock  argv[3];
     388              :     PGresult   *res;
     389              :     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            7 : lo_creat(PGconn *conn, int mode)
     439              : {
     440              :     PQArgBlock  argv[1];
     441              :     PGresult   *res;
     442              :     int         retval;
     443              :     int         result_len;
     444              : 
     445            7 :     if (lo_initialize(conn) < 0)
     446            0 :         return InvalidOid;
     447              : 
     448            7 :     argv[0].isint = 1;
     449            7 :     argv[0].len = 4;
     450            7 :     argv[0].u.integer = mode;
     451            7 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_creat,
     452              :                &retval, &result_len, 1, argv, 1);
     453            7 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
     454              :     {
     455            7 :         PQclear(res);
     456            7 :         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              : int64_t
     548            0 : lo_tell64(PGconn *conn, int fd)
     549              : {
     550              :     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           12 : lo_unlink(PGconn *conn, Oid lobjId)
     590              : {
     591              :     PQArgBlock  argv[1];
     592              :     PGresult   *res;
     593              :     int         result_len;
     594              :     int         retval;
     595              : 
     596           12 :     if (lo_initialize(conn) < 0)
     597            0 :         return -1;
     598              : 
     599           12 :     argv[0].isint = 1;
     600           12 :     argv[0].len = 4;
     601           12 :     argv[0].u.integer = lobjId;
     602              : 
     603           12 :     res = PQfn(conn, conn->lobjfuncs->fn_lo_unlink,
     604              :                &retval, &result_len, 1, argv, 1);
     605           12 :     if (PQresultStatus(res) == PGRES_COMMAND_OK)
     606              :     {
     607           12 :         PQclear(res);
     608           12 :         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            7 : lo_import(PGconn *conn, const char *filename)
     627              : {
     628            7 :     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            7 : 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            7 :     if (conn == NULL)
     658            0 :         return InvalidOid;
     659              : 
     660              :     /* Since this is the beginning of a query cycle, reset the error state */
     661            7 :     pqClearConnErrorState(conn);
     662              : 
     663              :     /*
     664              :      * open the file to be read in
     665              :      */
     666            7 :     fd = open(filename, O_RDONLY | PG_BINARY, 0666);
     667            7 :     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            7 :     if (oid == InvalidOid)
     678            7 :         lobjOid = lo_creat(conn, INV_READ | INV_WRITE);
     679              :     else
     680            0 :         lobjOid = lo_create(conn, oid);
     681              : 
     682            7 :     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            7 :     lobj = lo_open(conn, lobjOid, INV_WRITE);
     690            7 :     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          500 :     while ((nbytes = read(fd, buf, LO_BUFSIZE)) > 0)
     701              :     {
     702          493 :         tmp = lo_write(conn, lobj, buf, nbytes);
     703          493 :         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            7 :     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            7 :     (void) close(fd);
     732              : 
     733            7 :     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            7 :     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            3 : lo_export(PGconn *conn, Oid lobjId, const char *filename)
     749              : {
     750            3 :     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            3 :     lobj = lo_open(conn, lobjId, INV_READ);
     762            3 :     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            3 :     fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY, 0666);
     772            3 :     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          249 :     while ((nbytes = lo_read(conn, lobj, buf, LO_BUFSIZE)) > 0)
     790              :     {
     791          246 :         tmp = write(fd, buf, nbytes);
     792          246 :         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            6 :     if (nbytes < 0 ||
     815            3 :         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            3 :     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            3 :     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         1070 : 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         1070 :     if (conn == NULL)
     854            0 :         return -1;
     855              : 
     856              :     /* Since this is the beginning of a query cycle, reset the error state */
     857         1070 :     pqClearConnErrorState(conn);
     858              : 
     859              :     /* Nothing else to do if we already collected info */
     860         1070 :     if (conn->lobjfuncs != NULL)
     861         1023 :         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           47 :     lobjfuncs = (PGlobjfuncs *) malloc(sizeof(PGlobjfuncs));
     868           47 :     if (lobjfuncs == NULL)
     869              :     {
     870            0 :         libpq_append_conn_error(conn, "out of memory");
     871            0 :         return -1;
     872              :     }
     873           47 :     MemSet(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           47 :     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           47 :     res = PQexec(conn, query);
     898           47 :     if (res == NULL)
     899              :     {
     900            0 :         free(lobjfuncs);
     901            0 :         return -1;
     902              :     }
     903              : 
     904           47 :     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          658 :     for (n = 0; n < PQntuples(res); n++)
     916              :     {
     917          611 :         fname = PQgetvalue(res, n, 0);
     918          611 :         foid = (Oid) atoi(PQgetvalue(res, n, 1));
     919          611 :         if (strcmp(fname, "lo_open") == 0)
     920           47 :             lobjfuncs->fn_lo_open = foid;
     921          564 :         else if (strcmp(fname, "lo_close") == 0)
     922           47 :             lobjfuncs->fn_lo_close = foid;
     923          517 :         else if (strcmp(fname, "lo_creat") == 0)
     924           47 :             lobjfuncs->fn_lo_creat = foid;
     925          470 :         else if (strcmp(fname, "lo_create") == 0)
     926           47 :             lobjfuncs->fn_lo_create = foid;
     927          423 :         else if (strcmp(fname, "lo_unlink") == 0)
     928           47 :             lobjfuncs->fn_lo_unlink = foid;
     929          376 :         else if (strcmp(fname, "lo_lseek") == 0)
     930           47 :             lobjfuncs->fn_lo_lseek = foid;
     931          329 :         else if (strcmp(fname, "lo_lseek64") == 0)
     932           47 :             lobjfuncs->fn_lo_lseek64 = foid;
     933          282 :         else if (strcmp(fname, "lo_tell") == 0)
     934           47 :             lobjfuncs->fn_lo_tell = foid;
     935          235 :         else if (strcmp(fname, "lo_tell64") == 0)
     936           47 :             lobjfuncs->fn_lo_tell64 = foid;
     937          188 :         else if (strcmp(fname, "lo_truncate") == 0)
     938           47 :             lobjfuncs->fn_lo_truncate = foid;
     939          141 :         else if (strcmp(fname, "lo_truncate64") == 0)
     940           47 :             lobjfuncs->fn_lo_truncate64 = foid;
     941           94 :         else if (strcmp(fname, "loread") == 0)
     942           47 :             lobjfuncs->fn_lo_read = foid;
     943           47 :         else if (strcmp(fname, "lowrite") == 0)
     944           47 :             lobjfuncs->fn_lo_write = foid;
     945              :     }
     946              : 
     947           47 :     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           47 :     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           47 :     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           47 :     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           47 :     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           47 :     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           47 :     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           47 :     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           47 :     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           47 :     conn->lobjfuncs = lobjfuncs;
    1015           47 :     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 int64_t
    1023            0 : lo_hton64(int64_t host64)
    1024              : {
    1025              :     union
    1026              :     {
    1027              :         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 int64_t
    1048            0 : lo_ntoh64(int64_t net64)
    1049              : {
    1050              :     union
    1051              :     {
    1052              :         int64       i64;
    1053              :         uint32      i32[2];
    1054              :     }           swap;
    1055              :     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 2.0-1