LCOV - code coverage report
Current view: top level - src/interfaces/libpq - fe-lobj.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 41.2 % 362 149
Test Date: 2026-08-14 01:16:50 Functions: 55.6 % 18 10
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 44.1 % 170 75

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

Generated by: LCOV version 2.0-1