LCOV - code coverage report
Current view: top level - src/backend/libpq - be-fsstubs.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 86.0 % 271 233
Test Date: 2026-03-01 09:14:34 Functions: 96.6 % 29 28
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-2026, 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          193 : be_lo_open(PG_FUNCTION_ARGS)
      88              : {
      89          193 :     Oid         lobjId = PG_GETARG_OID(0);
      90          193 :     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          193 :     if (mode & INV_WRITE)
      99           70 :         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          190 :     fd = newLOfd();
     106              : 
     107          190 :     lobjDesc = inv_open(lobjId, mode, fscxt);
     108          160 :     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          160 :     if (lobjDesc->snapshot)
     116          114 :         lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
     117              :                                                      TopTransactionResourceOwner);
     118              : 
     119              :     Assert(cookies[fd] == NULL);
     120          160 :     cookies[fd] = lobjDesc;
     121              : 
     122          160 :     PG_RETURN_INT32(fd);
     123              : }
     124              : 
     125              : Datum
     126          106 : be_lo_close(PG_FUNCTION_ARGS)
     127              : {
     128          106 :     int32       fd = PG_GETARG_INT32(0);
     129              : 
     130          106 :     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          106 :     closeLOfd(fd);
     140              : 
     141          106 :     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          421 : lo_read(int fd, char *buf, int len)
     155              : {
     156              :     int         status;
     157              :     LargeObjectDesc *lobj;
     158              : 
     159          421 :     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          421 :     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          421 :     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          421 :     status = inv_read(lobj, buf, len);
     177              : 
     178          421 :     return status;
     179              : }
     180              : 
     181              : int
     182          520 : lo_write(int fd, const char *buf, int len)
     183              : {
     184              :     int         status;
     185              :     LargeObjectDesc *lobj;
     186              : 
     187          520 :     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          520 :     lobj = cookies[fd];
     192              : 
     193              :     /* see comment in lo_read() */
     194          520 :     if ((lobj->flags & IFS_WRLOCK) == 0)
     195            3 :         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          517 :     status = inv_write(lobj, buf, len);
     201              : 
     202          517 :     return status;
     203              : }
     204              : 
     205              : Datum
     206           27 : be_lo_lseek(PG_FUNCTION_ARGS)
     207              : {
     208           27 :     int32       fd = PG_GETARG_INT32(0);
     209           27 :     int32       offset = PG_GETARG_INT32(1);
     210           27 :     int32       whence = PG_GETARG_INT32(2);
     211              :     int64       status;
     212              : 
     213           27 :     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           27 :     status = inv_seek(cookies[fd], offset, whence);
     219              : 
     220              :     /* guard against result overflow */
     221           27 :     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           27 :     PG_RETURN_INT32((int32) status);
     228              : }
     229              : 
     230              : Datum
     231           12 : be_lo_lseek64(PG_FUNCTION_ARGS)
     232              : {
     233           12 :     int32       fd = PG_GETARG_INT32(0);
     234           12 :     int64       offset = PG_GETARG_INT64(1);
     235           12 :     int32       whence = PG_GETARG_INT32(2);
     236              :     int64       status;
     237              : 
     238           12 :     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           12 :     status = inv_seek(cookies[fd], offset, whence);
     244              : 
     245           12 :     PG_RETURN_INT64(status);
     246              : }
     247              : 
     248              : Datum
     249           13 : be_lo_creat(PG_FUNCTION_ARGS)
     250              : {
     251              :     Oid         lobjId;
     252              : 
     253           13 :     PreventCommandIfReadOnly("lo_creat()");
     254              : 
     255           10 :     lo_cleanup_needed = true;
     256           10 :     lobjId = inv_create(InvalidOid);
     257              : 
     258           10 :     PG_RETURN_OID(lobjId);
     259              : }
     260              : 
     261              : Datum
     262           48 : be_lo_create(PG_FUNCTION_ARGS)
     263              : {
     264           48 :     Oid         lobjId = PG_GETARG_OID(0);
     265              : 
     266           48 :     PreventCommandIfReadOnly("lo_create()");
     267              : 
     268           45 :     lo_cleanup_needed = true;
     269           45 :     lobjId = inv_create(lobjId);
     270              : 
     271           45 :     PG_RETURN_OID(lobjId);
     272              : }
     273              : 
     274              : Datum
     275           12 : be_lo_tell(PG_FUNCTION_ARGS)
     276              : {
     277           12 :     int32       fd = PG_GETARG_INT32(0);
     278              :     int64       offset;
     279              : 
     280           12 :     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           12 :     offset = inv_tell(cookies[fd]);
     286              : 
     287              :     /* guard against result overflow */
     288           12 :     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           12 :     PG_RETURN_INT32((int32) offset);
     295              : }
     296              : 
     297              : Datum
     298           12 : be_lo_tell64(PG_FUNCTION_ARGS)
     299              : {
     300           12 :     int32       fd = PG_GETARG_INT32(0);
     301              :     int64       offset;
     302              : 
     303           12 :     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           12 :     offset = inv_tell(cookies[fd]);
     309              : 
     310           12 :     PG_RETURN_INT64(offset);
     311              : }
     312              : 
     313              : Datum
     314           59 : be_lo_unlink(PG_FUNCTION_ARGS)
     315              : {
     316           59 :     Oid         lobjId = PG_GETARG_OID(0);
     317              : 
     318           59 :     PreventCommandIfReadOnly("lo_unlink()");
     319              : 
     320           56 :     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           56 :     if (!lo_compat_privileges &&
     331           53 :         !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           44 :     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           44 :     PG_RETURN_INT32(inv_drop(lobjId));
     355              : }
     356              : 
     357              : /*****************************************************************************
     358              :  *  Read/Write using bytea
     359              :  *****************************************************************************/
     360              : 
     361              : Datum
     362          421 : be_loread(PG_FUNCTION_ARGS)
     363              : {
     364          421 :     int32       fd = PG_GETARG_INT32(0);
     365          421 :     int32       len = PG_GETARG_INT32(1);
     366              :     bytea      *retval;
     367              :     int         totalread;
     368              : 
     369          421 :     if (len < 0)
     370            0 :         len = 0;
     371              : 
     372          421 :     retval = (bytea *) palloc(VARHDRSZ + len);
     373          421 :     totalread = lo_read(fd, VARDATA(retval), len);
     374          421 :     SET_VARSIZE(retval, totalread + VARHDRSZ);
     375              : 
     376          421 :     PG_RETURN_BYTEA_P(retval);
     377              : }
     378              : 
     379              : Datum
     380          523 : be_lowrite(PG_FUNCTION_ARGS)
     381              : {
     382          523 :     int32       fd = PG_GETARG_INT32(0);
     383          523 :     bytea      *wbuf = PG_GETARG_BYTEA_PP(1);
     384              :     int         bytestowrite;
     385              :     int         totalwritten;
     386              : 
     387          523 :     PreventCommandIfReadOnly("lowrite()");
     388              : 
     389          520 :     bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
     390          520 :     totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
     391          517 :     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            6 : be_lo_import(PG_FUNCTION_ARGS)
     404              : {
     405            6 :     text       *filename = PG_GETARG_TEXT_PP(0);
     406              : 
     407            6 :     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            6 : 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            6 :     PreventCommandIfReadOnly("lo_import()");
     435              : 
     436              :     /*
     437              :      * open the file to be read in
     438              :      */
     439            3 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
     440            3 :     fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
     441            3 :     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            3 :     lo_cleanup_needed = true;
     451            3 :     oid = inv_create(lobjOid);
     452              : 
     453              :     /*
     454              :      * read in from the filesystem and write to the inversion object
     455              :      */
     456            3 :     lobj = inv_open(oid, INV_WRITE, CurrentMemoryContext);
     457              : 
     458          249 :     while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
     459              :     {
     460          246 :         tmp = inv_write(lobj, buf, nbytes);
     461              :         Assert(tmp == nbytes);
     462              :     }
     463              : 
     464            3 :     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            3 :     inv_close(lobj);
     471              : 
     472            3 :     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            3 :     return oid;
     479              : }
     480              : 
     481              : /*
     482              :  * lo_export -
     483              :  *    exports an (inversion) large object.
     484              :  */
     485              : Datum
     486            6 : be_lo_export(PG_FUNCTION_ARGS)
     487              : {
     488            6 :     Oid         lobjId = PG_GETARG_OID(0);
     489            6 :     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            6 :     lo_cleanup_needed = true;
     502            6 :     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            6 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
     512            6 :     oumask = umask(S_IWGRP | S_IWOTH);
     513            6 :     PG_TRY();
     514              :     {
     515            6 :         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            6 :         umask(oumask);
     521              :     }
     522            6 :     PG_END_TRY();
     523            6 :     if (fd < 0)
     524            3 :         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          249 :     while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
     533              :     {
     534          246 :         tmp = write(fd, buf, nbytes);
     535          246 :         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            3 :     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            3 :     inv_close(lobj);
     549              : 
     550            3 :     PG_RETURN_INT32(1);
     551              : }
     552              : 
     553              : /*
     554              :  * lo_truncate -
     555              :  *    truncate a large object to a specified length
     556              :  */
     557              : static void
     558           24 : lo_truncate_internal(int32 fd, int64 len)
     559              : {
     560              :     LargeObjectDesc *lobj;
     561              : 
     562           24 :     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           24 :     lobj = cookies[fd];
     567              : 
     568              :     /* see comment in lo_read() */
     569           24 :     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           24 :     inv_truncate(lobj, len);
     576           24 : }
     577              : 
     578              : Datum
     579           21 : be_lo_truncate(PG_FUNCTION_ARGS)
     580              : {
     581           21 :     int32       fd = PG_GETARG_INT32(0);
     582           21 :     int32       len = PG_GETARG_INT32(1);
     583              : 
     584           21 :     PreventCommandIfReadOnly("lo_truncate()");
     585              : 
     586           18 :     lo_truncate_internal(fd, len);
     587           18 :     PG_RETURN_INT32(0);
     588              : }
     589              : 
     590              : Datum
     591            9 : be_lo_truncate64(PG_FUNCTION_ARGS)
     592              : {
     593            9 :     int32       fd = PG_GETARG_INT32(0);
     594            9 :     int64       len = PG_GETARG_INT64(1);
     595              : 
     596            9 :     PreventCommandIfReadOnly("lo_truncate64()");
     597              : 
     598            6 :     lo_truncate_internal(fd, len);
     599            6 :     PG_RETURN_INT32(0);
     600              : }
     601              : 
     602              : /*
     603              :  * AtEOXact_LargeObject -
     604              :  *       prepares large objects for transaction commit
     605              :  */
     606              : void
     607       550540 : AtEOXact_LargeObject(bool isCommit)
     608              : {
     609              :     int         i;
     610              : 
     611       550540 :     if (!lo_cleanup_needed)
     612       550249 :         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          291 :     if (isCommit)
     622              :     {
     623         4672 :         for (i = 0; i < cookies_size; i++)
     624              :         {
     625         4480 :             if (cookies[i] != NULL)
     626           45 :                 closeLOfd(i);
     627              :         }
     628              :     }
     629              : 
     630              :     /* Needn't actually pfree since we're about to zap context */
     631          291 :     cookies = NULL;
     632          291 :     cookies_size = 0;
     633              : 
     634              :     /* Release the LO memory context to prevent permanent memory leaks. */
     635          291 :     if (fscxt)
     636          152 :         MemoryContextDelete(fscxt);
     637          291 :     fscxt = NULL;
     638              : 
     639              :     /* Give inv_api.c a chance to clean up, too */
     640          291 :     close_lo_relation(isCommit);
     641              : 
     642          291 :     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        10085 : AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
     654              :                         SubTransactionId parentSubid)
     655              : {
     656              :     int         i;
     657              : 
     658        10085 :     if (fscxt == NULL)          /* no LO operations in this xact */
     659        10085 :         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          190 : newLOfd(void)
     681              : {
     682              :     int         i,
     683              :                 newsize;
     684              : 
     685          190 :     lo_cleanup_needed = true;
     686          190 :     if (fscxt == NULL)
     687          152 :         fscxt = AllocSetContextCreate(TopMemoryContext,
     688              :                                       "Filesystem",
     689              :                                       ALLOCSET_DEFAULT_SIZES);
     690              : 
     691              :     /* Try to find a free slot */
     692          190 :     for (i = 0; i < cookies_size; i++)
     693              :     {
     694           38 :         if (cookies[i] == NULL)
     695           38 :             return i;
     696              :     }
     697              : 
     698              :     /* No free slot, so make the array bigger */
     699          152 :     if (cookies_size <= 0)
     700              :     {
     701              :         /* First time through, arbitrarily make 64-element array */
     702          152 :         i = 0;
     703          152 :         newsize = 64;
     704          152 :         cookies = (LargeObjectDesc **)
     705          152 :             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          152 :     cookies_size = newsize;
     716              : 
     717          152 :     return i;
     718              : }
     719              : 
     720              : static void
     721          151 : 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          151 :     lobj = cookies[fd];
     730          151 :     cookies[fd] = NULL;
     731              : 
     732          151 :     if (lobj->snapshot)
     733          105 :         UnregisterSnapshotFromOwner(lobj->snapshot,
     734              :                                     TopTransactionResourceOwner);
     735          151 :     inv_close(lobj);
     736          151 : }
     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           59 : 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           59 :     bytea      *result = NULL;
     753              : 
     754           59 :     lo_cleanup_needed = true;
     755           59 :     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           54 :     loSize = inv_seek(loDesc, 0, SEEK_END);
     762           54 :     if (loSize > offset)
     763              :     {
     764           42 :         if (nbytes >= 0 && nbytes <= loSize - offset)
     765            9 :             result_length = nbytes; /* request is wholly inside LO */
     766              :         else
     767           33 :             result_length = loSize - offset;    /* adjust to end of LO */
     768              :     }
     769              :     else
     770           12 :         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           54 :     if (result_length > MaxAllocSize - VARHDRSZ)
     777            3 :         ereport(ERROR,
     778              :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     779              :                  errmsg("large object read request is too large")));
     780              : 
     781           51 :     result = (bytea *) palloc(VARHDRSZ + result_length);
     782              : 
     783           51 :     inv_seek(loDesc, offset, SEEK_SET);
     784           51 :     total_read = inv_read(loDesc, VARDATA(result), result_length);
     785              :     Assert(total_read == result_length);
     786           51 :     SET_VARSIZE(result, result_length + VARHDRSZ);
     787              : 
     788           51 :     inv_close(loDesc);
     789              : 
     790           51 :     return result;
     791              : }
     792              : 
     793              : /*
     794              :  * Read entire LO
     795              :  */
     796              : Datum
     797           47 : be_lo_get(PG_FUNCTION_ARGS)
     798              : {
     799           47 :     Oid         loOid = PG_GETARG_OID(0);
     800              :     bytea      *result;
     801              : 
     802           47 :     result = lo_get_fragment_internal(loOid, 0, -1);
     803              : 
     804           39 :     PG_RETURN_BYTEA_P(result);
     805              : }
     806              : 
     807              : /*
     808              :  * Read range within LO
     809              :  */
     810              : Datum
     811           12 : be_lo_get_fragment(PG_FUNCTION_ARGS)
     812              : {
     813           12 :     Oid         loOid = PG_GETARG_OID(0);
     814           12 :     int64       offset = PG_GETARG_INT64(1);
     815           12 :     int32       nbytes = PG_GETARG_INT32(2);
     816              :     bytea      *result;
     817              : 
     818           12 :     if (nbytes < 0)
     819            0 :         ereport(ERROR,
     820              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     821              :                  errmsg("requested length cannot be negative")));
     822              : 
     823           12 :     result = lo_get_fragment_internal(loOid, offset, nbytes);
     824              : 
     825           12 :     PG_RETURN_BYTEA_P(result);
     826              : }
     827              : 
     828              : /*
     829              :  * Create LO with initial contents given by a bytea argument
     830              :  */
     831              : Datum
     832           29 : be_lo_from_bytea(PG_FUNCTION_ARGS)
     833              : {
     834           29 :     Oid         loOid = PG_GETARG_OID(0);
     835           29 :     bytea      *str = PG_GETARG_BYTEA_PP(1);
     836              :     LargeObjectDesc *loDesc;
     837              :     int         written PG_USED_FOR_ASSERTS_ONLY;
     838              : 
     839           29 :     PreventCommandIfReadOnly("lo_from_bytea()");
     840              : 
     841           26 :     lo_cleanup_needed = true;
     842           26 :     loOid = inv_create(loOid);
     843           26 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
     844           26 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
     845              :     Assert(written == VARSIZE_ANY_EXHDR(str));
     846           26 :     inv_close(loDesc);
     847              : 
     848           26 :     PG_RETURN_OID(loOid);
     849              : }
     850              : 
     851              : /*
     852              :  * Update range within LO
     853              :  */
     854              : Datum
     855           18 : be_lo_put(PG_FUNCTION_ARGS)
     856              : {
     857           18 :     Oid         loOid = PG_GETARG_OID(0);
     858           18 :     int64       offset = PG_GETARG_INT64(1);
     859           18 :     bytea      *str = PG_GETARG_BYTEA_PP(2);
     860              :     LargeObjectDesc *loDesc;
     861              :     int         written PG_USED_FOR_ASSERTS_ONLY;
     862              : 
     863           18 :     PreventCommandIfReadOnly("lo_put()");
     864              : 
     865           15 :     lo_cleanup_needed = true;
     866           15 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
     867            9 :     inv_seek(loDesc, offset, SEEK_SET);
     868            9 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
     869              :     Assert(written == VARSIZE_ANY_EXHDR(str));
     870            9 :     inv_close(loDesc);
     871              : 
     872            9 :     PG_RETURN_VOID();
     873              : }
        

Generated by: LCOV version 2.0-1