LCOV - differential code coverage report
Current view: top level - src/backend/storage/large_object - inv_api.c (source / functions) Coverage Total Hit UBC GNC CBC
Current: c70b6db34ffeab48beef1fb4ce61bcad3772b8dd vs 06473f5a344df8c9594ead90a609b86f6724cff8 Lines: 94.7 % 300 284 16 2 282
Current Date: 2025-09-06 07:49:51 +0900 Functions: 100.0 % 13 13 2 11
Baseline: lcov-20250906-005545-baseline Branches: 51.7 % 242 125 117 125
Baseline Date: 2025-09-05 08:21:35 +0100 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(30,360] days: 100.0 % 3 3 2 1
(360..) days: 94.6 % 297 281 16 281
Function coverage date bins:
(360..) days: 100.0 % 13 13 2 11
Branch coverage date bins:
(30,360] days: 100.0 % 2 2 2
(360..) days: 51.2 % 240 123 117 123

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * inv_api.c
                                  4                 :                :  *    routines for manipulating inversion fs large objects. This file
                                  5                 :                :  *    contains the user-level large object application interface routines.
                                  6                 :                :  *
                                  7                 :                :  *
                                  8                 :                :  * Note: we access pg_largeobject.data using its C struct declaration.
                                  9                 :                :  * This is safe because it immediately follows pageno which is an int4 field,
                                 10                 :                :  * and therefore the data field will always be 4-byte aligned, even if it
                                 11                 :                :  * is in the short 1-byte-header format.  We have to detoast it since it's
                                 12                 :                :  * quite likely to be in compressed or short format.  We also need to check
                                 13                 :                :  * for NULLs, since initdb will mark loid and pageno but not data as NOT NULL.
                                 14                 :                :  *
                                 15                 :                :  * Note: many of these routines leak memory in CurrentMemoryContext, as indeed
                                 16                 :                :  * does most of the backend code.  We expect that CurrentMemoryContext will
                                 17                 :                :  * be a short-lived context.  Data that must persist across function calls
                                 18                 :                :  * is kept either in CacheMemoryContext (the Relation structs) or in the
                                 19                 :                :  * memory context given to inv_open (for LargeObjectDesc structs).
                                 20                 :                :  *
                                 21                 :                :  *
                                 22                 :                :  * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
                                 23                 :                :  * Portions Copyright (c) 1994, Regents of the University of California
                                 24                 :                :  *
                                 25                 :                :  *
                                 26                 :                :  * IDENTIFICATION
                                 27                 :                :  *    src/backend/storage/large_object/inv_api.c
                                 28                 :                :  *
                                 29                 :                :  *-------------------------------------------------------------------------
                                 30                 :                :  */
                                 31                 :                : #include "postgres.h"
                                 32                 :                : 
                                 33                 :                : #include <limits.h>
                                 34                 :                : 
                                 35                 :                : #include "access/detoast.h"
                                 36                 :                : #include "access/genam.h"
                                 37                 :                : #include "access/htup_details.h"
                                 38                 :                : #include "access/table.h"
                                 39                 :                : #include "access/xact.h"
                                 40                 :                : #include "catalog/dependency.h"
                                 41                 :                : #include "catalog/indexing.h"
                                 42                 :                : #include "catalog/objectaccess.h"
                                 43                 :                : #include "catalog/pg_largeobject.h"
                                 44                 :                : #include "libpq/libpq-fs.h"
                                 45                 :                : #include "miscadmin.h"
                                 46                 :                : #include "storage/large_object.h"
                                 47                 :                : #include "utils/acl.h"
                                 48                 :                : #include "utils/fmgroids.h"
                                 49                 :                : #include "utils/rel.h"
                                 50                 :                : #include "utils/snapmgr.h"
                                 51                 :                : 
                                 52                 :                : 
                                 53                 :                : /*
                                 54                 :                :  * GUC: backwards-compatibility flag to suppress LO permission checks
                                 55                 :                :  */
                                 56                 :                : bool        lo_compat_privileges;
                                 57                 :                : 
                                 58                 :                : /*
                                 59                 :                :  * All accesses to pg_largeobject and its index make use of a single
                                 60                 :                :  * Relation reference.  To guarantee that the relcache entry remains
                                 61                 :                :  * in the cache, on the first reference inside a subtransaction, we
                                 62                 :                :  * execute a slightly klugy maneuver to assign ownership of the
                                 63                 :                :  * Relation reference to TopTransactionResourceOwner.
                                 64                 :                :  */
                                 65                 :                : static Relation lo_heap_r = NULL;
                                 66                 :                : static Relation lo_index_r = NULL;
                                 67                 :                : 
                                 68                 :                : 
                                 69                 :                : /*
                                 70                 :                :  * Open pg_largeobject and its index, if not already done in current xact
                                 71                 :                :  */
                                 72                 :                : static void
 7710 tgl@sss.pgh.pa.us          73                 :CBC        1552 : open_lo_relation(void)
                                 74                 :                : {
                                 75                 :                :     ResourceOwner currentOwner;
                                 76                 :                : 
                                 77   [ +  +  +  - ]:           1552 :     if (lo_heap_r && lo_index_r)
                                 78                 :           1394 :         return;                 /* already open in current xact */
                                 79                 :                : 
                                 80                 :                :     /* Arrange for the top xact to own these relation references */
                                 81                 :            158 :     currentOwner = CurrentResourceOwner;
 2887                            82                 :            158 :     CurrentResourceOwner = TopTransactionResourceOwner;
                                 83                 :                : 
                                 84                 :                :     /* Use RowExclusiveLock since we might either read or write */
                                 85         [ +  - ]:            158 :     if (lo_heap_r == NULL)
 2420 andres@anarazel.de         86                 :            158 :         lo_heap_r = table_open(LargeObjectRelationId, RowExclusiveLock);
 2887 tgl@sss.pgh.pa.us          87         [ +  - ]:            158 :     if (lo_index_r == NULL)
                                 88                 :            158 :         lo_index_r = index_open(LargeObjectLOidPNIndexId, RowExclusiveLock);
                                 89                 :                : 
 7710                            90                 :            158 :     CurrentResourceOwner = currentOwner;
                                 91                 :                : }
                                 92                 :                : 
                                 93                 :                : /*
                                 94                 :                :  * Clean up at main transaction end
                                 95                 :                :  */
                                 96                 :                : void
                                 97                 :            241 : close_lo_relation(bool isCommit)
                                 98                 :                : {
                                 99   [ +  +  -  + ]:            241 :     if (lo_heap_r || lo_index_r)
                                100                 :                :     {
                                101                 :                :         /*
                                102                 :                :          * Only bother to close if committing; else abort cleanup will handle
                                103                 :                :          * it
                                104                 :                :          */
                                105         [ +  + ]:            158 :         if (isCommit)
                                106                 :                :         {
                                107                 :                :             ResourceOwner currentOwner;
                                108                 :                : 
                                109                 :            110 :             currentOwner = CurrentResourceOwner;
 2887                           110                 :            110 :             CurrentResourceOwner = TopTransactionResourceOwner;
                                111                 :                : 
                                112         [ +  - ]:            110 :             if (lo_index_r)
                                113                 :            110 :                 index_close(lo_index_r, NoLock);
                                114         [ +  - ]:            110 :             if (lo_heap_r)
 2420 andres@anarazel.de        115                 :            110 :                 table_close(lo_heap_r, NoLock);
                                116                 :                : 
 7710 tgl@sss.pgh.pa.us         117                 :            110 :             CurrentResourceOwner = currentOwner;
                                118                 :                :         }
                                119                 :            158 :         lo_heap_r = NULL;
                                120                 :            158 :         lo_index_r = NULL;
                                121                 :                :     }
                                122                 :            241 : }
                                123                 :                : 
                                124                 :                : 
                                125                 :                : /*
                                126                 :                :  * Extract data field from a pg_largeobject tuple, detoasting if needed
                                127                 :                :  * and verifying that the length is sane.  Returns data pointer (a bytea *),
                                128                 :                :  * data length, and an indication of whether to pfree the data pointer.
                                129                 :                :  */
                                130                 :                : static void
 4111                           131                 :           5130 : getdatafield(Form_pg_largeobject tuple,
                                132                 :                :              bytea **pdatafield,
                                133                 :                :              int *plen,
                                134                 :                :              bool *pfreeit)
                                135                 :                : {
                                136                 :                :     bytea      *datafield;
                                137                 :                :     int         len;
                                138                 :                :     bool        freeit;
                                139                 :                : 
                                140                 :           5130 :     datafield = &(tuple->data); /* see note at top of file */
                                141                 :           5130 :     freeit = false;
                                142         [ +  + ]:           5130 :     if (VARATT_IS_EXTENDED(datafield))
                                143                 :                :     {
                                144                 :                :         datafield = (bytea *)
 2164 rhaas@postgresql.org      145                 :           5047 :             detoast_attr((struct varlena *) datafield);
 4111 tgl@sss.pgh.pa.us         146                 :           5047 :         freeit = true;
                                147                 :                :     }
                                148                 :           5130 :     len = VARSIZE(datafield) - VARHDRSZ;
                                149   [ +  -  -  + ]:           5130 :     if (len < 0 || len > LOBLKSIZE)
 4111 tgl@sss.pgh.pa.us         150         [ #  # ]:UBC           0 :         ereport(ERROR,
                                151                 :                :                 (errcode(ERRCODE_DATA_CORRUPTED),
                                152                 :                :                  errmsg("pg_largeobject entry for OID %u, page %d has invalid data field size %d",
                                153                 :                :                         tuple->loid, tuple->pageno, len)));
 4111 tgl@sss.pgh.pa.us         154                 :CBC        5130 :     *pdatafield = datafield;
                                155                 :           5130 :     *plen = len;
                                156                 :           5130 :     *pfreeit = freeit;
 9083                           157                 :           5130 : }
                                158                 :                : 
                                159                 :                : 
                                160                 :                : /*
                                161                 :                :  *  inv_create -- create a new large object
                                162                 :                :  *
                                163                 :                :  *  Arguments:
                                164                 :                :  *    lobjId - OID to use for new large object, or InvalidOid to pick one
                                165                 :                :  *
                                166                 :                :  *  Returns:
                                167                 :                :  *    OID of new object
                                168                 :                :  *
                                169                 :                :  * If lobjId is not InvalidOid, then an error occurs if the OID is already
                                170                 :                :  * in use.
                                171                 :                :  */
                                172                 :                : Oid
 7390                           173                 :             68 : inv_create(Oid lobjId)
                                174                 :                : {
                                175                 :                :     Oid         lobjId_new;
                                176                 :                : 
                                177                 :                :     /*
                                178                 :                :      * Create a new largeobject with empty data pages
                                179                 :                :      */
 5748 itagaki.takahiro@gma      180                 :             68 :     lobjId_new = LargeObjectCreate(lobjId);
                                181                 :                : 
                                182                 :                :     /*
                                183                 :                :      * dependency on the owner of largeobject
                                184                 :                :      *
                                185                 :                :      * Note that LO dependencies are recorded using classId
                                186                 :                :      * LargeObjectRelationId for backwards-compatibility reasons.  Using
                                187                 :                :      * LargeObjectMetadataRelationId instead would simplify matters for the
                                188                 :                :      * backend, but it'd complicate pg_dump and possibly break other clients.
                                189                 :                :      */
                                190                 :             68 :     recordDependencyOnOwner(LargeObjectRelationId,
                                191                 :                :                             lobjId_new, GetUserId());
                                192                 :                : 
                                193                 :                :     /* Post creation hook for new large object */
 4567 rhaas@postgresql.org      194         [ -  + ]:             68 :     InvokeObjectPostCreateHook(LargeObjectRelationId, lobjId_new, 0);
                                195                 :                : 
                                196                 :                :     /*
                                197                 :                :      * Advance command counter to make new tuple visible to later operations.
                                198                 :                :      */
 9085 bruce@momjian.us          199                 :             68 :     CommandCounterIncrement();
                                200                 :                : 
 5748 itagaki.takahiro@gma      201                 :             68 :     return lobjId_new;
                                202                 :                : }
                                203                 :                : 
                                204                 :                : /*
                                205                 :                :  *  inv_open -- access an existing large object.
                                206                 :                :  *
                                207                 :                :  * Returns a large object descriptor, appropriately filled in.
                                208                 :                :  * The descriptor and subsidiary data are allocated in the specified
                                209                 :                :  * memory context, which must be suitably long-lived for the caller's
                                210                 :                :  * purposes.  If the returned descriptor has a snapshot associated
                                211                 :                :  * with it, the caller must ensure that it also lives long enough,
                                212                 :                :  * e.g. by calling RegisterSnapshotOnOwner
                                213                 :                :  */
                                214                 :                : LargeObjectDesc *
 7073 tgl@sss.pgh.pa.us         215                 :            252 : inv_open(Oid lobjId, int flags, MemoryContext mcxt)
                                216                 :                : {
                                217                 :                :     LargeObjectDesc *retval;
 4359 heikki.linnakangas@i      218                 :            252 :     Snapshot    snapshot = NULL;
                                219                 :            252 :     int         descflags = 0;
                                220                 :                : 
                                221                 :                :     /*
                                222                 :                :      * Historically, no difference is made between (INV_WRITE) and (INV_WRITE
                                223                 :                :      * | INV_READ), the caller being allowed to read the large object
                                224                 :                :      * descriptor in either case.
                                225                 :                :      */
 8934 bruce@momjian.us          226         [ +  + ]:            252 :     if (flags & INV_WRITE)
 2858 tgl@sss.pgh.pa.us         227                 :             77 :         descflags |= IFS_WRLOCK | IFS_RDLOCK;
                                228         [ +  + ]:            252 :     if (flags & INV_READ)
                                229                 :            190 :         descflags |= IFS_RDLOCK;
                                230                 :                : 
                                231         [ -  + ]:            252 :     if (descflags == 0)
 4716 tgl@sss.pgh.pa.us         232         [ #  # ]:UBC           0 :         ereport(ERROR,
                                233                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                234                 :                :                  errmsg("invalid flags for opening a large object: %d",
                                235                 :                :                         flags)));
                                236                 :                : 
                                237                 :                :     /* Get snapshot.  If write is requested, use an instantaneous snapshot. */
 2858 tgl@sss.pgh.pa.us         238         [ +  + ]:CBC         252 :     if (descflags & IFS_WRLOCK)
                                239                 :             77 :         snapshot = NULL;
                                240                 :                :     else
                                241                 :            175 :         snapshot = GetActiveSnapshot();
                                242                 :                : 
                                243                 :                :     /* Can't use LargeObjectExists here because we need to specify snapshot */
  359 fujii@postgresql.org      244         [ +  + ]:            252 :     if (!LargeObjectExistsWithSnapshot(lobjId, snapshot))
 7390 tgl@sss.pgh.pa.us         245         [ +  - ]:              5 :         ereport(ERROR,
                                246                 :                :                 (errcode(ERRCODE_UNDEFINED_OBJECT),
                                247                 :                :                  errmsg("large object %u does not exist", lobjId)));
                                248                 :                : 
                                249                 :                :     /* Apply permission checks, again specifying snapshot */
 2858                           250         [ +  - ]:            247 :     if ((descflags & IFS_RDLOCK) != 0)
                                251                 :                :     {
                                252   [ +  +  +  + ]:            485 :         if (!lo_compat_privileges &&
                                253                 :            238 :             pg_largeobject_aclcheck_snapshot(lobjId,
                                254                 :                :                                              GetUserId(),
                                255                 :                :                                              ACL_SELECT,
                                256                 :                :                                              snapshot) != ACLCHECK_OK)
                                257         [ +  - ]:             21 :             ereport(ERROR,
                                258                 :                :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                259                 :                :                      errmsg("permission denied for large object %u",
                                260                 :                :                             lobjId)));
                                261                 :                :     }
                                262         [ +  + ]:            226 :     if ((descflags & IFS_WRLOCK) != 0)
                                263                 :                :     {
                                264   [ +  +  +  + ]:            124 :         if (!lo_compat_privileges &&
                                265                 :             59 :             pg_largeobject_aclcheck_snapshot(lobjId,
                                266                 :                :                                              GetUserId(),
                                267                 :                :                                              ACL_UPDATE,
                                268                 :                :                                              snapshot) != ACLCHECK_OK)
                                269         [ +  - ]:              6 :             ereport(ERROR,
                                270                 :                :                     (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                271                 :                :                      errmsg("permission denied for large object %u",
                                272                 :                :                             lobjId)));
                                273                 :                :     }
                                274                 :                : 
                                275                 :                :     /* OK to create a descriptor */
                                276                 :            220 :     retval = (LargeObjectDesc *) MemoryContextAlloc(mcxt,
                                277                 :                :                                                     sizeof(LargeObjectDesc));
                                278                 :            220 :     retval->id = lobjId;
                                279                 :            220 :     retval->offset = 0;
                                280                 :            220 :     retval->flags = descflags;
                                281                 :                : 
                                282                 :                :     /* caller sets if needed, not used by the functions in this file */
 1403 heikki.linnakangas@i      283                 :            220 :     retval->subid = InvalidSubTransactionId;
                                284                 :                : 
                                285                 :                :     /*
                                286                 :                :      * The snapshot (if any) is just the currently active snapshot.  The
                                287                 :                :      * caller will replace it with a longer-lived copy if needed.
                                288                 :                :      */
 4359                           289                 :            220 :     retval->snapshot = snapshot;
                                290                 :                : 
 9867 bruce@momjian.us          291                 :            220 :     return retval;
                                292                 :                : }
                                293                 :                : 
                                294                 :                : /*
                                295                 :                :  * Closes a large object descriptor previously made by inv_open(), and
                                296                 :                :  * releases the long-term memory used by it.
                                297                 :                :  */
                                298                 :                : void
