LCOV - code coverage report
Current view: top level - src/backend/libpq - be-fsstubs.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 233 271 86.0 %
Date: 2025-01-18 04:15:08 Functions: 28 29 96.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * be-fsstubs.c
       4             :  *    Builtin functions for open/close/read/write operations on large objects
       5             :  *
       6             :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
       7             :  * Portions Copyright (c) 1994, Regents of the University of California
       8             :  *
       9             :  *
      10             :  * IDENTIFICATION
      11             :  *    src/backend/libpq/be-fsstubs.c
      12             :  *
      13             :  * NOTES
      14             :  *    This should be moved to a more appropriate place.  It is here
      15             :  *    for lack of a better place.
      16             :  *
      17             :  *    These functions store LargeObjectDesc structs in a private MemoryContext,
      18             :  *    which means that large object descriptors hang around until we destroy
      19             :  *    the context at transaction end.  It'd be possible to prolong the lifetime
      20             :  *    of the context so that LO FDs are good across transactions (for example,
      21             :  *    we could release the context only if we see that no FDs remain open).
      22             :  *    But we'd need additional state in order to do the right thing at the
      23             :  *    end of an aborted transaction.  FDs opened during an aborted xact would
      24             :  *    still need to be closed, since they might not be pointing at valid
      25             :  *    relations at all.  Locking semantics are also an interesting problem
      26             :  *    if LOs stay open across transactions.  For now, we'll stick with the
      27             :  *    existing documented semantics of LO FDs: they're only good within a
      28             :  *    transaction.
      29             :  *
      30             :  *    As of PostgreSQL 8.0, much of the angst expressed above is no longer
      31             :  *    relevant, and in fact it'd be pretty easy to allow LO FDs to stay
      32             :  *    open across transactions.  (Snapshot relevancy would still be an issue.)
      33             :  *    However backwards compatibility suggests that we should stick to the
      34             :  *    status quo.
      35             :  *
      36             :  *-------------------------------------------------------------------------
      37             :  */
      38             : 
      39             : #include "postgres.h"
      40             : 
      41             : #include <fcntl.h>
      42             : #include <sys/stat.h>
      43             : #include <unistd.h>
      44             : 
      45             : #include "access/xact.h"
      46             : #include "catalog/pg_largeobject.h"
      47             : #include "libpq/be-fsstubs.h"
      48             : #include "libpq/libpq-fs.h"
      49             : #include "miscadmin.h"
      50             : #include "storage/fd.h"
      51             : #include "storage/large_object.h"
      52             : #include "utils/acl.h"
      53             : #include "utils/builtins.h"
      54             : #include "utils/memutils.h"
      55             : #include "utils/snapmgr.h"
      56             : #include "varatt.h"
      57             : 
      58             : /* define this to enable debug logging */
      59             : /* #define FSDB 1 */
      60             : /* chunk size for lo_import/lo_export transfers */
      61             : #define BUFSIZE         8192
      62             : 
      63             : /*
      64             :  * LO "FD"s are indexes into the cookies array.
      65             :  *
      66             :  * A non-null entry is a pointer to a LargeObjectDesc allocated in the
      67             :  * LO private memory context "fscxt".  The cookies array itself is also
      68             :  * dynamically allocated in that context.  Its current allocated size is
      69             :  * cookies_size entries, of which any unused entries will be NULL.
      70             :  */
      71             : static LargeObjectDesc **cookies = NULL;
      72             : static int  cookies_size = 0;
      73             : 
      74             : static bool lo_cleanup_needed = false;
      75             : static MemoryContext fscxt = NULL;
      76             : 
      77             : static int  newLOfd(void);
      78             : static void closeLOfd(int fd);
      79             : static Oid  lo_import_internal(text *filename, Oid lobjOid);
      80             : 
      81             : 
      82             : /*****************************************************************************
      83             :  *  File Interfaces for Large Objects
      84             :  *****************************************************************************/
      85             : 
      86             : Datum
      87         342 : be_lo_open(PG_FUNCTION_ARGS)
      88             : {
      89         342 :     Oid         lobjId = PG_GETARG_OID(0);
      90         342 :     int32       mode = PG_GETARG_INT32(1);
      91             :     LargeObjectDesc *lobjDesc;
      92             :     int         fd;
      93             : 
      94             : #ifdef FSDB
      95             :     elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
      96             : #endif
      97             : 
      98         342 :     if (mode & INV_WRITE)
      99         116 :         PreventCommandIfReadOnly("lo_open(INV_WRITE)");
     100             : 
     101             :     /*
     102             :      * Allocate a large object descriptor first.  This will also create
     103             :      * 'fscxt' if this is the first LO opened in this transaction.
     104             :      */
     105         336 :     fd = newLOfd();
     106             : 
     107         336 :     lobjDesc = inv_open(lobjId, mode, fscxt);
     108         288 :     lobjDesc->subid = GetCurrentSubTransactionId();
     109             : 
     110             :     /*
     111             :      * We must register the snapshot in TopTransaction's resowner so that it
     112             :      * stays alive until the LO is closed rather than until the current portal
     113             :      * shuts down.
     114             :      */
     115         288 :     if (lobjDesc->snapshot)
     116         208 :         lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
     117             :                                                      TopTransactionResourceOwner);
     118             : 
     119             :     Assert(cookies[fd] == NULL);
     120         288 :     cookies[fd] = lobjDesc;
     121             : 
     122         288 :     PG_RETURN_INT32(fd);
     123             : }
     124             : 
     125             : Datum
     126         198 : be_lo_close(PG_FUNCTION_ARGS)
     127             : {
     128         198 :     int32       fd = PG_GETARG_INT32(0);
     129             : 
     130         198 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     131           0 :         ereport(ERROR,
     132             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     133             :                  errmsg("invalid large-object descriptor: %d", fd)));
     134             : 
     135             : #ifdef FSDB
     136             :     elog(DEBUG4, "lo_close(%d)", fd);
     137             : #endif
     138             : 
     139         198 :     closeLOfd(fd);
     140             : 
     141         198 :     PG_RETURN_INT32(0);
     142             : }
     143             : 
     144             : 
     145             : /*****************************************************************************
     146             :  *  Bare Read/Write operations --- these are not fmgr-callable!
     147             :  *
     148             :  *  We assume the large object supports byte oriented reads and seeks so
     149             :  *  that our work is easier.
     150             :  *
     151             :  *****************************************************************************/
     152             : 
     153             : int
     154         808 : lo_read(int fd, char *buf, int len)
     155             : {
     156             :     int         status;
     157             :     LargeObjectDesc *lobj;
     158             : 
     159         808 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     160           0 :         ereport(ERROR,
     161             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     162             :                  errmsg("invalid large-object descriptor: %d", fd)));
     163         808 :     lobj = cookies[fd];
     164             : 
     165             :     /*
     166             :      * Check state.  inv_read() would throw an error anyway, but we want the
     167             :      * error to be about the FD's state not the underlying privilege; it might
     168             :      * be that the privilege exists but user forgot to ask for read mode.
     169             :      */
     170         808 :     if ((lobj->flags & IFS_RDLOCK) == 0)
     171           0 :         ereport(ERROR,
     172             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     173             :                  errmsg("large object descriptor %d was not opened for reading",
     174             :                         fd)));
     175             : 
     176         808 :     status = inv_read(lobj, buf, len);
     177             : 
     178         808 :     return status;
     179             : }
     180             : 
     181             : int
     182        1034 : lo_write(int fd, const char *buf, int len)
     183             : {
     184             :     int         status;
     185             :     LargeObjectDesc *lobj;
     186             : 
     187        1034 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     188           0 :         ereport(ERROR,
     189             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     190             :                  errmsg("invalid large-object descriptor: %d", fd)));
     191        1034 :     lobj = cookies[fd];
     192             : 
     193             :     /* see comment in lo_read() */
     194        1034 :     if ((lobj->flags & IFS_WRLOCK) == 0)
     195           6 :         ereport(ERROR,
     196             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     197             :                  errmsg("large object descriptor %d was not opened for writing",
     198             :                         fd)));
     199             : 
     200        1028 :     status = inv_write(lobj, buf, len);
     201             : 
     202        1028 :     return status;
     203             : }
     204             : 
     205             : Datum
     206          54 : be_lo_lseek(PG_FUNCTION_ARGS)
     207             : {
     208          54 :     int32       fd = PG_GETARG_INT32(0);
     209          54 :     int32       offset = PG_GETARG_INT32(1);
     210          54 :     int32       whence = PG_GETARG_INT32(2);
     211             :     int64       status;
     212             : 
     213          54 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     214           0 :         ereport(ERROR,
     215             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     216             :                  errmsg("invalid large-object descriptor: %d", fd)));
     217             : 
     218          54 :     status = inv_seek(cookies[fd], offset, whence);
     219             : 
     220             :     /* guard against result overflow */
     221          54 :     if (status != (int32) status)
     222           0 :         ereport(ERROR,
     223             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     224             :                  errmsg("lo_lseek result out of range for large-object descriptor %d",
     225             :                         fd)));
     226             : 
     227          54 :     PG_RETURN_INT32((int32) status);
     228             : }
     229             : 
     230             : Datum
     231          24 : be_lo_lseek64(PG_FUNCTION_ARGS)
     232             : {
     233          24 :     int32       fd = PG_GETARG_INT32(0);
     234          24 :     int64       offset = PG_GETARG_INT64(1);
     235          24 :     int32       whence = PG_GETARG_INT32(2);
     236             :     int64       status;
     237             : 
     238          24 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     239           0 :         ereport(ERROR,
     240             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     241             :                  errmsg("invalid large-object descriptor: %d", fd)));
     242             : 
     243          24 :     status = inv_seek(cookies[fd], offset, whence);
     244             : 
     245          24 :     PG_RETURN_INT64(status);
     246             : }
     247             : 
     248             : Datum
     249          26 : be_lo_creat(PG_FUNCTION_ARGS)
     250             : {
     251             :     Oid         lobjId;
     252             : 
     253          26 :     PreventCommandIfReadOnly("lo_creat()");
     254             : 
     255          20 :     lo_cleanup_needed = true;
     256          20 :     lobjId = inv_create(InvalidOid);
     257             : 
     258          20 :     PG_RETURN_OID(lobjId);
     259             : }
     260             : 
     261             : Datum
     262          72 : be_lo_create(PG_FUNCTION_ARGS)
     263             : {
     264          72 :     Oid         lobjId = PG_GETARG_OID(0);
     265             : 
     266          72 :     PreventCommandIfReadOnly("lo_create()");
     267             : 
     268          66 :     lo_cleanup_needed = true;
     269          66 :     lobjId = inv_create(lobjId);
     270             : 
     271          66 :     PG_RETURN_OID(lobjId);
     272             : }
     273             : 
     274             : Datum
     275          24 : be_lo_tell(PG_FUNCTION_ARGS)
     276             : {
     277          24 :     int32       fd = PG_GETARG_INT32(0);
     278             :     int64       offset;
     279             : 
     280          24 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     281           0 :         ereport(ERROR,
     282             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     283             :                  errmsg("invalid large-object descriptor: %d", fd)));
     284             : 
     285          24 :     offset = inv_tell(cookies[fd]);
     286             : 
     287             :     /* guard against result overflow */
     288          24 :     if (offset != (int32) offset)
     289           0 :         ereport(ERROR,
     290             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
     291             :                  errmsg("lo_tell result out of range for large-object descriptor %d",
     292             :                         fd)));
     293             : 
     294          24 :     PG_RETURN_INT32((int32) offset);
     295             : }
     296             : 
     297             : Datum
     298          24 : be_lo_tell64(PG_FUNCTION_ARGS)
     299             : {
     300          24 :     int32       fd = PG_GETARG_INT32(0);
     301             :     int64       offset;
     302             : 
     303          24 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     304           0 :         ereport(ERROR,
     305             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     306             :                  errmsg("invalid large-object descriptor: %d", fd)));
     307             : 
     308          24 :     offset = inv_tell(cookies[fd]);
     309             : 
     310          24 :     PG_RETURN_INT64(offset);
     311             : }
     312             : 
     313             : Datum
     314         100 : be_lo_unlink(PG_FUNCTION_ARGS)
     315             : {
     316         100 :     Oid         lobjId = PG_GETARG_OID(0);
     317             : 
     318         100 :     PreventCommandIfReadOnly("lo_unlink()");
     319             : 
     320          94 :     if (!LargeObjectExists(lobjId))
     321           0 :         ereport(ERROR,
     322             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     323             :                  errmsg("large object %u does not exist", lobjId)));
     324             : 
     325             :     /*
     326             :      * Must be owner of the large object.  It would be cleaner to check this
     327             :      * in inv_drop(), but we want to throw the error before not after closing
     328             :      * relevant FDs.
     329             :      */
     330          94 :     if (!lo_compat_privileges &&
     331          88 :         !object_ownercheck(LargeObjectRelationId, lobjId, GetUserId()))
     332          12 :         ereport(ERROR,
     333             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
     334             :                  errmsg("must be owner of large object %u", lobjId)));
     335             : 
     336             :     /*
     337             :      * If there are any open LO FDs referencing that ID, close 'em.
     338             :      */
     339          82 :     if (fscxt != NULL)
     340             :     {
     341             :         int         i;
     342             : 
     343           0 :         for (i = 0; i < cookies_size; i++)
     344             :         {
     345           0 :             if (cookies[i] != NULL && cookies[i]->id == lobjId)
     346           0 :                 closeLOfd(i);
     347             :         }
     348             :     }
     349             : 
     350             :     /*
     351             :      * inv_drop does not create a need for end-of-transaction cleanup and
     352             :      * hence we don't need to set lo_cleanup_needed.
     353             :      */
     354          82 :     PG_RETURN_INT32(inv_drop(lobjId));
     355             : }
     356             : 
     357             : /*****************************************************************************
     358             :  *  Read/Write using bytea
     359             :  *****************************************************************************/
     360             : 
     361             : Datum
     362         808 : be_loread(PG_FUNCTION_ARGS)
     363             : {
     364         808 :     int32       fd = PG_GETARG_INT32(0);
     365         808 :     int32       len = PG_GETARG_INT32(1);
     366             :     bytea      *retval;
     367             :     int         totalread;
     368             : 
     369         808 :     if (len < 0)
     370           0 :         len = 0;
     371             : 
     372         808 :     retval = (bytea *) palloc(VARHDRSZ + len);
     373         808 :     totalread = lo_read(fd, VARDATA(retval), len);
     374         808 :     SET_VARSIZE(retval, totalread + VARHDRSZ);
     375             : 
     376         808 :     PG_RETURN_BYTEA_P(retval);
     377             : }
     378             : 
     379             : Datum
     380        1040 : be_lowrite(PG_FUNCTION_ARGS)
     381             : {
     382        1040 :     int32       fd = PG_GETARG_INT32(0);
     383        1040 :     bytea      *wbuf = PG_GETARG_BYTEA_PP(1);
     384             :     int         bytestowrite;
     385             :     int         totalwritten;
     386             : 
     387        1040 :     PreventCommandIfReadOnly("lowrite()");
     388             : 
     389        1034 :     bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
     390        1034 :     totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
     391        1028 :     PG_RETURN_INT32(totalwritten);
     392             : }
     393             : 
     394             : /*****************************************************************************
     395             :  *   Import/Export of Large Object
     396             :  *****************************************************************************/
     397             : 
     398             : /*
     399             :  * lo_import -
     400             :  *    imports a file as an (inversion) large object.
     401             :  */
     402             : Datum
     403          12 : be_lo_import(PG_FUNCTION_ARGS)
     404             : {
     405          12 :     text       *filename = PG_GETARG_TEXT_PP(0);
     406             : 
     407          12 :     PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
     408             : }
     409             : 
     410             : /*
     411             :  * lo_import_with_oid -
     412             :  *    imports a file as an (inversion) large object specifying oid.
     413             :  */
     414             : Datum
     415           0 : be_lo_import_with_oid(PG_FUNCTION_ARGS)
     416             : {
     417           0 :     text       *filename = PG_GETARG_TEXT_PP(0);
     418           0 :     Oid         oid = PG_GETARG_OID(1);
     419             : 
     420           0 :     PG_RETURN_OID(lo_import_internal(filename, oid));
     421             : }
     422             : 
     423             : static Oid
     424          12 : lo_import_internal(text *filename, Oid lobjOid)
     425             : {
     426             :     int         fd;
     427             :     int         nbytes,
     428             :                 tmp PG_USED_FOR_ASSERTS_ONLY;
     429             :     char        buf[BUFSIZE];
     430             :     char        fnamebuf[MAXPGPATH];
     431             :     LargeObjectDesc *lobj;
     432             :     Oid         oid;
     433             : 
     434          12 :     PreventCommandIfReadOnly("lo_import()");
     435             : 
     436             :     /*
     437             :      * open the file to be read in
     438             :      */
     439           6 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
     440           6 :     fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
     441           6 :     if (fd < 0)
     442           0 :         ereport(ERROR,
     443             :                 (errcode_for_file_access(),
     444             :                  errmsg("could not open server file \"%s\": %m",
     445             :                         fnamebuf)));
     446             : 
     447             :     /*
     448             :      * create an inversion object
     449             :      */
     450           6 :     lo_cleanup_needed = true;
     451           6 :     oid = inv_create(lobjOid);
     452             : 
     453             :     /*
     454             :      * read in from the filesystem and write to the inversion object
     455             :      */
     456           6 :     lobj = inv_open(oid, INV_WRITE, CurrentMemoryContext);
     457             : 
     458         498 :     while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
     459             :     {
     460         492 :         tmp = inv_write(lobj, buf, nbytes);
     461             :         Assert(tmp == nbytes);
     462             :     }
     463             : 
     464           6 :     if (nbytes < 0)
     465           0 :         ereport(ERROR,
     466             :                 (errcode_for_file_access(),
     467             :                  errmsg("could not read server file \"%s\": %m",
     468             :                         fnamebuf)));
     469             : 
     470           6 :     inv_close(lobj);
     471             : 
     472           6 :     if (CloseTransientFile(fd) != 0)
     473           0 :         ereport(ERROR,
     474             :                 (errcode_for_file_access(),
     475             :                  errmsg("could not close file \"%s\": %m",
     476             :                         fnamebuf)));
     477             : 
     478           6 :     return oid;
     479             : }
     480             : 
     481             : /*
     482             :  * lo_export -
     483             :  *    exports an (inversion) large object.
     484             :  */
     485             : Datum
     486          12 : be_lo_export(PG_FUNCTION_ARGS)
     487             : {
     488          12 :     Oid         lobjId = PG_GETARG_OID(0);
     489          12 :     text       *filename = PG_GETARG_TEXT_PP(1);
     490             :     int         fd;
     491             :     int         nbytes,
     492             :                 tmp;
     493             :     char        buf[BUFSIZE];
     494             :     char        fnamebuf[MAXPGPATH];
     495             :     LargeObjectDesc *lobj;
     496             :     mode_t      oumask;
     497             : 
     498             :     /*
     499             :      * open the inversion object (no need to test for failure)
     500             :      */
     501          12 :     lo_cleanup_needed = true;
     502          12 :     lobj = inv_open(lobjId, INV_READ, CurrentMemoryContext);
     503             : 
     504             :     /*
     505             :      * open the file to be written to
     506             :      *
     507             :      * Note: we reduce backend's normal 077 umask to the slightly friendlier
     508             :      * 022. This code used to drop it all the way to 0, but creating
     509             :      * world-writable export files doesn't seem wise.
     510             :      */
     511          12 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
     512          12 :     oumask = umask(S_IWGRP | S_IWOTH);
     513          12 :     PG_TRY();
     514             :     {
     515          12 :         fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
     516             :                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
     517             :     }
     518           0 :     PG_FINALLY();
     519             :     {
     520          12 :         umask(oumask);
     521             :     }
     522          12 :     PG_END_TRY();
     523          12 :     if (fd < 0)
     524           6 :         ereport(ERROR,
     525             :                 (errcode_for_file_access(),
     526             :                  errmsg("could not create server file \"%s\": %m",
     527             :                         fnamebuf)));
     528             : 
     529             :     /*
     530             :      * read in from the inversion file and write to the filesystem
     531             :      */
     532         498 :     while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
     533             :     {
     534         492 :         tmp = write(fd, buf, nbytes);
     535         492 :         if (tmp != nbytes)
     536           0 :             ereport(ERROR,
     537             :                     (errcode_for_file_access(),
     538             :                      errmsg("could not write server file \"%s\": %m",
     539             :                             fnamebuf)));
     540             :     }
     541             : 
     542           6 :     if (CloseTransientFile(fd) != 0)
     543           0 :         ereport(ERROR,
     544             :                 (errcode_for_file_access(),
     545             :                  errmsg("could not close file \"%s\": %m",
     546             :                         fnamebuf)));
     547             : 
     548           6 :     inv_close(lobj);
     549             : 
     550           6 :     PG_RETURN_INT32(1);
     551             : }
     552             : 
     553             : /*
     554             :  * lo_truncate -
     555             :  *    truncate a large object to a specified length
     556             :  */
     557             : static void
     558          42 : lo_truncate_internal(int32 fd, int64 len)
     559             : {
     560             :     LargeObjectDesc *lobj;
     561             : 
     562          42 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
     563           0 :         ereport(ERROR,
     564             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
     565             :                  errmsg("invalid large-object descriptor: %d", fd)));
     566          42 :     lobj = cookies[fd];
     567             : 
     568             :     /* see comment in lo_read() */
     569          42 :     if ((lobj->flags & IFS_WRLOCK) == 0)
     570           0 :         ereport(ERROR,
     571             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     572             :                  errmsg("large object descriptor %d was not opened for writing",
     573             :                         fd)));
     574             : 
     575          42 :     inv_truncate(lobj, len);
     576          42 : }
     577             : 
     578             : Datum
     579          36 : be_lo_truncate(PG_FUNCTION_ARGS)
     580             : {
     581          36 :     int32       fd = PG_GETARG_INT32(0);
     582          36 :     int32       len = PG_GETARG_INT32(1);
     583             : 
     584          36 :     PreventCommandIfReadOnly("lo_truncate()");
     585             : 
     586          30 :     lo_truncate_internal(fd, len);
     587          30 :     PG_RETURN_INT32(0);
     588             : }
     589             : 
     590             : Datum
     591          18 : be_lo_truncate64(PG_FUNCTION_ARGS)
     592             : {
     593          18 :     int32       fd = PG_GETARG_INT32(0);
     594          18 :     int64       len = PG_GETARG_INT64(1);
     595             : 
     596          18 :     PreventCommandIfReadOnly("lo_truncate64()");
     597             : 
     598          12 :     lo_truncate_internal(fd, len);
     599          12 :     PG_RETURN_INT32(0);
     600             : }
     601             : 
     602             : /*
     603             :  * AtEOXact_LargeObject -
     604             :  *       prepares large objects for transaction commit
     605             :  */
     606             : void
     607      786380 : AtEOXact_LargeObject(bool isCommit)
     608             : {
     609             :     int         i;
     610             : 
     611      786380 :     if (!lo_cleanup_needed)
     612      785938 :         return;                 /* no LO operations in this xact */
     613             : 
     614             :     /*
     615             :      * Close LO fds and clear cookies array so that LO fds are no longer good.
     616             :      * The memory context and resource owner holding them are going away at
     617             :      * the end-of-transaction anyway, but on commit, we need to close them to
     618             :      * avoid warnings about leaked resources at commit.  On abort we can skip
     619             :      * this step.
     620             :      */
     621         442 :     if (isCommit)
     622             :     {
     623        8092 :         for (i = 0; i < cookies_size; i++)
     624             :         {
     625        7808 :             if (cookies[i] != NULL)
     626          72 :                 closeLOfd(i);
     627             :         }
     628             :     }
     629             : 
     630             :     /* Needn't actually pfree since we're about to zap context */
     631         442 :     cookies = NULL;
     632         442 :     cookies_size = 0;
     633             : 
     634             :     /* Release the LO memory context to prevent permanent memory leaks. */
     635         442 :     if (fscxt)
     636         264 :         MemoryContextDelete(fscxt);
     637         442 :     fscxt = NULL;
     638             : 
     639             :     /* Give inv_api.c a chance to clean up, too */
     640         442 :     close_lo_relation(isCommit);
     641             : 
     642         442 :     lo_cleanup_needed = false;
     643             : }
     644             : 
     645             : /*
     646             :  * AtEOSubXact_LargeObject
     647             :  *      Take care of large objects at subtransaction commit/abort
     648             :  *
     649             :  * Reassign LOs created/opened during a committing subtransaction
     650             :  * to the parent subtransaction.  On abort, just close them.
     651             :  */
     652             : void
     653       20040 : AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
     654             :                         SubTransactionId parentSubid)
     655             : {
     656             :     int         i;
     657             : 
     658       20040 :     if (fscxt == NULL)          /* no LO operations in this xact */
     659       20040 :         return;
     660             : 
     661           0 :     for (i = 0; i < cookies_size; i++)
     662             :     {
     663           0 :         LargeObjectDesc *lo = cookies[i];
     664             : 
     665           0 :         if (lo != NULL && lo->subid == mySubid)
     666             :         {
     667           0 :             if (isCommit)
     668           0 :                 lo->subid = parentSubid;
     669             :             else
     670           0 :                 closeLOfd(i);
     671             :         }
     672             :     }
     673             : }
     674             : 
     675             : /*****************************************************************************
     676             :  *  Support routines for this file
     677             :  *****************************************************************************/
     678             : 
     679             : static int
     680         336 : newLOfd(void)
     681             : {
     682             :     int         i,
     683             :                 newsize;
     684             : 
     685         336 :     lo_cleanup_needed = true;
     686         336 :     if (fscxt == NULL)
     687         264 :         fscxt = AllocSetContextCreate(TopMemoryContext,
     688             :                                       "Filesystem",
     689             :                                       ALLOCSET_DEFAULT_SIZES);
     690             : 
     691             :     /* Try to find a free slot */
     692         336 :     for (i = 0; i < cookies_size; i++)
     693             :     {
     694          72 :         if (cookies[i] == NULL)
     695          72 :             return i;
     696             :     }
     697             : 
     698             :     /* No free slot, so make the array bigger */
     699         264 :     if (cookies_size <= 0)
     700             :     {
     701             :         /* First time through, arbitrarily make 64-element array */
     702         264 :         i = 0;
     703         264 :         newsize = 64;
     704         264 :         cookies = (LargeObjectDesc **)
     705         264 :             MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
     706             :     }
     707             :     else
     708             :     {
     709             :         /* Double size of array */
     710           0 :         i = cookies_size;
     711           0 :         newsize = cookies_size * 2;
     712           0 :         cookies =
     713           0 :             repalloc0_array(cookies, LargeObjectDesc *, cookies_size, newsize);
     714             :     }
     715         264 :     cookies_size = newsize;
     716             : 
     717         264 :     return i;
     718             : }
     719             : 
     720             : static void
     721         270 : closeLOfd(int fd)
     722             : {
     723             :     LargeObjectDesc *lobj;
     724             : 
     725             :     /*
     726             :      * Make sure we do not try to free twice if this errors out for some
     727             :      * reason.  Better a leak than a crash.
     728             :      */
     729         270 :     lobj = cookies[fd];
     730         270 :     cookies[fd] = NULL;
     731             : 
     732         270 :     if (lobj->snapshot)
     733         190 :         UnregisterSnapshotFromOwner(lobj->snapshot,
     734             :                                     TopTransactionResourceOwner);
     735         270 :     inv_close(lobj);
     736         270 : }
     737             : 
     738             : /*****************************************************************************
     739             :  *  Wrappers oriented toward SQL callers
     740             :  *****************************************************************************/
     741             : 
     742             : /*
     743             :  * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
     744             :  */
     745             : static bytea *
     746          72 : lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
     747             : {
     748             :     LargeObjectDesc *loDesc;
     749             :     int64       loSize;
     750             :     int64       result_length;
     751             :     int         total_read PG_USED_FOR_ASSERTS_ONLY;
     752          72 :     bytea      *result = NULL;
     753             : 
     754          72 :     lo_cleanup_needed = true;
     755          72 :     loDesc = inv_open(loOid, INV_READ, CurrentMemoryContext);
     756             : 
     757             :     /*
     758             :      * Compute number of bytes we'll actually read, accommodating nbytes == -1
     759             :      * and reads beyond the end of the LO.
     760             :      */
     761          68 :     loSize = inv_seek(loDesc, 0, SEEK_END);
     762          68 :     if (loSize > offset)
     763             :     {
     764          60 :         if (nbytes >= 0 && nbytes <= loSize - offset)
     765          18 :             result_length = nbytes; /* request is wholly inside LO */
     766             :         else
     767          42 :             result_length = loSize - offset;    /* adjust to end of LO */
     768             :     }
     769             :     else
     770           8 :         result_length = 0;      /* request is wholly outside LO */
     771             : 
     772             :     /*
     773             :      * A result_length calculated from loSize may not fit in a size_t.  Check
     774             :      * that the size will satisfy this and subsequently-enforced size limits.
     775             :      */
     776          68 :     if (result_length > MaxAllocSize - VARHDRSZ)
     777           6 :         ereport(ERROR,
     778             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     779             :                  errmsg("large object read request is too large")));
     780             : 
     781          62 :     result = (bytea *) palloc(VARHDRSZ + result_length);
     782             : 
     783          62 :     inv_seek(loDesc, offset, SEEK_SET);
     784          62 :     total_read = inv_read(loDesc, VARDATA(result), result_length);
     785             :     Assert(total_read == result_length);
     786          62 :     SET_VARSIZE(result, result_length + VARHDRSZ);
     787             : 
     788          62 :     inv_close(loDesc);
     789             : 
     790          62 :     return result;
     791             : }
     792             : 
     793             : /*
     794             :  * Read entire LO
     795             :  */
     796             : Datum
     797          48 : be_lo_get(PG_FUNCTION_ARGS)
     798             : {
     799          48 :     Oid         loOid = PG_GETARG_OID(0);
     800             :     bytea      *result;
     801             : 
     802          48 :     result = lo_get_fragment_internal(loOid, 0, -1);
     803             : 
     804          38 :     PG_RETURN_BYTEA_P(result);
     805             : }
     806             : 
     807             : /*
     808             :  * Read range within LO
     809             :  */
     810             : Datum
     811          24 : be_lo_get_fragment(PG_FUNCTION_ARGS)
     812             : {
     813          24 :     Oid         loOid = PG_GETARG_OID(0);
     814          24 :     int64       offset = PG_GETARG_INT64(1);
     815          24 :     int32       nbytes = PG_GETARG_INT32(2);
     816             :     bytea      *result;
     817             : 
     818          24 :     if (nbytes < 0)
     819           0 :         ereport(ERROR,
     820             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     821             :                  errmsg("requested length cannot be negative")));
     822             : 
     823          24 :     result = lo_get_fragment_internal(loOid, offset, nbytes);
     824             : 
     825          24 :     PG_RETURN_BYTEA_P(result);
     826             : }
     827             : 
     828             : /*
     829             :  * Create LO with initial contents given by a bytea argument
     830             :  */
     831             : Datum
     832          26 : be_lo_from_bytea(PG_FUNCTION_ARGS)
     833             : {
     834          26 :     Oid         loOid = PG_GETARG_OID(0);
     835          26 :     bytea      *str = PG_GETARG_BYTEA_PP(1);
     836             :     LargeObjectDesc *loDesc;
     837             :     int         written PG_USED_FOR_ASSERTS_ONLY;
     838             : 
     839          26 :     PreventCommandIfReadOnly("lo_from_bytea()");
     840             : 
     841          20 :     lo_cleanup_needed = true;
     842          20 :     loOid = inv_create(loOid);
     843          20 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
     844          20 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
     845             :     Assert(written == VARSIZE_ANY_EXHDR(str));
     846          20 :     inv_close(loDesc);
     847             : 
     848          20 :     PG_RETURN_OID(loOid);
     849             : }
     850             : 
     851             : /*
     852             :  * Update range within LO
     853             :  */
     854             : Datum
     855          24 : be_lo_put(PG_FUNCTION_ARGS)
     856             : {
     857          24 :     Oid         loOid = PG_GETARG_OID(0);
     858          24 :     int64       offset = PG_GETARG_INT64(1);
     859          24 :     bytea      *str = PG_GETARG_BYTEA_PP(2);
     860             :     LargeObjectDesc *loDesc;
     861             :     int         written PG_USED_FOR_ASSERTS_ONLY;
     862             : 
     863          24 :     PreventCommandIfReadOnly("lo_put()");
     864             : 
     865          18 :     lo_cleanup_needed = true;
     866          18 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
     867          12 :     inv_seek(loDesc, offset, SEEK_SET);
     868          12 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
     869             :     Assert(written == VARSIZE_ANY_EXHDR(str));
     870          12 :     inv_close(loDesc);
     871             : 
     872          12 :     PG_RETURN_VOID();
     873             : }

Generated by: LCOV version 1.14