LCOV - differential code coverage report
Current view: top level - src/backend/libpq - be-fsstubs.c (source / functions) Coverage Total Hit UNC LBC UIC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 86.3 % 277 239 2 12 24 12 145 13 69 26 155 3
Current Date: 2023-04-08 17:13:01 Functions: 96.6 % 29 28 1 28 1 28
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (120,180] days: 50.0 % 4 2 2 2
Legend: Lines: hit not hit (240..) days: 86.8 % 273 237 12 24 12 145 11 69 25 147
Function coverage date bins:
(240..) days: 48.3 % 58 28 1 28 1 28

 Age         Owner                  TLA  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-2023, 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_metadata.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
 2294 peter_e                    87 GIC         177 : be_lo_open(PG_FUNCTION_ARGS)
                                 88                 : {
 8339 tgl                        89 CBC         177 :     Oid         lobjId = PG_GETARG_OID(0);
 8339 tgl                        90 GIC         177 :     int32       mode = PG_GETARG_INT32(1);
 9345 bruce                      91 ECB             :     LargeObjectDesc *lobjDesc;
 9344                            92                 :     int         fd;
                                 93                 : 
                                 94                 : #ifdef FSDB
                                 95                 :     elog(DEBUG4, "lo_open(%u,%d)", lobjId, mode);
                                 96                 : #endif
                                 97                 : 
  279 michael                    98 GNC         177 :     if (mode & INV_WRITE)
                                 99              58 :         PreventCommandIfReadOnly("lo_open(INV_WRITE)");
                                100                 : 
                                101                 :     /*
                                102                 :      * Allocate a large object descriptor first.  This will also create
  522 heikki.linnakangas        103 ECB             :      * 'fscxt' if this is the first LO opened in this transaction.
                                104                 :      */
  522 heikki.linnakangas        105 GIC         174 :     fd = newLOfd();
                                106                 : 
 6192 tgl                       107             174 :     lobjDesc = inv_open(lobjId, mode, fscxt);
  522 heikki.linnakangas        108             150 :     lobjDesc->subid = GetCurrentSubTransactionId();
                                109                 : 
  522 heikki.linnakangas        110 ECB             :     /*
                                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                 :      */
  522 heikki.linnakangas        115 GIC         150 :     if (lobjDesc->snapshot)
                                116             110 :         lobjDesc->snapshot = RegisterSnapshotOnOwner(lobjDesc->snapshot,
                                117                 :                                                      TopTransactionResourceOwner);
                                118                 : 
                                119             150 :     Assert(cookies[fd] == NULL);
  522 heikki.linnakangas        120 CBC         150 :     cookies[fd] = lobjDesc;
 9770 scrappy                   121 ECB             : 
 8339 tgl                       122 GIC         150 :     PG_RETURN_INT32(fd);
                                123                 : }
 9770 scrappy                   124 ECB             : 
 8339 tgl                       125                 : Datum
 2294 peter_e                   126 GIC         105 : be_lo_close(PG_FUNCTION_ARGS)
 9770 scrappy                   127 ECB             : {
 8339 tgl                       128 GIC         105 :     int32       fd = PG_GETARG_INT32(0);
                                129                 : 
 8202                           130             105 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 7201 tgl                       131 LBC           0 :         ereport(ERROR,
                                132                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
 7201 tgl                       133 ECB             :                  errmsg("invalid large-object descriptor: %d", fd)));
                                134                 : 
 1268 peter                     135                 : #ifdef FSDB
 7257 bruce                     136 EUB             :     elog(DEBUG4, "lo_close(%d)", fd);
                                137                 : #endif
                                138                 : 
  522 heikki.linnakangas        139 GIC         105 :     closeLOfd(fd);
                                140                 : 
 8339 tgl                       141             105 :     PG_RETURN_INT32(0);
                                142                 : }
                                143                 : 
 8339 tgl                       144 ECB             : 
                                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
 9770 scrappy                   154 GIC         413 : lo_read(int fd, char *buf, int len)
                                155                 : {
                                156                 :     int         status;
                                157                 :     LargeObjectDesc *lobj;
                                158                 : 
 8202 tgl                       159 CBC         413 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 7201 tgl                       160 UIC           0 :         ereport(ERROR,
                                161                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                162                 :                  errmsg("invalid large-object descriptor: %d", fd)));
 3834 tgl                       163 GIC         413 :     lobj = cookies[fd];
 8714 tgl                       164 ECB             : 
 1977 tgl                       165 EUB             :     /*
                                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
 1977 tgl                       168 ECB             :      * be that the privilege exists but user forgot to ask for read mode.
                                169                 :      */
 1977 tgl                       170 GIC         413 :     if ((lobj->flags & IFS_RDLOCK) == 0)
 1977 tgl                       171 UIC           0 :         ereport(ERROR,
                                172                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                173                 :                  errmsg("large object descriptor %d was not opened for reading",
                                174                 :                         fd)));
 4867 itagaki.takahiro          175 ECB             : 
 3834 tgl                       176 GBC         413 :     status = inv_read(lobj, buf, len);
                                177                 : 
 8339 tgl                       178 GIC         413 :     return status;
                                179                 : }
                                180                 : 
 9770 scrappy                   181 ECB             : int
 6058 bruce                     182 GIC         517 : lo_write(int fd, const char *buf, int len)
 9770 scrappy                   183 ECB             : {
                                184                 :     int         status;
                                185                 :     LargeObjectDesc *lobj;
                                186                 : 
 8202 tgl                       187 CBC         517 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 7201 tgl                       188 UIC           0 :         ereport(ERROR,
                                189                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                190                 :                  errmsg("invalid large-object descriptor: %d", fd)));
 3834 tgl                       191 GIC         517 :     lobj = cookies[fd];
 8714 tgl                       192 ECB             : 
 1977 tgl                       193 EUB             :     /* see comment in lo_read() */
 3834 tgl                       194 GIC         517 :     if ((lobj->flags & IFS_WRLOCK) == 0)
 6509                           195               3 :         ereport(ERROR,
 6509 tgl                       196 ECB             :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                197                 :                  errmsg("large object descriptor %d was not opened for writing",
                                198                 :                         fd)));
                                199                 : 
 3834 tgl                       200 CBC         514 :     status = inv_write(lobj, buf, len);
                                201                 : 
 8339 tgl                       202 GIC         514 :     return status;
                                203                 : }
                                204                 : 
 8339 tgl                       205 ECB             : Datum
 2294 peter_e                   206 GIC          27 : be_lo_lseek(PG_FUNCTION_ARGS)
 9770 scrappy                   207 ECB             : {
 8339 tgl                       208 GIC          27 :     int32       fd = PG_GETARG_INT32(0);
                                209              27 :     int32       offset = PG_GETARG_INT32(1);
                                210              27 :     int32       whence = PG_GETARG_INT32(2);
 3836 ishii                     211 ECB             :     int64       status;
                                212                 : 
 8202 tgl                       213 CBC          27 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 7201 tgl                       214 LBC           0 :         ereport(ERROR,
 7201 tgl                       215 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                216                 :                  errmsg("invalid large-object descriptor: %d", fd)));
                                217                 : 
 8714 tgl                       218 CBC          27 :     status = inv_seek(cookies[fd], offset, whence);
 9518 scrappy                   219 EUB             : 
                                220                 :     /* guard against result overflow */
 3835 tgl                       221 GIC          27 :     if (status != (int32) status)
 3836 ishii                     222 UIC           0 :         ereport(ERROR,
 3835 tgl                       223 ECB             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                224                 :                  errmsg("lo_lseek result out of range for large-object descriptor %d",
                                225                 :                         fd)));
 3836 ishii                     226                 : 
 3835 tgl                       227 GBC          27 :     PG_RETURN_INT32((int32) status);
                                228                 : }
                                229                 : 
                                230                 : Datum
 2294 peter_e                   231 GIC          12 : be_lo_lseek64(PG_FUNCTION_ARGS)
 3836 ishii                     232 ECB             : {
 3836 ishii                     233 GIC          12 :     int32       fd = PG_GETARG_INT32(0);
                                234              12 :     int64       offset = PG_GETARG_INT64(1);
                                235              12 :     int32       whence = PG_GETARG_INT32(2);
 3835 tgl                       236 ECB             :     int64       status;
                                237                 : 
 3836 ishii                     238 CBC          12 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 3836 ishii                     239 LBC           0 :         ereport(ERROR,
 3836 ishii                     240 ECB             :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                241                 :                  errmsg("invalid large-object descriptor: %d", fd)));
                                242                 : 
 3836 ishii                     243 CBC          12 :     status = inv_seek(cookies[fd], offset, whence);
 3836 ishii                     244 EUB             : 
 3836 ishii                     245 GIC          12 :     PG_RETURN_INT64(status);
                                246                 : }
                                247                 : 
 8339 tgl                       248 ECB             : Datum
 2294 peter_e                   249 GIC          13 : be_lo_creat(PG_FUNCTION_ARGS)
 9770 scrappy                   250 ECB             : {
                                251                 :     Oid         lobjId;
                                252                 : 
  279 michael                   253 GNC          13 :     PreventCommandIfReadOnly("lo_creat()");
                                254                 : 
  522 heikki.linnakangas        255 GIC          10 :     lo_cleanup_needed = true;
 6509 tgl                       256 CBC          10 :     lobjId = inv_create(InvalidOid);
                                257                 : 
 6509 tgl                       258 GIC          10 :     PG_RETURN_OID(lobjId);
                                259                 : }
 6509 tgl                       260 ECB             : 
                                261                 : Datum
 2294 peter_e                   262 CBC          36 : be_lo_create(PG_FUNCTION_ARGS)
 6509 tgl                       263 ECB             : {
 6509 tgl                       264 GIC          36 :     Oid         lobjId = PG_GETARG_OID(0);
 9345 bruce                     265 ECB             : 
  279 michael                   266 GNC          36 :     PreventCommandIfReadOnly("lo_create()");
                                267                 : 
  522 heikki.linnakangas        268 GIC          33 :     lo_cleanup_needed = true;
 6509 tgl                       269              33 :     lobjId = inv_create(lobjId);
                                270                 : 
 8339 tgl                       271 CBC          33 :     PG_RETURN_OID(lobjId);
                                272                 : }
 9770 scrappy                   273 ECB             : 
                                274                 : Datum
 2294 peter_e                   275 CBC          12 : be_lo_tell(PG_FUNCTION_ARGS)
                                276                 : {
 8339 tgl                       277              12 :     int32       fd = PG_GETARG_INT32(0);
 3835 tgl                       278 ECB             :     int64       offset;
                                279                 : 
 8202 tgl                       280 CBC          12 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 7201 tgl                       281 UIC           0 :         ereport(ERROR,
                                282                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                283                 :                  errmsg("invalid large-object descriptor: %d", fd)));
 8714 tgl                       284 ECB             : 
 3836 ishii                     285 GIC          12 :     offset = inv_tell(cookies[fd]);
 3836 ishii                     286 ECB             : 
                                287                 :     /* guard against result overflow */
 3835 tgl                       288 GIC          12 :     if (offset != (int32) offset)
 3836 ishii                     289 LBC           0 :         ereport(ERROR,
 3835 tgl                       290 EUB             :                 (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
                                291                 :                  errmsg("lo_tell result out of range for large-object descriptor %d",
                                292                 :                         fd)));
                                293                 : 
 3835 tgl                       294 CBC          12 :     PG_RETURN_INT32((int32) offset);
                                295                 : }
                                296                 : 
 3836 ishii                     297 ECB             : Datum
 2294 peter_e                   298 GBC          12 : be_lo_tell64(PG_FUNCTION_ARGS)
                                299                 : {
 3836 ishii                     300 GIC          12 :     int32       fd = PG_GETARG_INT32(0);
                                301                 :     int64       offset;
                                302                 : 
 3836 ishii                     303 CBC          12 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 3836 ishii                     304 UIC           0 :         ereport(ERROR,
                                305                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                306                 :                  errmsg("invalid large-object descriptor: %d", fd)));
 3836 ishii                     307 ECB             : 
 3835 tgl                       308 GIC          12 :     offset = inv_tell(cookies[fd]);
 3835 tgl                       309 ECB             : 
 3835 tgl                       310 GIC          12 :     PG_RETURN_INT64(offset);
                                311                 : }
 9770 scrappy                   312 ECB             : 
 8339 tgl                       313 EUB             : Datum
 2294 peter_e                   314 GIC          50 : be_lo_unlink(PG_FUNCTION_ARGS)
                                315                 : {
 8339 tgl                       316              50 :     Oid         lobjId = PG_GETARG_OID(0);
 8397 bruce                     317 ECB             : 
  279 michael                   318 GNC          50 :     PreventCommandIfReadOnly("lo_unlink()");
                                319                 : 
                                320                 :     /*
 1977 tgl                       321 ECB             :      * Must be owner of the large object.  It would be cleaner to check this
                                322                 :      * in inv_drop(), but we want to throw the error before not after closing
                                323                 :      * relevant FDs.
                                324                 :      */
 4867 itagaki.takahiro          325 CBC          47 :     if (!lo_compat_privileges &&
  147 peter                     326 GNC          44 :         !object_ownercheck(LargeObjectMetadataRelationId, lobjId, GetUserId()))
 4867 itagaki.takahiro          327 CBC           6 :         ereport(ERROR,
                                328                 :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
 4867 itagaki.takahiro          329 ECB             :                  errmsg("must be owner of large object %u", lobjId)));
                                330                 : 
                                331                 :     /*
                                332                 :      * If there are any open LO FDs referencing that ID, close 'em.
                                333                 :      */
 8202 tgl                       334 GIC          41 :     if (fscxt != NULL)
                                335                 :     {
 8202 tgl                       336 ECB             :         int         i;
                                337                 : 
 8202 tgl                       338 LBC           0 :         for (i = 0; i < cookies_size; i++)
                                339                 :         {
 8202 tgl                       340 UIC           0 :             if (cookies[i] != NULL && cookies[i]->id == lobjId)
  522 heikki.linnakangas        341               0 :                 closeLOfd(i);
                                342                 :         }
                                343                 :     }
                                344                 : 
 8714 tgl                       345 ECB             :     /*
                                346                 :      * inv_drop does not create a need for end-of-transaction cleanup and
                                347                 :      * hence we don't need to set lo_cleanup_needed.
                                348                 :      */
 8339 tgl                       349 GBC          41 :     PG_RETURN_INT32(inv_drop(lobjId));
                                350                 : }
 9770 scrappy                   351 EUB             : 
                                352                 : /*****************************************************************************
                                353                 :  *  Read/Write using bytea
                                354                 :  *****************************************************************************/
                                355                 : 
                                356                 : Datum
 2294 peter_e                   357 GIC         413 : be_loread(PG_FUNCTION_ARGS)
                                358                 : {
 8339 tgl                       359             413 :     int32       fd = PG_GETARG_INT32(0);
 8339 tgl                       360 CBC         413 :     int32       len = PG_GETARG_INT32(1);
                                361                 :     bytea      *retval;
                                362                 :     int         totalread;
                                363                 : 
 8339 tgl                       364 GIC         413 :     if (len < 0)
 8339 tgl                       365 UIC           0 :         len = 0;
                                366                 : 
 7532 tgl                       367 GIC         413 :     retval = (bytea *) palloc(VARHDRSZ + len);
 9345 bruce                     368 CBC         413 :     totalread = lo_read(fd, VARDATA(retval), len);
 5885 tgl                       369 GIC         413 :     SET_VARSIZE(retval, totalread + VARHDRSZ);
 9345 bruce                     370 ECB             : 
 7532 tgl                       371 CBC         413 :     PG_RETURN_BYTEA_P(retval);
                                372                 : }
                                373                 : 
                                374                 : Datum
 2294 peter_e                   375             520 : be_lowrite(PG_FUNCTION_ARGS)
 9770 scrappy                   376 EUB             : {
 8053 bruce                     377 GIC         520 :     int32       fd = PG_GETARG_INT32(0);
 2219 noah                      378 CBC         520 :     bytea      *wbuf = PG_GETARG_BYTEA_PP(1);
 8053 bruce                     379 ECB             :     int         bytestowrite;
                                380                 :     int         totalwritten;
                                381                 : 
  279 michael                   382 GNC         520 :     PreventCommandIfReadOnly("lowrite()");
                                383                 : 
 2219 noah                      384 CBC         517 :     bytestowrite = VARSIZE_ANY_EXHDR(wbuf);
 2219 noah                      385 GIC         517 :     totalwritten = lo_write(fd, VARDATA_ANY(wbuf), bytestowrite);
 8339 tgl                       386             514 :     PG_RETURN_INT32(totalwritten);
                                387                 : }
 9770 scrappy                   388 ECB             : 
                                389                 : /*****************************************************************************
 9345 bruce                     390                 :  *   Import/Export of Large Object
 9770 scrappy                   391                 :  *****************************************************************************/
                                392                 : 
                                393                 : /*
                                394                 :  * lo_import -
 9345 bruce                     395                 :  *    imports a file as an (inversion) large object.
                                396                 :  */
 8339 tgl                       397                 : Datum
 2294 peter_e                   398 CBC           6 : be_lo_import(PG_FUNCTION_ARGS)
 9770 scrappy                   399 ECB             : {
 5493 tgl                       400 GIC           6 :     text       *filename = PG_GETARG_TEXT_PP(0);
                                401                 : 
 5496 ishii                     402               6 :     PG_RETURN_OID(lo_import_internal(filename, InvalidOid));
                                403                 : }
                                404                 : 
                                405                 : /*
                                406                 :  * lo_import_with_oid -
                                407                 :  *    imports a file as an (inversion) large object specifying oid.
                                408                 :  */
                                409                 : Datum
 2294 peter_e                   410 UIC           0 : be_lo_import_with_oid(PG_FUNCTION_ARGS)
 5496 ishii                     411 ECB             : {
 5493 tgl                       412 UIC           0 :     text       *filename = PG_GETARG_TEXT_PP(0);
 5050 bruce                     413 LBC           0 :     Oid         oid = PG_GETARG_OID(1);
                                414                 : 
 5496 ishii                     415               0 :     PG_RETURN_OID(lo_import_internal(filename, oid));
                                416                 : }
                                417                 : 
                                418                 : static Oid
 5496 ishii                     419 GIC           6 : lo_import_internal(text *filename, Oid lobjOid)
                                420                 : {
                                421                 :     int         fd;
                                422                 :     int         nbytes,
 4036 peter_e                   423 EUB             :                 tmp PG_USED_FOR_ASSERTS_ONLY;
                                424                 :     char        buf[BUFSIZE];
 8202 tgl                       425                 :     char        fnamebuf[MAXPGPATH];
 9345 bruce                     426                 :     LargeObjectDesc *lobj;
                                427                 :     Oid         oid;
 5050                           428                 : 
  279 michael                   429 GNC           6 :     PreventCommandIfReadOnly("lo_import()");
                                430                 : 
                                431                 :     /*
                                432                 :      * open the file to be read in
                                433                 :      */
 5493 tgl                       434 CBC           3 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
 2024 peter_e                   435 GIC           3 :     fd = OpenTransientFile(fnamebuf, O_RDONLY | PG_BINARY);
 9345 bruce                     436               3 :     if (fd < 0)
 7201 tgl                       437 UIC           0 :         ereport(ERROR,
                                438                 :                 (errcode_for_file_access(),
                                439                 :                  errmsg("could not open server file \"%s\": %m",
                                440                 :                         fnamebuf)));
                                441                 : 
                                442                 :     /*
                                443                 :      * create an inversion object
 9345 bruce                     444 ECB             :      */
  522 heikki.linnakangas        445 GIC           3 :     lo_cleanup_needed = true;
 5496 ishii                     446               3 :     oid = inv_create(lobjOid);
                                447                 : 
                                448                 :     /*
 6509 tgl                       449 ECB             :      * read in from the filesystem and write to the inversion object
 9345 bruce                     450                 :      */
  522 heikki.linnakangas        451 CBC           3 :     lobj = inv_open(oid, INV_WRITE, CurrentMemoryContext);
 6509 tgl                       452 EUB             : 
 3785 heikki.linnakangas        453 GIC         249 :     while ((nbytes = read(fd, buf, BUFSIZE)) > 0)
                                454                 :     {
 9345 bruce                     455             246 :         tmp = inv_write(lobj, buf, nbytes);
 7201 tgl                       456             246 :         Assert(tmp == nbytes);
                                457                 :     }
                                458                 : 
                                459               3 :     if (nbytes < 0)
 7201 tgl                       460 LBC           0 :         ereport(ERROR,
 7201 tgl                       461 ECB             :                 (errcode_for_file_access(),
                                462                 :                  errmsg("could not read server file \"%s\": %m",
                                463                 :                         fnamebuf)));
                                464                 : 
 9345 bruce                     465 GIC           3 :     inv_close(lobj);
 1492 michael                   466 ECB             : 
 1373 peter                     467 GIC           3 :     if (CloseTransientFile(fd) != 0)
 1492 michael                   468 LBC           0 :         ereport(ERROR,
                                469                 :                 (errcode_for_file_access(),
 1492 michael                   470 ECB             :                  errmsg("could not close file \"%s\": %m",
                                471                 :                         fnamebuf)));
                                472                 : 
 5496 ishii                     473 GIC           3 :     return oid;
 9770 scrappy                   474 ECB             : }
 9770 scrappy                   475 EUB             : 
                                476                 : /*
                                477                 :  * lo_export -
                                478                 :  *    exports an (inversion) large object.
                                479                 :  */
 8339 tgl                       480 ECB             : Datum
 2294 peter_e                   481 GIC           6 : be_lo_export(PG_FUNCTION_ARGS)
 9770 scrappy                   482 ECB             : {
 8339 tgl                       483 GBC           6 :     Oid         lobjId = PG_GETARG_OID(0);
 5493 tgl                       484 GIC           6 :     text       *filename = PG_GETARG_TEXT_PP(1);
                                485                 :     int         fd;
                                486                 :     int         nbytes,
                                487                 :                 tmp;
 9344 bruce                     488 ECB             :     char        buf[BUFSIZE];
                                489                 :     char        fnamebuf[MAXPGPATH];
                                490                 :     LargeObjectDesc *lobj;
                                491                 :     mode_t      oumask;
                                492                 : 
                                493                 :     /*
                                494                 :      * open the inversion object (no need to test for failure)
                                495                 :      */
  522 heikki.linnakangas        496 CBC           6 :     lo_cleanup_needed = true;
  522 heikki.linnakangas        497 GIC           6 :     lobj = inv_open(lobjId, INV_READ, CurrentMemoryContext);
 9345 bruce                     498 ECB             : 
                                499                 :     /*
                                500                 :      * open the file to be written to
                                501                 :      *
                                502                 :      * Note: we reduce backend's normal 077 umask to the slightly friendlier
                                503                 :      * 022. This code used to drop it all the way to 0, but creating
                                504                 :      * world-writable export files doesn't seem wise.
                                505                 :      */
 5493 tgl                       506 GIC           6 :     text_to_cstring_buffer(filename, fnamebuf, sizeof(fnamebuf));
 4503                           507               6 :     oumask = umask(S_IWGRP | S_IWOTH);
 2025 peter_e                   508               6 :     PG_TRY();
                                509                 :     {
 2024                           510               6 :         fd = OpenTransientFilePerm(fnamebuf, O_CREAT | O_WRONLY | O_TRUNC | PG_BINARY,
 2024 peter_e                   511 ECB             :                                    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 2025                           512                 :     }
 1255 peter                     513 UIC           0 :     PG_FINALLY();
                                514                 :     {
 2025 peter_e                   515 GIC           6 :         umask(oumask);
                                516                 :     }
                                517               6 :     PG_END_TRY();
 9345 bruce                     518               6 :     if (fd < 0)
 7201 tgl                       519               3 :         ereport(ERROR,
                                520                 :                 (errcode_for_file_access(),
 7201 tgl                       521 ECB             :                  errmsg("could not create server file \"%s\": %m",
                                522                 :                         fnamebuf)));
 9770 scrappy                   523                 : 
                                524                 :     /*
 6829 tgl                       525                 :      * read in from the inversion file and write to the filesystem
                                526                 :      */
 9345 bruce                     527 GIC         249 :     while ((nbytes = inv_read(lobj, buf, BUFSIZE)) > 0)
 9345 bruce                     528 EUB             :     {
 3785 heikki.linnakangas        529 GIC         246 :         tmp = write(fd, buf, nbytes);
 8202 tgl                       530 CBC         246 :         if (tmp != nbytes)
 7201 tgl                       531 UIC           0 :             ereport(ERROR,
 7201 tgl                       532 ECB             :                     (errcode_for_file_access(),
                                533                 :                      errmsg("could not write server file \"%s\": %m",
                                534                 :                             fnamebuf)));
                                535                 :     }
                                536                 : 
 1373 peter                     537 GIC           3 :     if (CloseTransientFile(fd) != 0)
 1492 michael                   538 UIC           0 :         ereport(ERROR,
                                539                 :                 (errcode_for_file_access(),
                                540                 :                  errmsg("could not close file \"%s\": %m",
                                541                 :                         fnamebuf)));
 1492 michael                   542 ECB             : 
 7201 tgl                       543 GIC           3 :     inv_close(lobj);
 9770 scrappy                   544 ECB             : 
 8339 tgl                       545 CBC           3 :     PG_RETURN_INT32(1);
 9770 scrappy                   546 EUB             : }
                                547                 : 
                                548                 : /*
                                549                 :  * lo_truncate -
                                550                 :  *    truncate a large object to a specified length
                                551                 :  */
 3834 tgl                       552 ECB             : static void
 3834 tgl                       553 GBC          21 : lo_truncate_internal(int32 fd, int64 len)
                                554                 : {
                                555                 :     LargeObjectDesc *lobj;
                                556                 : 
 5881 bruce                     557 GIC          21 :     if (fd < 0 || fd >= cookies_size || cookies[fd] == NULL)
 5881 bruce                     558 LBC           0 :         ereport(ERROR,
                                559                 :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
 5881 bruce                     560 ECB             :                  errmsg("invalid large-object descriptor: %d", fd)));
 3834 tgl                       561 GIC          21 :     lobj = cookies[fd];
                                562                 : 
                                563                 :     /* see comment in lo_read() */
                                564              21 :     if ((lobj->flags & IFS_WRLOCK) == 0)
 3836 ishii                     565 UIC           0 :         ereport(ERROR,
                                566                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
                                567                 :                  errmsg("large object descriptor %d was not opened for writing",
 2118 tgl                       568 ECB             :                         fd)));
                                569                 : 
 3834 tgl                       570 GIC          21 :     inv_truncate(lobj, len);
                                571              21 : }
 3836 ishii                     572 ECB             : 
 3834 tgl                       573 EUB             : Datum
 2294 peter_e                   574 GIC          18 : be_lo_truncate(PG_FUNCTION_ARGS)
                                575                 : {
 3834 tgl                       576 CBC          18 :     int32       fd = PG_GETARG_INT32(0);
 3834 tgl                       577 GIC          18 :     int32       len = PG_GETARG_INT32(1);
                                578                 : 
  279 michael                   579 GNC          18 :     PreventCommandIfReadOnly("lo_truncate()");
                                580                 : 
 3834 tgl                       581 CBC          15 :     lo_truncate_internal(fd, len);
 3836 ishii                     582 GBC          15 :     PG_RETURN_INT32(0);
                                583                 : }
                                584                 : 
                                585                 : Datum
 2294 peter_e                   586 GIC           9 : be_lo_truncate64(PG_FUNCTION_ARGS)
 3836 ishii                     587 ECB             : {
 3836 ishii                     588 CBC           9 :     int32       fd = PG_GETARG_INT32(0);
 3836 ishii                     589 GIC           9 :     int64       len = PG_GETARG_INT64(1);
                                590                 : 
  279 michael                   591 GNC           9 :     PreventCommandIfReadOnly("lo_truncate64()");
                                592                 : 
 3834 tgl                       593 CBC           6 :     lo_truncate_internal(fd, len);
 5881 bruce                     594 GIC           6 :     PG_RETURN_INT32(0);
 5881 bruce                     595 ECB             : }
                                596                 : 
                                597                 : /*
 6829 tgl                       598                 :  * AtEOXact_LargeObject -
                                599                 :  *       prepares large objects for transaction commit
 9028 bruce                     600                 :  */
 8986                           601                 : void
 6829 tgl                       602 GIC      486069 : AtEOXact_LargeObject(bool isCommit)
                                603                 : {
                                604                 :     int         i;
 9028 bruce                     605 ECB             : 
  522 heikki.linnakangas        606 GIC      486069 :     if (!lo_cleanup_needed)
 8714 tgl                       607 CBC      485846 :         return;                 /* no LO operations in this xact */
 8986 bruce                     608 ECB             : 
                                609                 :     /*
 6385                           610                 :      * Close LO fds and clear cookies array so that LO fds are no longer good.
                                611                 :      * The memory context and resource owner holding them are going away at
  522 heikki.linnakangas        612                 :      * the end-of-transaction anyway, but on commit, we need to close them to
                                613                 :      * avoid warnings about leaked resources at commit.  On abort we can skip
                                614                 :      * this step.
                                615                 :      */
  522 heikki.linnakangas        616 GIC         223 :     if (isCommit)
                                617                 :     {
                                618            4048 :         for (i = 0; i < cookies_size; i++)
                                619                 :         {
                                620            3904 :             if (cookies[i] != NULL)
  522 heikki.linnakangas        621 CBC          36 :                 closeLOfd(i);
                                622                 :         }
                                623                 :     }
                                624                 : 
 8202 tgl                       625 ECB             :     /* Needn't actually pfree since we're about to zap context */
 8202 tgl                       626 CBC         223 :     cookies = NULL;
 8202 tgl                       627 GIC         223 :     cookies_size = 0;
                                628                 : 
                                629                 :     /* Release the LO memory context to prevent permanent memory leaks. */
  522 heikki.linnakangas        630             223 :     if (fscxt)
                                631             132 :         MemoryContextDelete(fscxt);
 8714 tgl                       632             223 :     fscxt = NULL;
                                633                 : 
                                634                 :     /* Give inv_api.c a chance to clean up, too */
 6829 tgl                       635 CBC         223 :     close_lo_relation(isCommit);
                                636                 : 
  522 heikki.linnakangas        637             223 :     lo_cleanup_needed = false;
                                638                 : }
 9028 bruce                     639 ECB             : 
 6829 tgl                       640                 : /*
                                641                 :  * AtEOSubXact_LargeObject
                                642                 :  *      Take care of large objects at subtransaction commit/abort
                                643                 :  *
                                644                 :  * Reassign LOs created/opened during a committing subtransaction
 6779                           645                 :  * to the parent subtransaction.  On abort, just close them.
 6829                           646                 :  */
                                647                 : void
 6779 tgl                       648 GIC        8786 : AtEOSubXact_LargeObject(bool isCommit, SubTransactionId mySubid,
 6779 tgl                       649 ECB             :                         SubTransactionId parentSubid)
 6829                           650                 : {
 6797 bruce                     651                 :     int         i;
                                652                 : 
 6829 tgl                       653 GIC        8786 :     if (fscxt == NULL)          /* no LO operations in this xact */
 6829 tgl                       654 CBC        8786 :         return;
                                655                 : 
 6829 tgl                       656 LBC           0 :     for (i = 0; i < cookies_size; i++)
                                657                 :     {
 6829 tgl                       658 UIC           0 :         LargeObjectDesc *lo = cookies[i];
                                659                 : 
 6779                           660               0 :         if (lo != NULL && lo->subid == mySubid)
                                661                 :         {
 6829                           662               0 :             if (isCommit)
 6779                           663               0 :                 lo->subid = parentSubid;
                                664                 :             else
  522 heikki.linnakangas        665               0 :                 closeLOfd(i);
                                666                 :         }
 6829 tgl                       667 ECB             :     }
                                668                 : }
                                669                 : 
                                670                 : /*****************************************************************************
                                671                 :  *  Support routines for this file
 9770 scrappy                   672                 :  *****************************************************************************/
                                673                 : 
                                674                 : static int
  522 heikki.linnakangas        675 GBC         174 : newLOfd(void)
                                676                 : {
 8202 tgl                       677 EUB             :     int         i,
                                678                 :                 newsize;
 9345 bruce                     679                 : 
  522 heikki.linnakangas        680 GIC         174 :     lo_cleanup_needed = true;
  522 heikki.linnakangas        681 GBC         174 :     if (fscxt == NULL)
                                682             132 :         fscxt = AllocSetContextCreate(TopMemoryContext,
                                683                 :                                       "Filesystem",
  522 heikki.linnakangas        684 EUB             :                                       ALLOCSET_DEFAULT_SIZES);
                                685                 : 
                                686                 :     /* Try to find a free slot */
 8202 tgl                       687 GIC         174 :     for (i = 0; i < cookies_size; i++)
                                688                 :     {
 9345 bruce                     689              42 :         if (cookies[i] == NULL)
                                690              42 :             return i;
                                691                 :     }
                                692                 : 
                                693                 :     /* No free slot, so make the array bigger */
 8202 tgl                       694 CBC         132 :     if (cookies_size <= 0)
                                695                 :     {
                                696                 :         /* First time through, arbitrarily make 64-element array */
 8202 tgl                       697 GIC         132 :         i = 0;
                                698             132 :         newsize = 64;
 8202 tgl                       699 CBC         132 :         cookies = (LargeObjectDesc **)
 6192                           700             132 :             MemoryContextAllocZero(fscxt, newsize * sizeof(LargeObjectDesc *));
                                701                 :     }
                                702                 :     else
                                703                 :     {
                                704                 :         /* Double size of array */
 8202 tgl                       705 LBC           0 :         i = cookies_size;
 8202 tgl                       706 UIC           0 :         newsize = cookies_size * 2;
  148 peter                     707 UNC           0 :         cookies =
                                708               0 :             repalloc0_array(cookies, LargeObjectDesc *, cookies_size, newsize);
 8202 tgl                       709 ECB             :     }
  148 peter                     710 GNC         132 :     cookies_size = newsize;
                                711                 : 
 8202 tgl                       712 GIC         132 :     return i;
 9770 scrappy                   713 ECB             : }
                                714                 : 
 9345 bruce                     715                 : static void
  522 heikki.linnakangas        716 CBC         141 : closeLOfd(int fd)
                                717                 : {
                                718                 :     LargeObjectDesc *lobj;
                                719                 : 
                                720                 :     /*
  522 heikki.linnakangas        721 EUB             :      * Make sure we do not try to free twice if this errors out for some
                                722                 :      * reason.  Better a leak than a crash.
                                723                 :      */
  522 heikki.linnakangas        724 GBC         141 :     lobj = cookies[fd];
 9345 bruce                     725 GIC         141 :     cookies[fd] = NULL;
  522 heikki.linnakangas        726 ECB             : 
  522 heikki.linnakangas        727 GIC         141 :     if (lobj->snapshot)
  522 heikki.linnakangas        728 CBC         101 :         UnregisterSnapshotFromOwner(lobj->snapshot,
                                729                 :                                     TopTransactionResourceOwner);
  522 heikki.linnakangas        730 GIC         141 :     inv_close(lobj);
 9770 scrappy                   731             141 : }
 3451 noah                      732 ECB             : 
                                733                 : /*****************************************************************************
                                734                 :  *  Wrappers oriented toward SQL callers
                                735                 :  *****************************************************************************/
                                736                 : 
                                737                 : /*
                                738                 :  * Read [offset, offset+nbytes) within LO; when nbytes is -1, read to end.
                                739                 :  */
                                740                 : static bytea *
 3451 noah                      741 CBC          36 : lo_get_fragment_internal(Oid loOid, int64 offset, int32 nbytes)
                                742                 : {
 3451 noah                      743 ECB             :     LargeObjectDesc *loDesc;
                                744                 :     int64       loSize;
                                745                 :     int64       result_length;
 2118 tgl                       746                 :     int         total_read PG_USED_FOR_ASSERTS_ONLY;
 3451 noah                      747 CBC          36 :     bytea      *result = NULL;
                                748                 : 
  522 heikki.linnakangas        749 GIC          36 :     lo_cleanup_needed = true;
                                750              36 :     loDesc = inv_open(loOid, INV_READ, CurrentMemoryContext);
                                751                 : 
                                752                 :     /*
                                753                 :      * Compute number of bytes we'll actually read, accommodating nbytes == -1
                                754                 :      * and reads beyond the end of the LO.
                                755                 :      */
 3451 noah                      756              34 :     loSize = inv_seek(loDesc, 0, SEEK_END);
 3451 noah                      757 CBC          34 :     if (loSize > offset)
                                758                 :     {
 3451 noah                      759 GIC          30 :         if (nbytes >= 0 && nbytes <= loSize - offset)
 2118 tgl                       760               9 :             result_length = nbytes; /* request is wholly inside LO */
                                761                 :         else
 3451 noah                      762              21 :             result_length = loSize - offset;    /* adjust to end of LO */
 3451 noah                      763 ECB             :     }
                                764                 :     else
 3451 noah                      765 CBC           4 :         result_length = 0;      /* request is wholly outside LO */
 3451 noah                      766 ECB             : 
                                767                 :     /*
                                768                 :      * A result_length calculated from loSize may not fit in a size_t.  Check
                                769                 :      * that the size will satisfy this and subsequently-enforced size limits.
                                770                 :      */
 3451 noah                      771 GIC          34 :     if (result_length > MaxAllocSize - VARHDRSZ)
 3451 noah                      772 CBC           3 :         ereport(ERROR,
 3451 noah                      773 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
                                774                 :                  errmsg("large object read request is too large")));
                                775                 : 
 3451 noah                      776 CBC          31 :     result = (bytea *) palloc(VARHDRSZ + result_length);
                                777                 : 
                                778              31 :     inv_seek(loDesc, offset, SEEK_SET);
 3451 noah                      779 GIC          31 :     total_read = inv_read(loDesc, VARDATA(result), result_length);
                                780              31 :     Assert(total_read == result_length);
 3451 noah                      781 CBC          31 :     SET_VARSIZE(result, result_length + VARHDRSZ);
                                782                 : 
 3451 noah                      783 GIC          31 :     inv_close(loDesc);
                                784                 : 
                                785              31 :     return result;
                                786                 : }
 3451 noah                      787 ECB             : 
                                788                 : /*
                                789                 :  * Read entire LO
                                790                 :  */
                                791                 : Datum
 2294 peter_e                   792 CBC          24 : be_lo_get(PG_FUNCTION_ARGS)
                                793                 : {
 3451 noah                      794              24 :     Oid         loOid = PG_GETARG_OID(0);
 3451 noah                      795 ECB             :     bytea      *result;
                                796                 : 
 3451 noah                      797 CBC          24 :     result = lo_get_fragment_internal(loOid, 0, -1);
                                798                 : 
                                799              19 :     PG_RETURN_BYTEA_P(result);
                                800                 : }
 3451 noah                      801 ECB             : 
                                802                 : /*
                                803                 :  * Read range within LO
                                804                 :  */
                                805                 : Datum
 2294 peter_e                   806 GIC          12 : be_lo_get_fragment(PG_FUNCTION_ARGS)
                                807                 : {
 3451 noah                      808 CBC          12 :     Oid         loOid = PG_GETARG_OID(0);
 3451 noah                      809 GIC          12 :     int64       offset = PG_GETARG_INT64(1);
 3451 noah                      810 CBC          12 :     int32       nbytes = PG_GETARG_INT32(2);
                                811                 :     bytea      *result;
                                812                 : 
                                813              12 :     if (nbytes < 0)
 3451 noah                      814 UIC           0 :         ereport(ERROR,
 3451 noah                      815 ECB             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                816                 :                  errmsg("requested length cannot be negative")));
                                817                 : 
 3451 noah                      818 GIC          12 :     result = lo_get_fragment_internal(loOid, offset, nbytes);
                                819                 : 
                                820              12 :     PG_RETURN_BYTEA_P(result);
                                821                 : }
 3451 noah                      822 ECB             : 
                                823                 : /*
 3223 tgl                       824                 :  * Create LO with initial contents given by a bytea argument
 3451 noah                      825                 :  */
                                826                 : Datum
 2294 peter_e                   827 GIC          13 : be_lo_from_bytea(PG_FUNCTION_ARGS)
                                828                 : {
 3451 noah                      829 CBC          13 :     Oid         loOid = PG_GETARG_OID(0);
 3451 noah                      830 GBC          13 :     bytea      *str = PG_GETARG_BYTEA_PP(1);
                                831                 :     LargeObjectDesc *loDesc;
                                832                 :     int         written PG_USED_FOR_ASSERTS_ONLY;
                                833                 : 
  279 michael                   834 GNC          13 :     PreventCommandIfReadOnly("lo_from_bytea()");
                                835                 : 
  522 heikki.linnakangas        836 CBC          10 :     lo_cleanup_needed = true;
 3451 noah                      837 GIC          10 :     loOid = inv_create(loOid);
  522 heikki.linnakangas        838 CBC          10 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
 3451 noah                      839 GIC          10 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
                                840              10 :     Assert(written == VARSIZE_ANY_EXHDR(str));
                                841              10 :     inv_close(loDesc);
                                842                 : 
                                843              10 :     PG_RETURN_OID(loOid);
                                844                 : }
 3451 noah                      845 ECB             : 
                                846                 : /*
                                847                 :  * Update range within LO
                                848                 :  */
                                849                 : Datum
 2294 peter_e                   850 GIC          12 : be_lo_put(PG_FUNCTION_ARGS)
                                851                 : {
 3451 noah                      852 CBC          12 :     Oid         loOid = PG_GETARG_OID(0);
 3451 noah                      853 GIC          12 :     int64       offset = PG_GETARG_INT64(1);
 3451 noah                      854 CBC          12 :     bytea      *str = PG_GETARG_BYTEA_PP(2);
 3451 noah                      855 ECB             :     LargeObjectDesc *loDesc;
 2118 tgl                       856                 :     int         written PG_USED_FOR_ASSERTS_ONLY;
 3451 noah                      857                 : 
  279 michael                   858 GNC          12 :     PreventCommandIfReadOnly("lo_put()");
                                859                 : 
  522 heikki.linnakangas        860 CBC           9 :     lo_cleanup_needed = true;
                                861               9 :     loDesc = inv_open(loOid, INV_WRITE, CurrentMemoryContext);
                                862                 : 
 2071 tgl                       863 ECB             :     /* Permission check */
 2071 tgl                       864 GIC          12 :     if (!lo_compat_privileges &&
                                865               6 :         pg_largeobject_aclcheck_snapshot(loDesc->id,
                                866                 :                                          GetUserId(),
                                867                 :                                          ACL_UPDATE,
                                868                 :                                          loDesc->snapshot) != ACLCHECK_OK)
 2071 tgl                       869 UIC           0 :         ereport(ERROR,
 2071 tgl                       870 ECB             :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                871                 :                  errmsg("permission denied for large object %u",
                                872                 :                         loDesc->id)));
                                873                 : 
 3451 noah                      874 CBC           6 :     inv_seek(loDesc, offset, SEEK_SET);
 3451 noah                      875 GIC           6 :     written = inv_write(loDesc, VARDATA_ANY(str), VARSIZE_ANY_EXHDR(str));
                                876               6 :     Assert(written == VARSIZE_ANY_EXHDR(str));
                                877               6 :     inv_close(loDesc);
 3451 noah                      878 ECB             : 
 3451 noah                      879 GIC           6 :     PG_RETURN_VOID();
 3451 noah                      880 ECB             : }
        

Generated by: LCOV version v1.16-55-g56c0a2a