10225                           299                 :            205 : inv_close(LargeObjectDesc *obj_desc)
                                300                 :                : {
10226                           301         [ -  + ]:            205 :     Assert(PointerIsValid(obj_desc));
                                302                 :            205 :     pfree(obj_desc);
10651 scrappy@hub.org           303                 :            205 : }
                                304                 :                : 
                                305                 :                : /*
                                306                 :                :  * Destroys an existing large object (not to be confused with a descriptor!)
                                307                 :                :  *
                                308                 :                :  * Note we expect caller to have done any required permissions check.
                                309                 :                :  */
                                310                 :                : int
 9402 bruce@momjian.us          311                 :             44 : inv_drop(Oid lobjId)
                                312                 :                : {
                                313                 :                :     ObjectAddress object;
                                314                 :                : 
                                315                 :                :     /*
                                316                 :                :      * Delete any comments and dependencies on the large object
                                317                 :                :      */
 5748 itagaki.takahiro@gma      318                 :             44 :     object.classId = LargeObjectRelationId;
                                319                 :             44 :     object.objectId = lobjId;
                                320                 :             44 :     object.objectSubId = 0;
 4972 rhaas@postgresql.org      321                 :             44 :     performDeletion(&object, DROP_CASCADE, 0);
                                322                 :                : 
                                323                 :                :     /*
                                324                 :                :      * Advance command counter so that tuple removal will be seen by later
                                325                 :                :      * large-object operations in this transaction.
                                326                 :                :      */
 9083 tgl@sss.pgh.pa.us         327                 :             44 :     CommandCounterIncrement();
                                328                 :                : 
                                329                 :                :     /* For historical reasons, we always return 1 on success. */
10226 bruce@momjian.us          330                 :             44 :     return 1;
                                331                 :                : }
                                332                 :                : 
                                333                 :                : /*
                                334                 :                :  * Determine size of a large object
                                335                 :                :  *
                                336                 :                :  * NOTE: LOs can contain gaps, just like Unix files.  We actually return
                                337                 :                :  * the offset of the last byte + 1.
                                338                 :                :  */
                                339                 :                : static uint64
 9083 tgl@sss.pgh.pa.us         340                 :             57 : inv_getsize(LargeObjectDesc *obj_desc)
                                341                 :                : {
 4717 ishii@postgresql.org      342                 :             57 :     uint64      lastbyte = 0;
                                343                 :                :     ScanKeyData skey[1];
                                344                 :                :     SysScanDesc sd;
                                345                 :                :     HeapTuple   tuple;
                                346                 :                : 
10226 bruce@momjian.us          347         [ -  + ]:             57 :     Assert(PointerIsValid(obj_desc));
                                348                 :                : 
 7710 tgl@sss.pgh.pa.us         349                 :             57 :     open_lo_relation();
                                350                 :                : 
 7969                           351                 :             57 :     ScanKeyInit(&skey[0],
                                352                 :                :                 Anum_pg_largeobject_loid,
                                353                 :                :                 BTEqualStrategyNumber, F_OIDEQ,
                                354                 :                :                 ObjectIdGetDatum(obj_desc->id));
                                355                 :                : 
 6356                           356                 :             57 :     sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
                                357                 :                :                                     obj_desc->snapshot, 1, skey);
                                358                 :                : 
                                359                 :                :     /*
                                360                 :                :      * Because the pg_largeobject index is on both loid and pageno, but we
                                361                 :                :      * constrain only loid, a backwards scan should visit all pages of the
                                362                 :                :      * large object in reverse pageno order.  So, it's sufficient to examine
                                363                 :                :      * the first valid tuple (== last valid page).
                                364                 :                :      */
 5748 itagaki.takahiro@gma      365                 :             57 :     tuple = systable_getnext_ordered(sd, BackwardScanDirection);
                                366         [ +  + ]:             57 :     if (HeapTupleIsValid(tuple))
                                367                 :                :     {
                                368                 :                :         Form_pg_largeobject data;
                                369                 :                :         bytea      *datafield;
                                370                 :                :         int         len;
                                371                 :                :         bool        pfreeit;
                                372                 :                : 
 6505 bruce@momjian.us          373         [ -  + ]:             48 :         if (HeapTupleHasNulls(tuple))   /* paranoia */
 6661 tgl@sss.pgh.pa.us         374         [ #  # ]:UBC           0 :             elog(ERROR, "null field found in pg_largeobject");
 8510 tgl@sss.pgh.pa.us         375                 :CBC          48 :         data = (Form_pg_largeobject) GETSTRUCT(tuple);
 4111                           376                 :             48 :         getdatafield(data, &datafield, &len, &pfreeit);
                                377                 :             48 :         lastbyte = (uint64) data->pageno * LOBLKSIZE + len;
 9083                           378         [ +  + ]:             48 :         if (pfreeit)
                                379                 :              9 :             pfree(datafield);
                                380                 :                :     }
                                381                 :                : 
 6356                           382                 :             57 :     systable_endscan_ordered(sd);
                                383                 :                : 
 9083                           384                 :             57 :     return lastbyte;
                                385                 :                : }
                                386                 :                : 
                                387                 :                : int64
 4717 ishii@postgresql.org      388                 :            120 : inv_seek(LargeObjectDesc *obj_desc, int64 offset, int whence)
                                389                 :                : {
                                390                 :                :     int64       newoffset;
                                391                 :                : 
10226 bruce@momjian.us          392         [ -  + ]:            120 :     Assert(PointerIsValid(obj_desc));
                                393                 :                : 
                                394                 :                :     /*
                                395                 :                :      * We allow seek/tell if you have either read or write permission, so no
                                396                 :                :      * need for a permission check here.
                                397                 :                :      */
                                398                 :                : 
                                399                 :                :     /*
                                400                 :                :      * Note: overflow in the additions is possible, but since we will reject
                                401                 :                :      * negative results, we don't need any extra test for that.
                                402                 :                :      */
 9083 tgl@sss.pgh.pa.us         403   [ +  +  +  - ]:            120 :     switch (whence)
                                404                 :                :     {
                                405                 :             54 :         case SEEK_SET:
 4716                           406                 :             54 :             newoffset = offset;
 9083                           407                 :             54 :             break;
                                408                 :              9 :         case SEEK_CUR:
 4716                           409                 :              9 :             newoffset = obj_desc->offset + offset;
 9083                           410                 :              9 :             break;
                                411                 :             57 :         case SEEK_END:
 4716                           412                 :             57 :             newoffset = inv_getsize(obj_desc) + offset;
 9083                           413                 :             57 :             break;
 9083 tgl@sss.pgh.pa.us         414                 :UBC           0 :         default:
 4716                           415         [ #  # ]:              0 :             ereport(ERROR,
                                416                 :                :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                417                 :                :                      errmsg("invalid whence setting: %d", whence)));
                                418                 :                :             newoffset = 0;      /* keep compiler quiet */
                                419                 :                :             break;
                                420                 :                :     }
                                421                 :                : 
                                422                 :                :     /*
                                423                 :                :      * use errmsg_internal here because we don't want to expose INT64_FORMAT
                                424                 :                :      * in translatable strings; doing better is not worth the trouble
                                425                 :                :      */
 4716 tgl@sss.pgh.pa.us         426   [ +  -  -  + ]:CBC         120 :     if (newoffset < 0 || newoffset > MAX_LARGE_OBJECT_SIZE)
 4716 tgl@sss.pgh.pa.us         427         [ #  # ]:UBC           0 :         ereport(ERROR,
                                428                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                429                 :                :                  errmsg_internal("invalid large object seek target: " INT64_FORMAT,
                                430                 :                :                                  newoffset)));
                                431                 :                : 
 4716 tgl@sss.pgh.pa.us         432                 :CBC         120 :     obj_desc->offset = newoffset;
                                433                 :            120 :     return newoffset;
                                434                 :                : }
                                435                 :                : 
                                436                 :                : int64
10225 bruce@momjian.us          437                 :             24 : inv_tell(LargeObjectDesc *obj_desc)
                                438                 :                : {
10226                           439         [ -  + ]:             24 :     Assert(PointerIsValid(obj_desc));
                                440                 :                : 
                                441                 :                :     /*
                                442                 :                :      * We allow seek/tell if you have either read or write permission, so no
                                443                 :                :      * need for a permission check here.
                                444                 :                :      */
                                445                 :                : 
 9867                           446                 :             24 :     return obj_desc->offset;
                                447                 :                : }
                                448                 :                : 
                                449                 :                : int
10225                           450                 :            707 : inv_read(LargeObjectDesc *obj_desc, char *buf, int nbytes)
                                451                 :                : {
 8934                           452                 :            707 :     int         nread = 0;
                                453                 :                :     int64       n;
                                454                 :                :     int64       off;
                                455                 :                :     int         len;
                                456                 :            707 :     int32       pageno = (int32) (obj_desc->offset / LOBLKSIZE);
                                457                 :                :     uint64      pageoff;
                                458                 :                :     ScanKeyData skey[2];
                                459                 :                :     SysScanDesc sd;
                                460                 :                :     HeapTuple   tuple;
                                461                 :                : 
10226                           462         [ -  + ]:            707 :     Assert(PointerIsValid(obj_desc));
                                463         [ -  + ]:            707 :     Assert(buf != NULL);
                                464                 :                : 
 2858 tgl@sss.pgh.pa.us         465         [ -  + ]:            707 :     if ((obj_desc->flags & IFS_RDLOCK) == 0)
 2858 tgl@sss.pgh.pa.us         466         [ #  # ]:UBC           0 :         ereport(ERROR,
                                467                 :                :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                468                 :                :                  errmsg("permission denied for large object %u",
                                469                 :                :                         obj_desc->id)));
                                470                 :                : 
 9083 tgl@sss.pgh.pa.us         471         [ +  + ]:CBC         707 :     if (nbytes <= 0)
 9085 bruce@momjian.us          472                 :              9 :         return 0;
                                473                 :                : 
 7710 tgl@sss.pgh.pa.us         474                 :            698 :     open_lo_relation();
                                475                 :                : 
 7969                           476                 :            698 :     ScanKeyInit(&skey[0],
                                477                 :                :                 Anum_pg_largeobject_loid,
                                478                 :                :                 BTEqualStrategyNumber, F_OIDEQ,
                                479                 :                :                 ObjectIdGetDatum(obj_desc->id));
                                480                 :                : 
                                481                 :            698 :     ScanKeyInit(&skey[1],
                                482                 :                :                 Anum_pg_largeobject_pageno,
                                483                 :                :                 BTGreaterEqualStrategyNumber, F_INT4GE,
                                484                 :                :                 Int32GetDatum(pageno));
                                485                 :                : 
 6356                           486                 :            698 :     sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
                                487                 :                :                                     obj_desc->snapshot, 2, skey);
                                488                 :                : 
                                489         [ +  + ]:           5234 :     while ((tuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
                                490                 :                :     {
                                491                 :                :         Form_pg_largeobject data;
                                492                 :                :         bytea      *datafield;
                                493                 :                :         bool        pfreeit;
                                494                 :                : 
 6505 bruce@momjian.us          495         [ -  + ]:           5067 :         if (HeapTupleHasNulls(tuple))   /* paranoia */
 6661 tgl@sss.pgh.pa.us         496         [ #  # ]:UBC           0 :             elog(ERROR, "null field found in pg_largeobject");
 8510 tgl@sss.pgh.pa.us         497                 :CBC        5067 :         data = (Form_pg_largeobject) GETSTRUCT(tuple);
                                498                 :                : 
                                499                 :                :         /*
                                500                 :                :          * We expect the indexscan will deliver pages in order.  However,
                                501                 :                :          * there may be missing pages if the LO contains unwritten "holes". We
                                502                 :                :          * want missing sections to read out as zeroes.
                                503                 :                :          */
 4717 ishii@postgresql.org      504                 :           5067 :         pageoff = ((uint64) data->pageno) * LOBLKSIZE;
 9083 tgl@sss.pgh.pa.us         505         [ +  + ]:           5067 :         if (pageoff > obj_desc->offset)
                                506                 :                :         {
                                507                 :              6 :             n = pageoff - obj_desc->offset;
                                508                 :              6 :             n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
                                509   [ -  +  -  -  :              6 :             MemSet(buf + nread, 0, n);
                                     -  -  -  -  -  
                                                 - ]
                                510                 :              6 :             nread += n;
                                511                 :              6 :             obj_desc->offset += n;
                                512                 :                :         }
                                513                 :                : 
                                514         [ +  + ]:           5067 :         if (nread < nbytes)
                                515                 :                :         {
                                516         [ -  + ]:           5064 :             Assert(obj_desc->offset >= pageoff);
                                517                 :           5064 :             off = (int) (obj_desc->offset - pageoff);
                                518   [ +  -  -  + ]:           5064 :             Assert(off >= 0 && off < LOBLKSIZE);
                                519                 :                : 
 4111                           520                 :           5064 :             getdatafield(data, &datafield, &len, &pfreeit);
 9083                           521         [ +  + ]:           5064 :             if (len > off)
                                522                 :                :             {
                                523                 :           5010 :                 n = len - off;
                                524                 :           5010 :                 n = (n <= (nbytes - nread)) ? n : (nbytes - nread);
                                525                 :           5010 :                 memcpy(buf + nread, VARDATA(datafield) + off, n);
                                526                 :           5010 :                 nread += n;
                                527                 :           5010 :                 obj_desc->offset += n;
                                528                 :                :             }
                                529         [ +  + ]:           5064 :             if (pfreeit)
                                530                 :           5026 :                 pfree(datafield);
                                531                 :                :         }
                                532                 :                : 
                                533         [ +  + ]:           5067 :         if (nread >= nbytes)
                                534                 :            531 :             break;
                                535                 :                :     }
                                536                 :                : 
 6356                           537                 :            698 :     systable_endscan_ordered(sd);
                                538                 :                : 
 9867 bruce@momjian.us          539                 :            698 :     return nread;
                                540                 :                : }
                                541                 :                : 
                                542                 :                : int
 6939                           543                 :            776 : inv_write(LargeObjectDesc *obj_desc, const char *buf, int nbytes)
                                544                 :                : {
 8934                           545                 :            776 :     int         nwritten = 0;
                                546                 :                :     int         n;
                                547                 :                :     int         off;
                                548                 :                :     int         len;
                                549                 :            776 :     int32       pageno = (int32) (obj_desc->offset / LOBLKSIZE);
                                550                 :                :     ScanKeyData skey[2];
                                551                 :                :     SysScanDesc sd;
                                552                 :                :     HeapTuple   oldtuple;
                                553                 :                :     Form_pg_largeobject olddata;
                                554                 :                :     bool        neednextpage;
                                555                 :                :     bytea      *datafield;
                                556                 :                :     bool        pfreeit;
                                557                 :                :     union
                                558                 :                :     {
                                559                 :                :         bytea       hdr;
                                560                 :                :         /* this is to make the union big enough for a LO data chunk: */
                                561                 :                :         char        data[LOBLKSIZE + VARHDRSZ];
                                562                 :                :         /* ensure union is aligned well enough: */
                                563                 :                :         int32       align_it;
   32 msawada@postgresql.o      564                 :GNC         776 :     }           workbuf = {0};
 6766 tgl@sss.pgh.pa.us         565                 :CBC         776 :     char       *workb = VARDATA(&workbuf.hdr);
                                566                 :                :     HeapTuple   newtup;
                                567                 :                :     Datum       values[Natts_pg_largeobject];
                                568                 :                :     bool        nulls[Natts_pg_largeobject];
                                569                 :                :     bool        replace[Natts_pg_largeobject];
                                570                 :                :     CatalogIndexState indstate;
                                571                 :                : 
10226 bruce@momjian.us          572         [ -  + ]:            776 :     Assert(PointerIsValid(obj_desc));
                                573         [ -  + ]:            776 :     Assert(buf != NULL);
                                574                 :                : 
                                575                 :                :     /* enforce writability because snapshot is probably wrong otherwise */
 2858 tgl@sss.pgh.pa.us         576         [ -  + ]:            776 :     if ((obj_desc->flags & IFS_WRLOCK) == 0)
 2858 tgl@sss.pgh.pa.us         577         [ #  # ]:UBC           0 :         ereport(ERROR,
                                578                 :                :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                579                 :                :                  errmsg("permission denied for large object %u",
                                580                 :                :                         obj_desc->id)));
                                581                 :                : 
 9083 tgl@sss.pgh.pa.us         582         [ -  + ]:CBC         776 :     if (nbytes <= 0)
 9083 tgl@sss.pgh.pa.us         583                 :UBC           0 :         return 0;
                                584                 :                : 
                                585                 :                :     /* this addition can't overflow because nbytes is only int32 */
 4717 ishii@postgresql.org      586         [ -  + ]:CBC         776 :     if ((nbytes + obj_desc->offset) > MAX_LARGE_OBJECT_SIZE)
 4716 tgl@sss.pgh.pa.us         587         [ #  # ]:UBC           0 :         ereport(ERROR,
                                588                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                589                 :                :                  errmsg("invalid large object write request size: %d",
                                590                 :                :                         nbytes)));
                                591                 :                : 
 7710 tgl@sss.pgh.pa.us         592                 :CBC         776 :     open_lo_relation();
                                593                 :                : 
                                594                 :            776 :     indstate = CatalogOpenIndexes(lo_heap_r);
                                595                 :                : 
 7969                           596                 :            776 :     ScanKeyInit(&skey[0],
                                597                 :                :                 Anum_pg_largeobject_loid,
                                598                 :                :                 BTEqualStrategyNumber, F_OIDEQ,
                                599                 :                :                 ObjectIdGetDatum(obj_desc->id));
                                600                 :                : 
                                601                 :            776 :     ScanKeyInit(&skey[1],
                                602                 :                :                 Anum_pg_largeobject_pageno,
                                603                 :                :                 BTGreaterEqualStrategyNumber, F_INT4GE,
                                604                 :                :                 Int32GetDatum(pageno));
                                605                 :                : 
 6356                           606                 :            776 :     sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
                                607                 :                :                                     obj_desc->snapshot, 2, skey);
                                608                 :                : 
 8510                           609                 :            776 :     oldtuple = NULL;
 9083                           610                 :            776 :     olddata = NULL;
                                611                 :            776 :     neednextpage = true;
                                612                 :                : 
 9085 bruce@momjian.us          613         [ +  + ]:           4750 :     while (nwritten < nbytes)
                                614                 :                :     {
                                615                 :                :         /*
                                616                 :                :          * If possible, get next pre-existing page of the LO.  We expect the
                                617                 :                :          * indexscan will deliver these in order --- but there may be holes.
                                618                 :                :          */
 9083 tgl@sss.pgh.pa.us         619         [ +  + ]:           3974 :         if (neednextpage)
                                620                 :                :         {
 6356                           621         [ +  + ]:            779 :             if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
                                622                 :                :             {
 2999                           623         [ -  + ]:             12 :                 if (HeapTupleHasNulls(oldtuple))    /* paranoia */
 6661 tgl@sss.pgh.pa.us         624         [ #  # ]:UBC           0 :                     elog(ERROR, "null field found in pg_largeobject");
 8510 tgl@sss.pgh.pa.us         625                 :CBC          12 :                 olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
                                626         [ -  + ]:             12 :                 Assert(olddata->pageno >= pageno);
                                627                 :                :             }
 9083                           628                 :            779 :             neednextpage = false;
                                629                 :                :         }
                                630                 :                : 
                                631                 :                :         /*
                                632                 :                :          * If we have a pre-existing page, see if it is the page we want to
                                633                 :                :          * write, or a later one.
                                634                 :                :          */
                                635   [ +  +  +  - ]:           3974 :         if (olddata != NULL && olddata->pageno == pageno)
                                636                 :                :         {
                                637                 :                :             /*
                                638                 :                :              * Update an existing page with fresh data.
                                639                 :                :              *
                                640                 :                :              * First, load old data into workbuf
                                641                 :                :              */
 4111                           642                 :             12 :             getdatafield(olddata, &datafield, &len, &pfreeit);
 9083                           643                 :             12 :             memcpy(workb, VARDATA(datafield), len);
                                644         [ +  + ]:             12 :             if (pfreeit)
                                645                 :              9 :                 pfree(datafield);
                                646                 :                : 
                                647                 :                :             /*
                                648                 :                :              * Fill any hole
                                649                 :                :              */
                                650                 :             12 :             off = (int) (obj_desc->offset % LOBLKSIZE);
                                651         [ -  + ]:             12 :             if (off > len)
 9083 tgl@sss.pgh.pa.us         652   [ #  #  #  #  :UBC           0 :                 MemSet(workb + len, 0, off - len);
                                     #  #  #  #  #  
                                                 # ]
                                653                 :                : 
                                654                 :                :             /*
                                655                 :                :              * Insert appropriate portion of new data
                                656                 :                :              */
 9083 tgl@sss.pgh.pa.us         657                 :CBC          12 :             n = LOBLKSIZE - off;
                                658                 :             12 :             n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
                                659                 :             12 :             memcpy(workb + off, buf + nwritten, n);
                                660                 :             12 :             nwritten += n;
                                661                 :             12 :             obj_desc->offset += n;
                                662                 :             12 :             off += n;
                                663                 :                :             /* compute valid length of new page */
                                664                 :             12 :             len = (len >= off) ? len : off;
 6766                           665                 :             12 :             SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
                                666                 :                : 
                                667                 :                :             /*
                                668                 :                :              * Form and insert updated tuple
                                669                 :                :              */
 9083                           670                 :             12 :             memset(values, 0, sizeof(values));
 6152                           671                 :             12 :             memset(nulls, false, sizeof(nulls));
                                672                 :             12 :             memset(replace, false, sizeof(replace));
 8931                           673                 :             12 :             values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
 6152                           674                 :             12 :             replace[Anum_pg_largeobject_data - 1] = true;
                                675                 :             12 :             newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
                                676                 :                :                                        values, nulls, replace);
 3139                           677                 :             12 :             CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
                                678                 :                :                                        indstate);
 9083                           679                 :             12 :             heap_freetuple(newtup);
                                680                 :                : 
                                681                 :                :             /*
                                682                 :                :              * We're done with this old page.
                                683                 :                :              */
 8510                           684                 :             12 :             oldtuple = NULL;
 9083                           685                 :             12 :             olddata = NULL;
                                686                 :             12 :             neednextpage = true;
                                687                 :                :         }
                                688                 :                :         else
                                689                 :                :         {
                                690                 :                :             /*
                                691                 :                :              * Write a brand new page.
                                692                 :                :              *
                                693                 :                :              * First, fill any hole
                                694                 :                :              */
                                695                 :           3962 :             off = (int) (obj_desc->offset % LOBLKSIZE);
                                696         [ +  + ]:           3962 :             if (off > 0)
                                697   [ -  +  -  -  :              3 :                 MemSet(workb, 0, off);
                                     -  -  -  -  -  
                                                 - ]
                                698                 :                : 
                                699                 :                :             /*
                                700                 :                :              * Insert appropriate portion of new data
                                701                 :                :              */
                                702                 :           3962 :             n = LOBLKSIZE - off;
                                703                 :           3962 :             n = (n <= (nbytes - nwritten)) ? n : (nbytes - nwritten);
                                704                 :           3962 :             memcpy(workb + off, buf + nwritten, n);
                                705                 :           3962 :             nwritten += n;
                                706                 :           3962 :             obj_desc->offset += n;
                                707                 :                :             /* compute valid length of new page */
                                708                 :           3962 :             len = off + n;
 6766                           709                 :           3962 :             SET_VARSIZE(&workbuf.hdr, len + VARHDRSZ);
                                710                 :                : 
                                711                 :                :             /*
                                712                 :                :              * Form and insert updated tuple
                                713                 :                :              */
 9083                           714                 :           3962 :             memset(values, 0, sizeof(values));
 6152                           715                 :           3962 :             memset(nulls, false, sizeof(nulls));
 9083                           716                 :           3962 :             values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
                                717                 :           3962 :             values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
 8931                           718                 :           3962 :             values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
 6152                           719                 :           3962 :             newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
 3139                           720                 :           3962 :             CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
 9083                           721                 :           3962 :             heap_freetuple(newtup);
                                722                 :                :         }
                                723                 :           3974 :         pageno++;
                                724                 :                :     }
                                725                 :                : 
 6356                           726                 :            776 :     systable_endscan_ordered(sd);
                                727                 :                : 
 8433                           728                 :            776 :     CatalogCloseIndexes(indstate);
                                729                 :                : 
                                730                 :                :     /*
                                731                 :                :      * Advance command counter so that my tuple updates will be seen by later
                                732                 :                :      * large-object operations in this transaction.
                                733                 :                :      */
 9083                           734                 :            776 :     CommandCounterIncrement();
                                735                 :                : 
 9085 bruce@momjian.us          736                 :            776 :     return nwritten;
                                737                 :                : }
                                738                 :                : 
                                739                 :                : void
 4717 ishii@postgresql.org      740                 :             21 : inv_truncate(LargeObjectDesc *obj_desc, int64 len)
                                741                 :                : {
 6762 bruce@momjian.us          742                 :             21 :     int32       pageno = (int32) (len / LOBLKSIZE);
                                743                 :                :     int32       off;
                                744                 :                :     ScanKeyData skey[2];
                                745                 :                :     SysScanDesc sd;
                                746                 :                :     HeapTuple   oldtuple;
                                747                 :                :     Form_pg_largeobject olddata;
                                748                 :                :     union
                                749                 :                :     {
                                750                 :                :         bytea       hdr;
                                751                 :                :         /* this is to make the union big enough for a LO data chunk: */
                                752                 :                :         char        data[LOBLKSIZE + VARHDRSZ];
                                753                 :                :         /* ensure union is aligned well enough: */
                                754                 :                :         int32       align_it;
   32 msawada@postgresql.o      755                 :GNC          21 :     }           workbuf = {0};
 6505 bruce@momjian.us          756                 :CBC          21 :     char       *workb = VARDATA(&workbuf.hdr);
                                757                 :                :     HeapTuple   newtup;
                                758                 :                :     Datum       values[Natts_pg_largeobject];
                                759                 :                :     bool        nulls[Natts_pg_largeobject];
                                760                 :                :     bool        replace[Natts_pg_largeobject];
                                761                 :                :     CatalogIndexState indstate;
                                762                 :                : 
 6762                           763         [ -  + ]:             21 :     Assert(PointerIsValid(obj_desc));
                                764                 :                : 
                                765                 :                :     /* enforce writability because snapshot is probably wrong otherwise */
 2858 tgl@sss.pgh.pa.us         766         [ -  + ]:             21 :     if ((obj_desc->flags & IFS_WRLOCK) == 0)
 2858 tgl@sss.pgh.pa.us         767         [ #  # ]:UBC           0 :         ereport(ERROR,
                                768                 :                :                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
                                769                 :                :                  errmsg("permission denied for large object %u",
                                770                 :                :                         obj_desc->id)));
                                771                 :                : 
                                772                 :                :     /*
                                773                 :                :      * use errmsg_internal here because we don't want to expose INT64_FORMAT
                                774                 :                :      * in translatable strings; doing better is not worth the trouble
                                775                 :                :      */
 4716 tgl@sss.pgh.pa.us         776   [ +  -  -  + ]:CBC          21 :     if (len < 0 || len > MAX_LARGE_OBJECT_SIZE)
 4716 tgl@sss.pgh.pa.us         777         [ #  # ]:UBC           0 :         ereport(ERROR,
                                778                 :                :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                                779                 :                :                  errmsg_internal("invalid large object truncation target: " INT64_FORMAT,
                                780                 :                :                                  len)));
                                781                 :                : 
 6762 bruce@momjian.us          782                 :CBC          21 :     open_lo_relation();
                                783                 :                : 
                                784                 :             21 :     indstate = CatalogOpenIndexes(lo_heap_r);
                                785                 :                : 
                                786                 :                :     /*
                                787                 :                :      * Set up to find all pages with desired loid and pageno >= target
                                788                 :                :      */
                                789                 :             21 :     ScanKeyInit(&skey[0],
                                790                 :                :                 Anum_pg_largeobject_loid,
                                791                 :                :                 BTEqualStrategyNumber, F_OIDEQ,
                                792                 :                :                 ObjectIdGetDatum(obj_desc->id));
                                793                 :                : 
                                794                 :             21 :     ScanKeyInit(&skey[1],
                                795                 :                :                 Anum_pg_largeobject_pageno,
                                796                 :                :                 BTGreaterEqualStrategyNumber, F_INT4GE,
                                797                 :                :                 Int32GetDatum(pageno));
                                798                 :                : 
 6356 tgl@sss.pgh.pa.us         799                 :             21 :     sd = systable_beginscan_ordered(lo_heap_r, lo_index_r,
                                800                 :                :                                     obj_desc->snapshot, 2, skey);
                                801                 :                : 
                                802                 :                :     /*
                                803                 :                :      * If possible, get the page the truncation point is in. The truncation
                                804                 :                :      * point may be beyond the end of the LO or in a hole.
                                805                 :                :      */
 6762 bruce@momjian.us          806                 :             21 :     olddata = NULL;
 6356 tgl@sss.pgh.pa.us         807         [ +  + ]:             21 :     if ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
                                808                 :                :     {
 2999                           809         [ -  + ]:             12 :         if (HeapTupleHasNulls(oldtuple))    /* paranoia */
 6661 tgl@sss.pgh.pa.us         810         [ #  # ]:UBC           0 :             elog(ERROR, "null field found in pg_largeobject");
 6762 bruce@momjian.us          811                 :CBC          12 :         olddata = (Form_pg_largeobject) GETSTRUCT(oldtuple);
                                812         [ -  + ]:             12 :         Assert(olddata->pageno >= pageno);
                                813                 :                :     }
                                814                 :                : 
                                815                 :                :     /*
                                816                 :                :      * If we found the page of the truncation point we need to truncate the
                                817                 :                :      * data in it.  Otherwise if we're in a hole, we need to create a page to
                                818                 :                :      * mark the end of data.
                                819                 :                :      */
                                820   [ +  +  +  + ]:             21 :     if (olddata != NULL && olddata->pageno == pageno)
                                821                 :              6 :     {
                                822                 :                :         /* First, load old data into workbuf */
                                823                 :                :         bytea      *datafield;
                                824                 :                :         int         pagelen;
                                825                 :                :         bool        pfreeit;
                                826                 :                : 
 4111 tgl@sss.pgh.pa.us         827                 :              6 :         getdatafield(olddata, &datafield, &pagelen, &pfreeit);
 6762 bruce@momjian.us          828                 :              6 :         memcpy(workb, VARDATA(datafield), pagelen);
                                829         [ +  + ]:              6 :         if (pfreeit)
 6505                           830                 :              3 :             pfree(datafield);
                                831                 :                : 
                                832                 :                :         /*
                                833                 :                :          * Fill any hole
                                834                 :                :          */
 6762                           835                 :              6 :         off = len % LOBLKSIZE;
                                836         [ +  + ]:              6 :         if (off > pagelen)
 6505                           837   [ +  -  -  +  :              3 :             MemSet(workb + pagelen, 0, off - pagelen);
                                     -  -  -  -  -  
                                                 - ]
                                838                 :                : 
                                839                 :                :         /* compute length of new page */
 6762                           840                 :              6 :         SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
                                841                 :                : 
                                842                 :                :         /*
                                843                 :                :          * Form and insert updated tuple
                                844                 :                :          */
                                845                 :              6 :         memset(values, 0, sizeof(values));
 6152 tgl@sss.pgh.pa.us         846                 :              6 :         memset(nulls, false, sizeof(nulls));
                                847                 :              6 :         memset(replace, false, sizeof(replace));
 6762 bruce@momjian.us          848                 :              6 :         values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
 6152 tgl@sss.pgh.pa.us         849                 :              6 :         replace[Anum_pg_largeobject_data - 1] = true;
                                850                 :              6 :         newtup = heap_modify_tuple(oldtuple, RelationGetDescr(lo_heap_r),
                                851                 :                :                                    values, nulls, replace);
 3139                           852                 :              6 :         CatalogTupleUpdateWithInfo(lo_heap_r, &newtup->t_self, newtup,
                                853                 :                :                                    indstate);
 6762 bruce@momjian.us          854                 :              6 :         heap_freetuple(newtup);
                                855                 :                :     }
                                856                 :                :     else
                                857                 :                :     {
                                858                 :                :         /*
                                859                 :                :          * If the first page we found was after the truncation point, we're in
                                860                 :                :          * a hole that we'll fill, but we need to delete the later page
                                861                 :                :          * because the loop below won't visit it again.
                                862                 :                :          */
 5337 tgl@sss.pgh.pa.us         863         [ +  + ]:             15 :         if (olddata != NULL)
                                864                 :                :         {
                                865         [ -  + ]:              6 :             Assert(olddata->pageno > pageno);
 3139                           866                 :              6 :             CatalogTupleDelete(lo_heap_r, &oldtuple->t_self);
                                867                 :                :         }
                                868                 :                : 
                                869                 :                :         /*
                                870                 :                :          * Write a brand new page.
                                871                 :                :          *
                                872                 :                :          * Fill the hole up to the truncation point
                                873                 :                :          */
 6762 bruce@momjian.us          874                 :             15 :         off = len % LOBLKSIZE;
                                875         [ +  - ]:             15 :         if (off > 0)
                                876   [ -  +  -  -  :             15 :             MemSet(workb, 0, off);
                                     -  -  -  -  -  
                                                 - ]
                                877                 :                : 
                                878                 :                :         /* compute length of new page */
                                879                 :             15 :         SET_VARSIZE(&workbuf.hdr, off + VARHDRSZ);
                                880                 :                : 
                                881                 :                :         /*
                                882                 :                :          * Form and insert new tuple
                                883                 :                :          */
                                884                 :             15 :         memset(values, 0, sizeof(values));
 6152 tgl@sss.pgh.pa.us         885                 :             15 :         memset(nulls, false, sizeof(nulls));
 6762 bruce@momjian.us          886                 :             15 :         values[Anum_pg_largeobject_loid - 1] = ObjectIdGetDatum(obj_desc->id);
                                887                 :             15 :         values[Anum_pg_largeobject_pageno - 1] = Int32GetDatum(pageno);
                                888                 :             15 :         values[Anum_pg_largeobject_data - 1] = PointerGetDatum(&workbuf);
 6152 tgl@sss.pgh.pa.us         889                 :             15 :         newtup = heap_form_tuple(lo_heap_r->rd_att, values, nulls);
 3139                           890                 :             15 :         CatalogTupleInsertWithInfo(lo_heap_r, newtup, indstate);
 6762 bruce@momjian.us          891                 :             15 :         heap_freetuple(newtup);
                                892                 :                :     }
                                893                 :                : 
                                894                 :                :     /*
                                895                 :                :      * Delete any pages after the truncation point.  If the initial search
                                896                 :                :      * didn't find a page, then of course there's nothing more to do.
                                897                 :                :      */
 5337 tgl@sss.pgh.pa.us         898         [ +  + ]:             21 :     if (olddata != NULL)
                                899                 :                :     {
                                900         [ +  + ]:             15 :         while ((oldtuple = systable_getnext_ordered(sd, ForwardScanDirection)) != NULL)
                                901                 :                :         {
 3139                           902                 :              3 :             CatalogTupleDelete(lo_heap_r, &oldtuple->t_self);
                                903                 :                :         }
                                904                 :                :     }
                                905                 :                : 
 6356                           906                 :             21 :     systable_endscan_ordered(sd);
                                907                 :                : 
 6762 bruce@momjian.us          908                 :             21 :     CatalogCloseIndexes(indstate);
                                909                 :                : 
                                910                 :                :     /*
                                911                 :                :      * Advance command counter so that tuple updates will be seen by later
                                912                 :                :      * large-object operations in this transaction.
                                913                 :                :      */
                                914                 :             21 :     CommandCounterIncrement();
                                915                 :             21 : }
        

Generated by: LCOV version 2.4-beta